From edbf3b6af777b721cd2a1ef461947e51e88241e1 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:31:44 -0800 Subject: [PATCH] auto import from //depot/cupcake/@135843 --- MODULE_LICENSE_APACHE2 | 0 NOTICE | 222 + awt/Android.mk | 31 + .../internal/awt/AndroidGraphics2D.java | 1354 ++++ .../awt/AndroidGraphicsConfiguration.java | 96 + .../internal/awt/AndroidGraphicsFactory.java | 87 + .../internal/awt/AndroidImageDecoder.java | 274 + .../internal/awt/AndroidJavaBlitter.java | 536 ++ .../internal/awt/AndroidNativeEventQueue.java | 75 + awt/com/android/internal/awt/AndroidWTK.java | 88 + awt/com/android/internal/awt/AwtFactory.java | 52 + .../awt/ImageOutputStreamWrapper.java | 66 + awt/java/awt/AWTEvent.java | 681 ++ awt/java/awt/AWTException.java | 47 + awt/java/awt/AWTKeyStroke.java | 712 ++ awt/java/awt/AWTListenerList.java | 47 + awt/java/awt/AWTPermission.java | 61 + awt/java/awt/ActiveEvent.java | 39 + awt/java/awt/Adjustable.java | 166 + awt/java/awt/AlphaComposite.java | 352 + awt/java/awt/BasicStroke.java | 2443 +++++++ awt/java/awt/BufferCapabilities.java | 195 + awt/java/awt/Color.java | 990 +++ awt/java/awt/Component.java | 6020 +++++++++++++++++ awt/java/awt/ComponentBehavior.java | 56 + awt/java/awt/ComponentOrientation.java | 154 + awt/java/awt/Composite.java | 51 + awt/java/awt/CompositeContext.java | 54 + awt/java/awt/Cursor.java | 427 ++ awt/java/awt/Dimension.java | 201 + awt/java/awt/Dispatcher.java | 723 ++ awt/java/awt/DisplayMode.java | 165 + awt/java/awt/Event.java | 596 ++ awt/java/awt/EventDispatchThread.java | 118 + awt/java/awt/EventQueue.java | 320 + awt/java/awt/EventQueueCore.java | 253 + awt/java/awt/Font.java | 1541 +++++ awt/java/awt/FontFormatException.java | 47 + awt/java/awt/FontMetrics.java | 466 ++ awt/java/awt/GradientPaint.java | 255 + awt/java/awt/GradientPaintContext.java | 204 + awt/java/awt/Graphics.java | 924 +++ awt/java/awt/Graphics2D.java | 513 ++ awt/java/awt/GraphicsConfiguration.java | 226 + awt/java/awt/GraphicsDevice.java | 196 + awt/java/awt/GraphicsEnvironment.java | 212 + awt/java/awt/HeadlessException.java | 54 + awt/java/awt/HeadlessGraphicsEnvironment.java | 72 + awt/java/awt/HeadlessToolkit.java | 226 + .../awt/IllegalComponentStateException.java | 55 + awt/java/awt/Image.java | 205 + awt/java/awt/ImageCapabilities.java | 78 + awt/java/awt/Insets.java | 179 + awt/java/awt/ItemSelectable.java | 59 + awt/java/awt/MenuComponent.java | 783 +++ awt/java/awt/MenuContainer.java | 57 + awt/java/awt/ModalContext.java | 64 + awt/java/awt/MouseDispatcher.java | 418 ++ awt/java/awt/Paint.java | 57 + awt/java/awt/PaintContext.java | 69 + awt/java/awt/Point.java | 211 + awt/java/awt/Polygon.java | 515 ++ awt/java/awt/Rectangle.java | 723 ++ awt/java/awt/RenderingHints.java | 606 ++ awt/java/awt/Shape.java | 162 + awt/java/awt/Stroke.java | 50 + awt/java/awt/Toolkit.java | 1444 ++++ awt/java/awt/ToolkitImpl.java | 255 + awt/java/awt/Transparency.java | 57 + awt/java/awt/color/CMMException.java | 44 + awt/java/awt/color/ColorSpace.java | 414 ++ awt/java/awt/color/ICC_ColorSpace.java | 468 ++ awt/java/awt/color/ICC_Profile.java | 1477 ++++ awt/java/awt/color/ICC_ProfileGray.java | 78 + awt/java/awt/color/ICC_ProfileRGB.java | 154 + awt/java/awt/color/ICC_ProfileStub.java | 173 + awt/java/awt/color/ProfileDataException.java | 47 + awt/java/awt/color/package.html | 8 + awt/java/awt/event/AWTEventListener.java | 36 + awt/java/awt/event/AWTEventListenerProxy.java | 58 + awt/java/awt/event/ActionEvent.java | 114 + awt/java/awt/event/ActionListener.java | 35 + awt/java/awt/event/AdjustmentEvent.java | 123 + awt/java/awt/event/AdjustmentListener.java | 35 + awt/java/awt/event/ComponentAdapter.java | 46 + awt/java/awt/event/ComponentEvent.java | 88 + awt/java/awt/event/ComponentListener.java | 41 + awt/java/awt/event/ContainerAdapter.java | 40 + awt/java/awt/event/ContainerEvent.java | 89 + awt/java/awt/event/ContainerListener.java | 37 + awt/java/awt/event/FocusAdapter.java | 40 + awt/java/awt/event/FocusEvent.java | 96 + awt/java/awt/event/FocusListener.java | 37 + .../awt/event/HierarchyBoundsAdapter.java | 40 + .../awt/event/HierarchyBoundsListener.java | 37 + awt/java/awt/event/HierarchyEvent.java | 154 + awt/java/awt/event/HierarchyListener.java | 35 + awt/java/awt/event/InputEvent.java | 190 + awt/java/awt/event/InputMethodEvent.java | 156 + awt/java/awt/event/InputMethodListener.java | 37 + awt/java/awt/event/InvocationEvent.java | 138 + awt/java/awt/event/ItemEvent.java | 96 + awt/java/awt/event/ItemListener.java | 35 + awt/java/awt/event/KeyAdapter.java | 43 + awt/java/awt/event/KeyEvent.java | 687 ++ awt/java/awt/event/KeyListener.java | 39 + awt/java/awt/event/MouseAdapter.java | 49 + awt/java/awt/event/MouseEvent.java | 232 + awt/java/awt/event/MouseListener.java | 43 + awt/java/awt/event/MouseMotionAdapter.java | 40 + awt/java/awt/event/MouseMotionListener.java | 37 + awt/java/awt/event/MouseWheelEvent.java | 103 + awt/java/awt/event/MouseWheelListener.java | 35 + awt/java/awt/event/PaintEvent.java | 86 + awt/java/awt/event/TextEvent.java | 59 + awt/java/awt/event/TextListener.java | 36 + awt/java/awt/event/WindowAdapter.java | 64 + awt/java/awt/event/WindowEvent.java | 168 + awt/java/awt/event/WindowFocusListener.java | 37 + awt/java/awt/event/WindowListener.java | 47 + awt/java/awt/event/WindowStateListener.java | 36 + awt/java/awt/font/FontRenderContext.java | 178 + awt/java/awt/font/GlyphJustificationInfo.java | 197 + awt/java/awt/font/GlyphMetrics.java | 266 + awt/java/awt/font/GlyphVector.java | 403 ++ awt/java/awt/font/GraphicAttribute.java | 179 + awt/java/awt/font/ImageGraphicAttribute.java | 185 + awt/java/awt/font/LineBreakMeasurer.java | 238 + awt/java/awt/font/LineMetrics.java | 116 + awt/java/awt/font/MultipleMaster.java | 91 + awt/java/awt/font/OpenType.java | 418 ++ awt/java/awt/font/ShapeGraphicAttribute.java | 206 + awt/java/awt/font/TextHitInfo.java | 215 + awt/java/awt/font/TextLayout.java | 927 +++ awt/java/awt/font/TextMeasurer.java | 182 + awt/java/awt/font/TransformAttribute.java | 86 + awt/java/awt/font/package.html | 8 + awt/java/awt/geom/AffineTransform.java | 1267 ++++ awt/java/awt/geom/Arc2D.java | 1157 ++++ awt/java/awt/geom/Area.java | 330 + awt/java/awt/geom/CubicCurve2D.java | 1047 +++ awt/java/awt/geom/Dimension2D.java | 83 + awt/java/awt/geom/Ellipse2D.java | 458 ++ awt/java/awt/geom/FlatteningPathIterator.java | 358 + awt/java/awt/geom/GeneralPath.java | 624 ++ .../awt/geom/IllegalPathStateException.java | 55 + awt/java/awt/geom/Line2D.java | 948 +++ .../geom/NoninvertibleTransformException.java | 48 + awt/java/awt/geom/PathIterator.java | 146 + awt/java/awt/geom/Point2D.java | 323 + awt/java/awt/geom/QuadCurve2D.java | 918 +++ awt/java/awt/geom/Rectangle2D.java | 824 +++ awt/java/awt/geom/RectangularShape.java | 297 + awt/java/awt/geom/RoundRectangle2D.java | 635 ++ awt/java/awt/geom/package.html | 8 + awt/java/awt/im/InputContext.java | 83 + awt/java/awt/im/InputMethodHighlight.java | 95 + awt/java/awt/im/InputMethodRequests.java | 49 + awt/java/awt/im/InputSubset.java | 59 + awt/java/awt/im/spi/InputMethod.java | 67 + awt/java/awt/im/spi/InputMethodContext.java | 46 + .../awt/im/spi/InputMethodDescriptor.java | 46 + awt/java/awt/image/AffineTransformOp.java | 618 ++ .../awt/image/AreaAveragingScaleFilter.java | 288 + .../image/AwtImageBackdoorAccessorImpl.java | 156 + awt/java/awt/image/BandCombineOp.java | 658 ++ awt/java/awt/image/BandedSampleModel.java | 425 ++ awt/java/awt/image/BufferStrategy.java | 74 + awt/java/awt/image/BufferedImage.java | 952 +++ awt/java/awt/image/BufferedImageFilter.java | 397 ++ awt/java/awt/image/BufferedImageOp.java | 92 + awt/java/awt/image/ByteLookupTable.java | 140 + awt/java/awt/image/ColorConvertOp.java | 710 ++ awt/java/awt/image/ColorModel.java | 964 +++ awt/java/awt/image/ComponentColorModel.java | 1482 ++++ awt/java/awt/image/ComponentSampleModel.java | 705 ++ awt/java/awt/image/ConvolveOp.java | 545 ++ awt/java/awt/image/CropImageFilter.java | 196 + awt/java/awt/image/DataBuffer.java | 481 ++ awt/java/awt/image/DataBufferByte.java | 183 + awt/java/awt/image/DataBufferDouble.java | 226 + awt/java/awt/image/DataBufferFloat.java | 226 + awt/java/awt/image/DataBufferInt.java | 182 + awt/java/awt/image/DataBufferShort.java | 181 + awt/java/awt/image/DataBufferUShort.java | 195 + awt/java/awt/image/DirectColorModel.java | 889 +++ awt/java/awt/image/FilteredImageSource.java | 98 + awt/java/awt/image/ImageConsumer.java | 185 + awt/java/awt/image/ImageFilter.java | 134 + awt/java/awt/image/ImageObserver.java | 101 + awt/java/awt/image/ImageProducer.java | 79 + awt/java/awt/image/ImagingOpException.java | 49 + awt/java/awt/image/IndexColorModel.java | 1080 +++ awt/java/awt/image/Kernel.java | 153 + awt/java/awt/image/LookupOp.java | 661 ++ awt/java/awt/image/LookupTable.java | 101 + awt/java/awt/image/MemoryImageSource.java | 603 ++ .../image/MultiPixelPackedSampleModel.java | 479 ++ awt/java/awt/image/PackedColorModel.java | 402 ++ awt/java/awt/image/PixelGrabber.java | 408 ++ .../image/PixelInterleavedSampleModel.java | 134 + awt/java/awt/image/RGBImageFilter.java | 195 + awt/java/awt/image/Raster.java | 1515 +++++ awt/java/awt/image/RasterFormatException.java | 48 + awt/java/awt/image/RasterOp.java | 88 + awt/java/awt/image/RenderedImage.java | 198 + awt/java/awt/image/ReplicateScaleFilter.java | 225 + awt/java/awt/image/RescaleOp.java | 659 ++ awt/java/awt/image/SampleModel.java | 1166 ++++ awt/java/awt/image/ShortLookupTable.java | 137 + .../image/SinglePixelPackedSampleModel.java | 519 ++ awt/java/awt/image/TileObserver.java | 49 + awt/java/awt/image/VolatileImage.java | 146 + awt/java/awt/image/WritableRaster.java | 592 ++ awt/java/awt/image/WritableRenderedImage.java | 109 + awt/java/awt/image/package.html | 8 + .../ContextualRenderedImageFactory.java | 97 + .../awt/image/renderable/ParameterBlock.java | 568 ++ .../awt/image/renderable/RenderContext.java | 214 + .../awt/image/renderable/RenderableImage.java | 138 + .../image/renderable/RenderableImageOp.java | 191 + .../renderable/RenderableImageProducer.java | 151 + .../renderable/RenderedImageFactory.java | 46 + awt/java/awt/image/renderable/package.html | 8 + awt/java/awt/package.html | 8 + awt/java/awt/peer/ButtonPeer.java | 25 + awt/java/awt/peer/CanvasPeer.java | 25 + awt/java/awt/peer/CheckboxMenuItemPeer.java | 25 + awt/java/awt/peer/CheckboxPeer.java | 25 + awt/java/awt/peer/ChoicePeer.java | 25 + awt/java/awt/peer/ComponentPeer.java | 25 + awt/java/awt/peer/DialogPeer.java | 25 + awt/java/awt/peer/FileDialogPeer.java | 25 + awt/java/awt/peer/FontPeer.java | 25 + awt/java/awt/peer/FramePeer.java | 25 + awt/java/awt/peer/LabelPeer.java | 25 + awt/java/awt/peer/LightweightPeer.java | 25 + awt/java/awt/peer/ListPeer.java | 25 + awt/java/awt/peer/MenuBarPeer.java | 25 + awt/java/awt/peer/MenuComponentPeer.java | 25 + awt/java/awt/peer/MenuItemPeer.java | 25 + awt/java/awt/peer/MenuPeer.java | 25 + awt/java/awt/peer/MouseInfoPeer.java | 25 + awt/java/awt/peer/PanelPeer.java | 25 + awt/java/awt/peer/PopupMenuPeer.java | 25 + awt/java/awt/peer/ScrollPanePeer.java | 25 + awt/java/awt/peer/ScrollbarPeer.java | 25 + awt/java/awt/peer/TextAreaPeer.java | 25 + awt/java/awt/peer/TextFieldPeer.java | 25 + awt/java/awt/peer/WindowPeer.java | 25 + awt/java/beans/FeatureDescriptor.java | 234 + awt/java/beans/IndexedPropertyDescriptor.java | 227 + awt/java/beans/IntrospectionException.java | 27 + awt/java/beans/PropertyDescriptor.java | 300 + awt/java/beans/PropertyEditor.java | 49 + awt/java/beans/PropertyEditorManager.java | 114 + awt/java/beans/PropertyEditorSupport.java | 129 + awt/java/beans/PropertyVetoException.java | 54 + awt/javax/imageio/IIOException.java | 60 + awt/javax/imageio/IIOImage.java | 224 + awt/javax/imageio/IIOParam.java | 342 + awt/javax/imageio/IIOParamController.java | 45 + awt/javax/imageio/ImageIO.java | 800 +++ awt/javax/imageio/ImageReadParam.java | 201 + awt/javax/imageio/ImageReader.java | 1162 ++++ awt/javax/imageio/ImageTranscoder.java | 67 + awt/javax/imageio/ImageTypeSpecifier.java | 347 + awt/javax/imageio/ImageWriteParam.java | 664 ++ awt/javax/imageio/ImageWriter.java | 1001 +++ .../event/IIOReadProgressListener.java | 121 + .../imageio/event/IIOReadUpdateListener.java | 182 + .../imageio/event/IIOReadWarningListener.java | 49 + .../event/IIOWriteProgressListener.java | 101 + .../event/IIOWriteWarningListener.java | 46 + awt/javax/imageio/event/package.html | 8 + .../metadata/IIOInvalidTreeException.java | 74 + awt/javax/imageio/metadata/IIOMetadata.java | 391 ++ .../metadata/IIOMetadataController.java | 46 + .../imageio/metadata/IIOMetadataFormat.java | 404 ++ .../metadata/IIOMetadataFormatImpl.java | 1056 +++ .../imageio/metadata/IIOMetadataNode.java | 1070 +++ .../metadata/IIOStandardMetadataFormat.java | 297 + ...StandardMetadataFormatResources.properties | 133 + awt/javax/imageio/metadata/package.html | 8 + awt/javax/imageio/package.html | 8 + .../plugins/bmp/BMPImageWriteParam.java | 79 + awt/javax/imageio/plugins/bmp/package.html | 8 + .../plugins/jpeg/JPEGHuffmanTable.java | 226 + .../plugins/jpeg/JPEGImageReadParam.java | 123 + .../plugins/jpeg/JPEGImageWriteParam.java | 209 + .../imageio/plugins/jpeg/JPEGQTable.java | 165 + awt/javax/imageio/plugins/jpeg/package.html | 8 + awt/javax/imageio/spi/IIORegistry.java | 115 + awt/javax/imageio/spi/IIOServiceProvider.java | 105 + .../imageio/spi/ImageInputStreamSpi.java | 131 + .../imageio/spi/ImageOutputStreamSpi.java | 132 + awt/javax/imageio/spi/ImageReaderSpi.java | 204 + .../imageio/spi/ImageReaderWriterSpi.java | 344 + awt/javax/imageio/spi/ImageTranscoderSpi.java | 76 + awt/javax/imageio/spi/ImageWriterSpi.java | 227 + .../imageio/spi/RegisterableService.java | 54 + awt/javax/imageio/spi/ServiceRegistry.java | 552 ++ awt/javax/imageio/spi/package.html | 8 + .../stream/FileCacheImageInputStream.java | 137 + .../stream/FileCacheImageOutputStream.java | 196 + .../imageio/stream/FileImageInputStream.java | 122 + .../imageio/stream/FileImageOutputStream.java | 128 + awt/javax/imageio/stream/IIOByteBuffer.java | 124 + .../imageio/stream/ImageInputStream.java | 502 ++ .../imageio/stream/ImageInputStreamImpl.java | 418 ++ .../imageio/stream/ImageOutputStream.java | 307 + .../imageio/stream/ImageOutputStreamImpl.java | 174 + .../stream/MemoryCacheImageInputStream.java | 119 + .../stream/MemoryCacheImageOutputStream.java | 135 + awt/javax/imageio/stream/package.html | 8 + awt/org/apache/harmony/awt/ChoiceStyle.java | 33 + awt/org/apache/harmony/awt/ClipRegion.java | 84 + .../harmony/awt/ComponentInternals.java | 212 + .../apache/harmony/awt/ContextStorage.java | 154 + .../harmony/awt/ContextThreadGroup.java | 34 + awt/org/apache/harmony/awt/ListenerList.java | 194 + .../apache/harmony/awt/ReadOnlyIterator.java | 53 + .../awt/gl/AwtImageBackdoorAccessor.java | 65 + .../harmony/awt/gl/CommonGraphics2D.java | 1132 ++++ .../awt/gl/CommonGraphics2DFactory.java | 78 + .../awt/gl/CommonGraphicsEnvironment.java | 67 + awt/org/apache/harmony/awt/gl/Crossing.java | 889 +++ .../harmony/awt/gl/GLVolatileImage.java | 30 + .../harmony/awt/gl/ICompositeContext.java | 90 + .../apache/harmony/awt/gl/ImageSurface.java | 323 + .../apache/harmony/awt/gl/MultiRectArea.java | 836 +++ .../harmony/awt/gl/MultiRectAreaOp.java | 837 +++ awt/org/apache/harmony/awt/gl/Surface.java | 309 + .../apache/harmony/awt/gl/TextRenderer.java | 59 + .../apache/harmony/awt/gl/XORComposite.java | 48 + .../harmony/awt/gl/color/ColorConverter.java | 257 + .../harmony/awt/gl/color/ColorScaler.java | 355 + .../awt/gl/color/ICC_ProfileHelper.java | 82 + .../harmony/awt/gl/color/ICC_Transform.java | 156 + .../awt/gl/color/LUTColorConverter.java | 148 + .../harmony/awt/gl/color/NativeCMM.java | 92 + .../awt/gl/color/NativeImageFormat.java | 642 ++ .../harmony/awt/gl/font/AndroidFont.java | 254 + .../awt/gl/font/AndroidFontManager.java | 277 + .../awt/gl/font/AndroidFontProperty.java | 81 + .../awt/gl/font/AndroidGlyphVector.java | 219 + .../awt/gl/font/AndroidLineMetrics.java | 120 + .../harmony/awt/gl/font/BasicMetrics.java | 134 + .../harmony/awt/gl/font/CaretManager.java | 530 ++ .../awt/gl/font/CommonGlyphVector.java | 954 +++ .../harmony/awt/gl/font/CompositeFont.java | 486 ++ .../harmony/awt/gl/font/FontExtraMetrics.java | 145 + .../harmony/awt/gl/font/FontFinder.java | 121 + .../harmony/awt/gl/font/FontManager.java | 819 +++ .../harmony/awt/gl/font/FontMetricsImpl.java | 282 + .../harmony/awt/gl/font/FontPeerImpl.java | 499 ++ .../harmony/awt/gl/font/FontProperty.java | 106 + awt/org/apache/harmony/awt/gl/font/Glyph.java | 236 + .../harmony/awt/gl/font/LineMetricsImpl.java | 412 ++ .../harmony/awt/gl/font/TextDecorator.java | 433 ++ .../awt/gl/font/TextMetricsCalculator.java | 209 + .../harmony/awt/gl/font/TextRunBreaker.java | 861 +++ .../harmony/awt/gl/font/TextRunSegment.java | 165 + .../awt/gl/font/TextRunSegmentImpl.java | 979 +++ .../awt/gl/image/BufferedImageGraphics2D.java | 79 + .../awt/gl/image/BufferedImageSource.java | 136 + .../image/ByteArrayDecodingImageSource.java | 62 + .../awt/gl/image/DataBufferListener.java | 31 + .../awt/gl/image/DecodingImageSource.java | 261 + .../awt/gl/image/FileDecodingImageSource.java | 68 + .../harmony/awt/gl/image/GifDecoder.java | 692 ++ .../harmony/awt/gl/image/ImageDecoder.java | 258 + .../harmony/awt/gl/image/ImageLoader.java | 208 + .../harmony/awt/gl/image/JpegDecoder.java | 231 + .../harmony/awt/gl/image/OffscreenImage.java | 532 ++ .../awt/gl/image/OrdinaryWritableRaster.java | 153 + .../harmony/awt/gl/image/PngDecoder.java | 270 + .../harmony/awt/gl/image/PngDecoderJava.java | 282 + .../awt/gl/image/URLDecodingImageSource.java | 77 + .../apache/harmony/awt/gl/render/Blitter.java | 53 + .../awt/gl/render/JavaArcRasterizer.java | 502 ++ .../harmony/awt/gl/render/JavaBlitter.java | 611 ++ .../awt/gl/render/JavaLineRasterizer.java | 760 +++ .../awt/gl/render/JavaShapeRasterizer.java | 475 ++ .../awt/gl/render/JavaTextRenderer.java | 263 + .../awt/gl/render/NativeImageBlitter.java | 218 + .../harmony/awt/gl/render/NullBlitter.java | 56 + .../harmony/awt/im/InputMethodContext.java | 563 ++ .../harmony/awt/internal/nls/Messages.java | 151 + .../harmony/awt/internal/nls/MsgHelp.java | 86 + .../harmony/awt/state/MenuItemState.java | 51 + .../apache/harmony/awt/state/MenuState.java | 46 + awt/org/apache/harmony/awt/state/State.java | 55 + .../harmony/awt/wtk/CreationParams.java | 133 + .../apache/harmony/awt/wtk/CursorFactory.java | 85 + .../harmony/awt/wtk/GraphicsFactory.java | 82 + awt/org/apache/harmony/awt/wtk/KeyInfo.java | 53 + .../apache/harmony/awt/wtk/NativeCursor.java | 45 + .../apache/harmony/awt/wtk/NativeEvent.java | 268 + .../harmony/awt/wtk/NativeEventQueue.java | 117 + .../harmony/awt/wtk/NativeEventThread.java | 78 + awt/org/apache/harmony/awt/wtk/NativeIM.java | 130 + .../harmony/awt/wtk/NativeMouseInfo.java | 42 + .../apache/harmony/awt/wtk/NativeRobot.java | 75 + .../apache/harmony/awt/wtk/NativeWindow.java | 220 + .../harmony/awt/wtk/ShutdownThread.java | 83 + .../harmony/awt/wtk/ShutdownWatchdog.java | 86 + .../apache/harmony/awt/wtk/Synchronizer.java | 200 + .../harmony/awt/wtk/SystemProperties.java | 59 + awt/org/apache/harmony/awt/wtk/WTK.java | 61 + .../apache/harmony/awt/wtk/WindowFactory.java | 85 + .../harmony/beans/internal/nls/Messages.java | 151 + .../harmony/beans/internal/nls/MsgHelp.java | 86 + .../x/imageio/internal/nls/Messages.java | 124 + .../imageio/internal/nls/messages.properties | 18 + .../x/imageio/metadata/IIOMetadataUtils.java | 94 + .../plugins/jpeg/IISDecodingImageSource.java | 115 + .../x/imageio/plugins/jpeg/JPEGConsts.java | 44 + .../imageio/plugins/jpeg/JPEGImageReader.java | 126 + .../plugins/jpeg/JPEGImageReaderSpi.java | 86 + .../imageio/plugins/jpeg/JPEGImageWriter.java | 402 ++ .../plugins/jpeg/JPEGImageWriterSpi.java | 56 + .../x/imageio/plugins/jpeg/JPEGSpiConsts.java | 57 + .../x/imageio/plugins/png/PNGImageReader.java | 106 + .../plugins/png/PNGImageReaderSpi.java | 88 + .../x/imageio/plugins/png/PNGImageWriter.java | 247 + .../plugins/png/PNGImageWriterParam.java | 41 + .../plugins/png/PNGImageWriterSpi.java | 113 + .../harmony/x/imageio/spi/FileIISSpi.java | 53 + .../harmony/x/imageio/spi/FileIOSSpi.java | 52 + .../x/imageio/spi/InputStreamIISSpi.java | 59 + .../x/imageio/spi/OutputStreamIOSSpi.java | 60 + .../harmony/x/imageio/spi/RAFIISSpi.java | 54 + .../harmony/x/imageio/spi/RAFIOSSpi.java | 53 + .../stream/RandomAccessMemoryCache.java | 226 + .../awt/internal/nls/messages.properties | 495 ++ .../beans/internals/nls/messages.properties | 103 + camera/libcameraservice/Android.mk | 59 + .../libcameraservice/CameraHardwareStub.cpp | 388 ++ camera/libcameraservice/CameraHardwareStub.h | 124 + camera/libcameraservice/CameraService.cpp | 1073 +++ camera/libcameraservice/CameraService.h | 206 + camera/libcameraservice/CannedJpeg.h | 1546 +++++ camera/libcameraservice/FakeCamera.cpp | 404 ++ camera/libcameraservice/FakeCamera.h | 51 + cmds/runtime/Android.mk | 29 + cmds/runtime/MODULE_LICENSE_APACHE2 | 0 cmds/runtime/NOTICE | 190 + cmds/runtime/ServiceManager.cpp | 74 + cmds/runtime/ServiceManager.h | 38 + cmds/runtime/SignalHandler.cpp | 249 + cmds/runtime/SignalHandler.h | 137 + cmds/runtime/main_runtime.cpp | 514 ++ cmds/surfaceflinger/Android.mk | 16 + cmds/surfaceflinger/main_surfaceflinger.cpp | 18 + im/java/android/im/BrandingResourceIDs.java | 52 + im/java/android/im/IImPlugin.aidl | 69 + im/java/android/im/ImPluginConsts.java | 27 + include/pim/EventRecurrence.h | 82 + include/private/opengles/gl_context.h | 632 ++ include/private/ui/LayerState.h | 75 + include/private/ui/SharedState.h | 168 + include/private/ui/SurfaceFlingerSynchro.h | 76 + include/private/utils/Static.h | 58 + include/private/utils/binder_module.h | 148 + include/private/utils/futex_synchro.h | 60 + include/ui/Camera.h | 196 + include/ui/CameraHardwareInterface.h | 190 + include/ui/CameraParameters.h | 79 + include/ui/DisplayInfo.h | 43 + include/ui/EGLDisplaySurface.h | 86 + include/ui/EGLNativeSurface.h | 55 + include/ui/EGLNativeWindowSurface.h | 59 + include/ui/EventHub.h | 145 + include/ui/ICamera.h | 102 + include/ui/ICameraClient.h | 55 + include/ui/ICameraService.h | 55 + include/ui/IOverlay.h | 53 + include/ui/ISurface.h | 105 + include/ui/ISurfaceComposer.h | 181 + include/ui/ISurfaceFlingerClient.h | 90 + include/ui/KeyCharacterMap.h | 72 + include/ui/KeycodeLabels.h | 236 + include/ui/Overlay.h | 109 + include/ui/PixelFormat.h | 125 + include/ui/Point.h | 88 + include/ui/Rect.h | 152 + include/ui/Region.h | 174 + include/ui/Surface.h | 137 + include/ui/SurfaceComposerClient.h | 179 + include/utils.h | 33 + include/utils/AndroidUnicode.h | 255 + include/utils/Asset.h | 315 + include/utils/AssetDir.h | 145 + include/utils/AssetManager.h | 323 + include/utils/Atomic.h | 22 + include/utils/Binder.h | 103 + include/utils/BpBinder.h | 122 + include/utils/Buffer.h | 107 + include/utils/BufferedTextOutput.h | 67 + include/utils/ByteOrder.h | 69 + include/utils/CallStack.h | 76 + include/utils/Debug.h | 45 + include/utils/Endian.h | 40 + include/utils/Errors.h | 87 + include/utils/FileMap.h | 134 + include/utils/IBinder.h | 159 + include/utils/IInterface.h | 135 + include/utils/IMemory.h | 94 + include/utils/IPCThreadState.h | 110 + include/utils/IPermissionController.h | 56 + include/utils/IServiceManager.h | 98 + include/utils/KeyedVector.h | 201 + include/utils/List.h | 280 + include/utils/Log.h | 33 + include/utils/LogSocket.h | 20 + include/utils/MemoryBase.h | 51 + include/utils/MemoryDealer.h | 238 + include/utils/MemoryHeapBase.h | 98 + include/utils/MemoryHeapPmem.h | 80 + include/utils/Parcel.h | 209 + include/utils/Pipe.h | 108 + include/utils/ProcessState.h | 117 + include/utils/RefBase.h | 550 ++ include/utils/ResourceTypes.h | 1720 +++++ include/utils/SharedBuffer.h | 146 + include/utils/Socket.h | 80 + include/utils/SortedVector.h | 282 + include/utils/StopWatch.h | 62 + include/utils/String16.h | 260 + include/utils/String8.h | 353 + include/utils/SystemClock.h | 32 + include/utils/TextOutput.h | 190 + include/utils/TimeUtils.h | 89 + include/utils/TimerProbe.h | 72 + include/utils/Timers.h | 137 + include/utils/TypeHelpers.h | 254 + include/utils/Vector.h | 359 + include/utils/VectorImpl.h | 199 + include/utils/ZipEntry.h | 345 + include/utils/ZipFile.h | 269 + include/utils/ZipFileCRO.h | 59 + include/utils/ZipFileRO.h | 222 + include/utils/ZipUtils.h | 67 + include/utils/ashmem.h | 41 + include/utils/executablepath.h | 28 + include/utils/inet_address.h | 103 + include/utils/misc.h | 93 + include/utils/ported.h | 50 + include/utils/string_array.h | 135 + include/utils/threads.h | 347 + libs/audioflinger/A2dpAudioInterface.cpp | 242 + libs/audioflinger/A2dpAudioInterface.h | 109 + libs/audioflinger/Android.mk | 56 + libs/audioflinger/AudioBufferProvider.h | 47 + libs/audioflinger/AudioDumpInterface.cpp | 117 + libs/audioflinger/AudioDumpInterface.h | 97 + libs/audioflinger/AudioFlinger.cpp | 2474 +++++++ libs/audioflinger/AudioFlinger.h | 637 ++ libs/audioflinger/AudioHardwareGeneric.cpp | 313 + libs/audioflinger/AudioHardwareGeneric.h | 141 + libs/audioflinger/AudioHardwareInterface.cpp | 247 + libs/audioflinger/AudioHardwareStub.cpp | 185 + libs/audioflinger/AudioHardwareStub.h | 100 + libs/audioflinger/AudioMixer.cpp | 913 +++ libs/audioflinger/AudioMixer.h | 192 + libs/audioflinger/AudioResampler.cpp | 595 ++ libs/audioflinger/AudioResampler.h | 93 + libs/audioflinger/AudioResamplerCubic.cpp | 184 + libs/audioflinger/AudioResamplerCubic.h | 68 + libs/audioflinger/AudioResamplerSinc.cpp | 358 + libs/audioflinger/AudioResamplerSinc.h | 88 + libs/surfaceflinger/Android.mk | 49 + libs/surfaceflinger/Barrier.h | 59 + libs/surfaceflinger/BlurFilter.cpp | 326 + libs/surfaceflinger/BlurFilter.h | 35 + libs/surfaceflinger/BootAnimation.cpp | 403 ++ libs/surfaceflinger/BootAnimation.h | 87 + libs/surfaceflinger/CPUGauge.cpp | 171 + libs/surfaceflinger/CPUGauge.h | 74 + .../DisplayHardware/DisplayHardware.cpp | 353 + .../DisplayHardware/DisplayHardware.h | 113 + .../DisplayHardware/DisplayHardwareBase.cpp | 403 ++ .../DisplayHardware/DisplayHardwareBase.h | 96 + .../GPUHardware/GPUHardware.cpp | 581 ++ libs/surfaceflinger/GPUHardware/GPUHardware.h | 63 + libs/surfaceflinger/Layer.cpp | 568 ++ libs/surfaceflinger/Layer.h | 120 + libs/surfaceflinger/LayerBase.cpp | 740 ++ libs/surfaceflinger/LayerBase.h | 356 + libs/surfaceflinger/LayerBitmap.cpp | 185 + libs/surfaceflinger/LayerBitmap.h | 84 + libs/surfaceflinger/LayerBlur.cpp | 234 + libs/surfaceflinger/LayerBlur.h | 65 + libs/surfaceflinger/LayerBuffer.cpp | 655 ++ libs/surfaceflinger/LayerBuffer.h | 216 + libs/surfaceflinger/LayerDim.cpp | 113 + libs/surfaceflinger/LayerDim.h | 57 + libs/surfaceflinger/LayerOrientationAnim.cpp | 287 + libs/surfaceflinger/LayerOrientationAnim.h | 75 + libs/surfaceflinger/MODULE_LICENSE_APACHE2 | 0 libs/surfaceflinger/OrientationAnimation.cpp | 155 + libs/surfaceflinger/OrientationAnimation.h | 73 + libs/surfaceflinger/SurfaceFlinger.cpp | 1840 +++++ libs/surfaceflinger/SurfaceFlinger.h | 435 ++ libs/surfaceflinger/Tokenizer.cpp | 172 + libs/surfaceflinger/Tokenizer.h | 57 + libs/surfaceflinger/Transform.cpp | 204 + libs/surfaceflinger/Transform.h | 87 + libs/surfaceflinger/VRamHeap.cpp | 176 + libs/surfaceflinger/VRamHeap.h | 78 + libs/surfaceflinger/clz.cpp | 37 + libs/surfaceflinger/clz.h | 37 + libs/surfaceflinger/tests/Android.mk | 1 + libs/surfaceflinger/tests/overlays/Android.mk | 16 + .../tests/overlays/overlays.cpp | 58 + libs/ui/Android.mk | 41 + libs/ui/Camera.cpp | 408 ++ libs/ui/CameraParameters.cpp | 273 + libs/ui/EGLDisplaySurface.cpp | 519 ++ libs/ui/EGLNativeWindowSurface.cpp | 161 + libs/ui/EventHub.cpp | 793 +++ libs/ui/EventRecurrence.cpp | 484 ++ libs/ui/ICamera.cpp | 346 + libs/ui/ICameraClient.cpp | 185 + libs/ui/ICameraService.cpp | 77 + libs/ui/IOverlay.cpp | 72 + libs/ui/ISurface.cpp | 164 + libs/ui/ISurfaceComposer.cpp | 277 + libs/ui/ISurfaceFlingerClient.cpp | 208 + libs/ui/KeyCharacterMap.cpp | 263 + libs/ui/KeyLayoutMap.cpp | 235 + libs/ui/KeyLayoutMap.h | 31 + libs/ui/LayerState.cpp | 53 + libs/ui/MODULE_LICENSE_APACHE2 | 0 libs/ui/NOTICE | 190 + libs/ui/Overlay.cpp | 181 + libs/ui/PixelFormat.cpp | 97 + libs/ui/Point.cpp | 11 + libs/ui/Rect.cpp | 86 + libs/ui/Region.cpp | 313 + libs/ui/Surface.cpp | 256 + libs/ui/SurfaceComposerClient.cpp | 1026 +++ libs/ui/SurfaceFlingerSynchro.cpp | 123 + libs/ui/Time.cpp | 199 + libs/utils/Android.mk | 156 + libs/utils/Asset.cpp | 813 +++ libs/utils/AssetDir.cpp | 66 + libs/utils/AssetManager.cpp | 1637 +++++ libs/utils/Binder.cpp | 242 + libs/utils/BpBinder.cpp | 348 + libs/utils/BufferedTextOutput.cpp | 279 + libs/utils/CallStack.cpp | 335 + libs/utils/Debug.cpp | 318 + libs/utils/FileMap.cpp | 222 + libs/utils/IDataConnection.cpp | 89 + libs/utils/IInterface.cpp | 35 + libs/utils/IMemory.cpp | 486 ++ libs/utils/IPCThreadState.cpp | 1030 +++ libs/utils/IPermissionController.cpp | 86 + libs/utils/IServiceManager.cpp | 230 + libs/utils/InetAddress.cpp | 236 + libs/utils/LogSocket.cpp | 129 + libs/utils/MODULE_LICENSE_APACHE2 | 0 libs/utils/MemoryBase.cpp | 46 + libs/utils/MemoryDealer.cpp | 409 ++ libs/utils/MemoryHeapBase.cpp | 183 + libs/utils/MemoryHeapPmem.cpp | 248 + libs/utils/NOTICE | 190 + libs/utils/Parcel.cpp | 1377 ++++ libs/utils/Pipe.cpp | 465 ++ libs/utils/ProcessState.cpp | 398 ++ libs/utils/README | 14 + libs/utils/RefBase.cpp | 534 ++ libs/utils/ResourceTypes.cpp | 3983 +++++++++++ libs/utils/SharedBuffer.cpp | 113 + libs/utils/Socket.cpp | 388 ++ libs/utils/Static.cpp | 120 + libs/utils/StopWatch.cpp | 79 + libs/utils/String16.cpp | 609 ++ libs/utils/String8.cpp | 604 ++ libs/utils/SystemClock.cpp | 139 + libs/utils/TextOutput.cpp | 146 + libs/utils/Threads.cpp | 1128 +++ libs/utils/TimerProbe.cpp | 131 + libs/utils/Timers.cpp | 240 + libs/utils/Unicode.cpp | 193 + libs/utils/VectorImpl.cpp | 611 ++ libs/utils/ZipEntry.cpp | 696 ++ libs/utils/ZipFile.cpp | 1296 ++++ libs/utils/ZipFileCRO.cpp | 54 + libs/utils/ZipFileRO.cpp | 724 ++ libs/utils/ZipUtils.cpp | 344 + libs/utils/characterData.h | 730 ++ libs/utils/executablepath_darwin.cpp | 31 + libs/utils/executablepath_linux.cpp | 30 + libs/utils/futex_synchro.c | 175 + libs/utils/misc.cpp | 185 + libs/utils/ported.cpp | 106 + opengl/include/EGL/egl.h | 330 + opengl/include/EGL/eglext.h | 138 + opengl/include/EGL/eglnatives.h | 271 + opengl/include/EGL/eglplatform.h | 117 + opengl/include/GLES/egl.h | 15 + opengl/include/GLES/gl.h | 769 +++ opengl/include/GLES/glext.h | 622 ++ opengl/include/GLES/glplatform.h | 39 + opengl/include/KHR/khrplatform.h | 241 + opengl/libagl/Android.mk | 39 + opengl/libagl/BufferObjectManager.cpp | 103 + opengl/libagl/BufferObjectManager.h | 85 + opengl/libagl/TextureObjectManager.cpp | 309 + opengl/libagl/TextureObjectManager.h | 140 + opengl/libagl/TokenManager.cpp | 62 + opengl/libagl/TokenManager.h | 53 + opengl/libagl/Tokenizer.cpp | 173 + opengl/libagl/Tokenizer.h | 59 + opengl/libagl/array.cpp | 1557 +++++ opengl/libagl/array.h | 37 + opengl/libagl/context.h | 20 + opengl/libagl/dxt.cpp | 636 ++ opengl/libagl/dxt.h | 33 + opengl/libagl/egl.cpp | 1543 +++++ opengl/libagl/fixed_asm.S | 65 + opengl/libagl/fp.cpp | 87 + opengl/libagl/fp.h | 243 + opengl/libagl/iterators.S | 88 + opengl/libagl/light.cpp | 852 +++ opengl/libagl/light.h | 38 + opengl/libagl/matrix.cpp | 1145 ++++ opengl/libagl/matrix.h | 355 + opengl/libagl/mipmap.cpp | 192 + opengl/libagl/primitives.cpp | 1111 +++ opengl/libagl/primitives.h | 37 + opengl/libagl/state.cpp | 586 ++ opengl/libagl/state.h | 54 + opengl/libagl/texture.cpp | 1421 ++++ opengl/libagl/texture.h | 45 + opengl/libagl/vertex.cpp | 247 + opengl/libagl/vertex.h | 48 + opengl/libs/Android.mk | 53 + opengl/libs/EGL/egl.cpp | 1363 ++++ opengl/libs/EGL/gpu.cpp | 212 + opengl/libs/GLES_CM/gl.cpp | 116 + opengl/libs/GLES_CM/gl_api.in | 606 ++ opengl/libs/GLES_CM/gl_logger.cpp | 1060 +++ opengl/libs/egl_entries.in | 45 + opengl/libs/egl_impl.h | 43 + opengl/libs/gl_entries.in | 159 + opengl/libs/gl_enums.in | 261 + opengl/libs/gl_logger.h | 26 + opengl/libs/hooks.h | 134 + opengl/libs/tools/enumextract.sh | 32 + opengl/tests/Android.mk | 1 + opengl/tests/angeles/Android.mk | 17 + .../tests/angeles/MODULE_LICENSE_BSD_OR_LGPL | 0 opengl/tests/angeles/README.txt | 77 + opengl/tests/angeles/app-linux.c | 223 + opengl/tests/angeles/app.h | 56 + opengl/tests/angeles/cams.h | 65 + opengl/tests/angeles/demo.c | 792 +++ opengl/tests/angeles/gpustate.c | 39 + opengl/tests/angeles/include/GLES/egl.h | 229 + opengl/tests/angeles/include/GLES/egltypes.h | 20 + opengl/tests/angeles/include/GLES/gl.h | 584 ++ opengl/tests/angeles/license-BSD.txt | 34 + opengl/tests/angeles/license-LGPL.txt | 504 ++ opengl/tests/angeles/license.txt | 19 + opengl/tests/angeles/shapes.h | 59 + opengl/tests/filter/Android.mk | 17 + opengl/tests/filter/filter.c | 130 + opengl/tests/finish/Android.mk | 17 + opengl/tests/finish/finish.c | 224 + opengl/tests/textures/Android.mk | 17 + opengl/tests/textures/textures.c | 109 + opengl/tests/tritex/Android.mk | 17 + opengl/tests/tritex/tritex.c | 273 + opengl/tools/glgen/gen | 99 + opengl/tools/glgen/glspec-1.0 | 106 + opengl/tools/glgen/glspec-1.0ext | 1 + opengl/tools/glgen/glspec-1.1 | 42 + opengl/tools/glgen/glspec-1.1ext | 16 + opengl/tools/glgen/glspec-1.1extpack | 38 + opengl/tools/glgen/glspec-checks | 59 + opengl/tools/glgen/src/CFunc.java | 155 + opengl/tools/glgen/src/CType.java | 85 + opengl/tools/glgen/src/CodeEmitter.java | 8 + opengl/tools/glgen/src/GenerateGL.java | 164 + opengl/tools/glgen/src/JFunc.java | 148 + opengl/tools/glgen/src/JType.java | 139 + opengl/tools/glgen/src/JniCodeEmitter.java | 1086 +++ opengl/tools/glgen/src/ParameterChecker.java | 28 + .../tools/glgen/stubs/GL10ExtHeader.java-if | 22 + opengl/tools/glgen/stubs/GL10Header.java-if | 259 + .../tools/glgen/stubs/GL11ExtHeader.java-if | 40 + .../stubs/GL11ExtensionPackHeader.java-if | 108 + opengl/tools/glgen/stubs/GL11Header.java-if | 145 + .../glgen/stubs/GL11ImplHeader.java-impl | 30 + opengl/tools/glgen/stubs/GLCHeader.cpp | 129 + opengl/tools/glgen/stubs/GLHeader.java-if | 22 + .../tools/glgen/stubs/GLImplHeader.java-impl | 48 + opengl/tools/glgen/stubs/glGetString.cpp | 10 + .../tools/glgen/stubs/glGetString.java-10-if | 4 + opengl/tools/glgen/stubs/glGetString.java-if | 4 + .../tools/glgen/stubs/glGetString.java-impl | 16 + .../tools/glgen/stubs/glGetString.nativeReg | 1 + services/Android.mk | 17 + 807 files changed, 207817 insertions(+) create mode 100644 MODULE_LICENSE_APACHE2 create mode 100644 NOTICE create mode 100644 awt/Android.mk create mode 100644 awt/com/android/internal/awt/AndroidGraphics2D.java create mode 100644 awt/com/android/internal/awt/AndroidGraphicsConfiguration.java create mode 100644 awt/com/android/internal/awt/AndroidGraphicsFactory.java create mode 100644 awt/com/android/internal/awt/AndroidImageDecoder.java create mode 100644 awt/com/android/internal/awt/AndroidJavaBlitter.java create mode 100644 awt/com/android/internal/awt/AndroidNativeEventQueue.java create mode 100644 awt/com/android/internal/awt/AndroidWTK.java create mode 100644 awt/com/android/internal/awt/AwtFactory.java create mode 100644 awt/com/android/internal/awt/ImageOutputStreamWrapper.java create mode 100644 awt/java/awt/AWTEvent.java create mode 100644 awt/java/awt/AWTException.java create mode 100644 awt/java/awt/AWTKeyStroke.java create mode 100644 awt/java/awt/AWTListenerList.java create mode 100644 awt/java/awt/AWTPermission.java create mode 100644 awt/java/awt/ActiveEvent.java create mode 100644 awt/java/awt/Adjustable.java create mode 100644 awt/java/awt/AlphaComposite.java create mode 100644 awt/java/awt/BasicStroke.java create mode 100644 awt/java/awt/BufferCapabilities.java create mode 100644 awt/java/awt/Color.java create mode 100644 awt/java/awt/Component.java create mode 100644 awt/java/awt/ComponentBehavior.java create mode 100644 awt/java/awt/ComponentOrientation.java create mode 100644 awt/java/awt/Composite.java create mode 100644 awt/java/awt/CompositeContext.java create mode 100644 awt/java/awt/Cursor.java create mode 100644 awt/java/awt/Dimension.java create mode 100644 awt/java/awt/Dispatcher.java create mode 100644 awt/java/awt/DisplayMode.java create mode 100644 awt/java/awt/Event.java create mode 100644 awt/java/awt/EventDispatchThread.java create mode 100644 awt/java/awt/EventQueue.java create mode 100644 awt/java/awt/EventQueueCore.java create mode 100644 awt/java/awt/Font.java create mode 100644 awt/java/awt/FontFormatException.java create mode 100644 awt/java/awt/FontMetrics.java create mode 100644 awt/java/awt/GradientPaint.java create mode 100644 awt/java/awt/GradientPaintContext.java create mode 100644 awt/java/awt/Graphics.java create mode 100644 awt/java/awt/Graphics2D.java create mode 100644 awt/java/awt/GraphicsConfiguration.java create mode 100644 awt/java/awt/GraphicsDevice.java create mode 100644 awt/java/awt/GraphicsEnvironment.java create mode 100644 awt/java/awt/HeadlessException.java create mode 100644 awt/java/awt/HeadlessGraphicsEnvironment.java create mode 100644 awt/java/awt/HeadlessToolkit.java create mode 100644 awt/java/awt/IllegalComponentStateException.java create mode 100644 awt/java/awt/Image.java create mode 100644 awt/java/awt/ImageCapabilities.java create mode 100644 awt/java/awt/Insets.java create mode 100644 awt/java/awt/ItemSelectable.java create mode 100644 awt/java/awt/MenuComponent.java create mode 100644 awt/java/awt/MenuContainer.java create mode 100644 awt/java/awt/ModalContext.java create mode 100644 awt/java/awt/MouseDispatcher.java create mode 100644 awt/java/awt/Paint.java create mode 100644 awt/java/awt/PaintContext.java create mode 100644 awt/java/awt/Point.java create mode 100644 awt/java/awt/Polygon.java create mode 100644 awt/java/awt/Rectangle.java create mode 100644 awt/java/awt/RenderingHints.java create mode 100644 awt/java/awt/Shape.java create mode 100644 awt/java/awt/Stroke.java create mode 100644 awt/java/awt/Toolkit.java create mode 100644 awt/java/awt/ToolkitImpl.java create mode 100644 awt/java/awt/Transparency.java create mode 100644 awt/java/awt/color/CMMException.java create mode 100644 awt/java/awt/color/ColorSpace.java create mode 100644 awt/java/awt/color/ICC_ColorSpace.java create mode 100644 awt/java/awt/color/ICC_Profile.java create mode 100644 awt/java/awt/color/ICC_ProfileGray.java create mode 100644 awt/java/awt/color/ICC_ProfileRGB.java create mode 100644 awt/java/awt/color/ICC_ProfileStub.java create mode 100644 awt/java/awt/color/ProfileDataException.java create mode 100644 awt/java/awt/color/package.html create mode 100644 awt/java/awt/event/AWTEventListener.java create mode 100644 awt/java/awt/event/AWTEventListenerProxy.java create mode 100644 awt/java/awt/event/ActionEvent.java create mode 100644 awt/java/awt/event/ActionListener.java create mode 100644 awt/java/awt/event/AdjustmentEvent.java create mode 100644 awt/java/awt/event/AdjustmentListener.java create mode 100644 awt/java/awt/event/ComponentAdapter.java create mode 100644 awt/java/awt/event/ComponentEvent.java create mode 100644 awt/java/awt/event/ComponentListener.java create mode 100644 awt/java/awt/event/ContainerAdapter.java create mode 100644 awt/java/awt/event/ContainerEvent.java create mode 100644 awt/java/awt/event/ContainerListener.java create mode 100644 awt/java/awt/event/FocusAdapter.java create mode 100644 awt/java/awt/event/FocusEvent.java create mode 100644 awt/java/awt/event/FocusListener.java create mode 100644 awt/java/awt/event/HierarchyBoundsAdapter.java create mode 100644 awt/java/awt/event/HierarchyBoundsListener.java create mode 100644 awt/java/awt/event/HierarchyEvent.java create mode 100644 awt/java/awt/event/HierarchyListener.java create mode 100644 awt/java/awt/event/InputEvent.java create mode 100644 awt/java/awt/event/InputMethodEvent.java create mode 100644 awt/java/awt/event/InputMethodListener.java create mode 100644 awt/java/awt/event/InvocationEvent.java create mode 100644 awt/java/awt/event/ItemEvent.java create mode 100644 awt/java/awt/event/ItemListener.java create mode 100644 awt/java/awt/event/KeyAdapter.java create mode 100644 awt/java/awt/event/KeyEvent.java create mode 100644 awt/java/awt/event/KeyListener.java create mode 100644 awt/java/awt/event/MouseAdapter.java create mode 100644 awt/java/awt/event/MouseEvent.java create mode 100644 awt/java/awt/event/MouseListener.java create mode 100644 awt/java/awt/event/MouseMotionAdapter.java create mode 100644 awt/java/awt/event/MouseMotionListener.java create mode 100644 awt/java/awt/event/MouseWheelEvent.java create mode 100644 awt/java/awt/event/MouseWheelListener.java create mode 100644 awt/java/awt/event/PaintEvent.java create mode 100644 awt/java/awt/event/TextEvent.java create mode 100644 awt/java/awt/event/TextListener.java create mode 100644 awt/java/awt/event/WindowAdapter.java create mode 100644 awt/java/awt/event/WindowEvent.java create mode 100644 awt/java/awt/event/WindowFocusListener.java create mode 100644 awt/java/awt/event/WindowListener.java create mode 100644 awt/java/awt/event/WindowStateListener.java create mode 100644 awt/java/awt/font/FontRenderContext.java create mode 100644 awt/java/awt/font/GlyphJustificationInfo.java create mode 100644 awt/java/awt/font/GlyphMetrics.java create mode 100644 awt/java/awt/font/GlyphVector.java create mode 100644 awt/java/awt/font/GraphicAttribute.java create mode 100644 awt/java/awt/font/ImageGraphicAttribute.java create mode 100644 awt/java/awt/font/LineBreakMeasurer.java create mode 100644 awt/java/awt/font/LineMetrics.java create mode 100644 awt/java/awt/font/MultipleMaster.java create mode 100644 awt/java/awt/font/OpenType.java create mode 100644 awt/java/awt/font/ShapeGraphicAttribute.java create mode 100644 awt/java/awt/font/TextHitInfo.java create mode 100644 awt/java/awt/font/TextLayout.java create mode 100644 awt/java/awt/font/TextMeasurer.java create mode 100644 awt/java/awt/font/TransformAttribute.java create mode 100644 awt/java/awt/font/package.html create mode 100644 awt/java/awt/geom/AffineTransform.java create mode 100644 awt/java/awt/geom/Arc2D.java create mode 100644 awt/java/awt/geom/Area.java create mode 100644 awt/java/awt/geom/CubicCurve2D.java create mode 100644 awt/java/awt/geom/Dimension2D.java create mode 100644 awt/java/awt/geom/Ellipse2D.java create mode 100644 awt/java/awt/geom/FlatteningPathIterator.java create mode 100644 awt/java/awt/geom/GeneralPath.java create mode 100644 awt/java/awt/geom/IllegalPathStateException.java create mode 100644 awt/java/awt/geom/Line2D.java create mode 100644 awt/java/awt/geom/NoninvertibleTransformException.java create mode 100644 awt/java/awt/geom/PathIterator.java create mode 100644 awt/java/awt/geom/Point2D.java create mode 100644 awt/java/awt/geom/QuadCurve2D.java create mode 100644 awt/java/awt/geom/Rectangle2D.java create mode 100644 awt/java/awt/geom/RectangularShape.java create mode 100644 awt/java/awt/geom/RoundRectangle2D.java create mode 100644 awt/java/awt/geom/package.html create mode 100644 awt/java/awt/im/InputContext.java create mode 100644 awt/java/awt/im/InputMethodHighlight.java create mode 100644 awt/java/awt/im/InputMethodRequests.java create mode 100644 awt/java/awt/im/InputSubset.java create mode 100644 awt/java/awt/im/spi/InputMethod.java create mode 100644 awt/java/awt/im/spi/InputMethodContext.java create mode 100644 awt/java/awt/im/spi/InputMethodDescriptor.java create mode 100644 awt/java/awt/image/AffineTransformOp.java create mode 100644 awt/java/awt/image/AreaAveragingScaleFilter.java create mode 100644 awt/java/awt/image/AwtImageBackdoorAccessorImpl.java create mode 100644 awt/java/awt/image/BandCombineOp.java create mode 100644 awt/java/awt/image/BandedSampleModel.java create mode 100644 awt/java/awt/image/BufferStrategy.java create mode 100644 awt/java/awt/image/BufferedImage.java create mode 100644 awt/java/awt/image/BufferedImageFilter.java create mode 100644 awt/java/awt/image/BufferedImageOp.java create mode 100644 awt/java/awt/image/ByteLookupTable.java create mode 100644 awt/java/awt/image/ColorConvertOp.java create mode 100644 awt/java/awt/image/ColorModel.java create mode 100644 awt/java/awt/image/ComponentColorModel.java create mode 100644 awt/java/awt/image/ComponentSampleModel.java create mode 100644 awt/java/awt/image/ConvolveOp.java create mode 100644 awt/java/awt/image/CropImageFilter.java create mode 100644 awt/java/awt/image/DataBuffer.java create mode 100644 awt/java/awt/image/DataBufferByte.java create mode 100644 awt/java/awt/image/DataBufferDouble.java create mode 100644 awt/java/awt/image/DataBufferFloat.java create mode 100644 awt/java/awt/image/DataBufferInt.java create mode 100644 awt/java/awt/image/DataBufferShort.java create mode 100644 awt/java/awt/image/DataBufferUShort.java create mode 100644 awt/java/awt/image/DirectColorModel.java create mode 100644 awt/java/awt/image/FilteredImageSource.java create mode 100644 awt/java/awt/image/ImageConsumer.java create mode 100644 awt/java/awt/image/ImageFilter.java create mode 100644 awt/java/awt/image/ImageObserver.java create mode 100644 awt/java/awt/image/ImageProducer.java create mode 100644 awt/java/awt/image/ImagingOpException.java create mode 100644 awt/java/awt/image/IndexColorModel.java create mode 100644 awt/java/awt/image/Kernel.java create mode 100644 awt/java/awt/image/LookupOp.java create mode 100644 awt/java/awt/image/LookupTable.java create mode 100644 awt/java/awt/image/MemoryImageSource.java create mode 100644 awt/java/awt/image/MultiPixelPackedSampleModel.java create mode 100644 awt/java/awt/image/PackedColorModel.java create mode 100644 awt/java/awt/image/PixelGrabber.java create mode 100644 awt/java/awt/image/PixelInterleavedSampleModel.java create mode 100644 awt/java/awt/image/RGBImageFilter.java create mode 100644 awt/java/awt/image/Raster.java create mode 100644 awt/java/awt/image/RasterFormatException.java create mode 100644 awt/java/awt/image/RasterOp.java create mode 100644 awt/java/awt/image/RenderedImage.java create mode 100644 awt/java/awt/image/ReplicateScaleFilter.java create mode 100644 awt/java/awt/image/RescaleOp.java create mode 100644 awt/java/awt/image/SampleModel.java create mode 100644 awt/java/awt/image/ShortLookupTable.java create mode 100644 awt/java/awt/image/SinglePixelPackedSampleModel.java create mode 100644 awt/java/awt/image/TileObserver.java create mode 100644 awt/java/awt/image/VolatileImage.java create mode 100644 awt/java/awt/image/WritableRaster.java create mode 100644 awt/java/awt/image/WritableRenderedImage.java create mode 100644 awt/java/awt/image/package.html create mode 100644 awt/java/awt/image/renderable/ContextualRenderedImageFactory.java create mode 100644 awt/java/awt/image/renderable/ParameterBlock.java create mode 100644 awt/java/awt/image/renderable/RenderContext.java create mode 100644 awt/java/awt/image/renderable/RenderableImage.java create mode 100644 awt/java/awt/image/renderable/RenderableImageOp.java create mode 100644 awt/java/awt/image/renderable/RenderableImageProducer.java create mode 100644 awt/java/awt/image/renderable/RenderedImageFactory.java create mode 100644 awt/java/awt/image/renderable/package.html create mode 100644 awt/java/awt/package.html create mode 100644 awt/java/awt/peer/ButtonPeer.java create mode 100644 awt/java/awt/peer/CanvasPeer.java create mode 100644 awt/java/awt/peer/CheckboxMenuItemPeer.java create mode 100644 awt/java/awt/peer/CheckboxPeer.java create mode 100644 awt/java/awt/peer/ChoicePeer.java create mode 100644 awt/java/awt/peer/ComponentPeer.java create mode 100644 awt/java/awt/peer/DialogPeer.java create mode 100644 awt/java/awt/peer/FileDialogPeer.java create mode 100644 awt/java/awt/peer/FontPeer.java create mode 100644 awt/java/awt/peer/FramePeer.java create mode 100644 awt/java/awt/peer/LabelPeer.java create mode 100644 awt/java/awt/peer/LightweightPeer.java create mode 100644 awt/java/awt/peer/ListPeer.java create mode 100644 awt/java/awt/peer/MenuBarPeer.java create mode 100644 awt/java/awt/peer/MenuComponentPeer.java create mode 100644 awt/java/awt/peer/MenuItemPeer.java create mode 100644 awt/java/awt/peer/MenuPeer.java create mode 100644 awt/java/awt/peer/MouseInfoPeer.java create mode 100644 awt/java/awt/peer/PanelPeer.java create mode 100644 awt/java/awt/peer/PopupMenuPeer.java create mode 100644 awt/java/awt/peer/ScrollPanePeer.java create mode 100644 awt/java/awt/peer/ScrollbarPeer.java create mode 100644 awt/java/awt/peer/TextAreaPeer.java create mode 100644 awt/java/awt/peer/TextFieldPeer.java create mode 100644 awt/java/awt/peer/WindowPeer.java create mode 100644 awt/java/beans/FeatureDescriptor.java create mode 100644 awt/java/beans/IndexedPropertyDescriptor.java create mode 100644 awt/java/beans/IntrospectionException.java create mode 100644 awt/java/beans/PropertyDescriptor.java create mode 100644 awt/java/beans/PropertyEditor.java create mode 100644 awt/java/beans/PropertyEditorManager.java create mode 100644 awt/java/beans/PropertyEditorSupport.java create mode 100644 awt/java/beans/PropertyVetoException.java create mode 100644 awt/javax/imageio/IIOException.java create mode 100644 awt/javax/imageio/IIOImage.java create mode 100644 awt/javax/imageio/IIOParam.java create mode 100644 awt/javax/imageio/IIOParamController.java create mode 100644 awt/javax/imageio/ImageIO.java create mode 100644 awt/javax/imageio/ImageReadParam.java create mode 100644 awt/javax/imageio/ImageReader.java create mode 100644 awt/javax/imageio/ImageTranscoder.java create mode 100644 awt/javax/imageio/ImageTypeSpecifier.java create mode 100644 awt/javax/imageio/ImageWriteParam.java create mode 100644 awt/javax/imageio/ImageWriter.java create mode 100644 awt/javax/imageio/event/IIOReadProgressListener.java create mode 100644 awt/javax/imageio/event/IIOReadUpdateListener.java create mode 100644 awt/javax/imageio/event/IIOReadWarningListener.java create mode 100644 awt/javax/imageio/event/IIOWriteProgressListener.java create mode 100644 awt/javax/imageio/event/IIOWriteWarningListener.java create mode 100644 awt/javax/imageio/event/package.html create mode 100644 awt/javax/imageio/metadata/IIOInvalidTreeException.java create mode 100644 awt/javax/imageio/metadata/IIOMetadata.java create mode 100644 awt/javax/imageio/metadata/IIOMetadataController.java create mode 100644 awt/javax/imageio/metadata/IIOMetadataFormat.java create mode 100644 awt/javax/imageio/metadata/IIOMetadataFormatImpl.java create mode 100644 awt/javax/imageio/metadata/IIOMetadataNode.java create mode 100644 awt/javax/imageio/metadata/IIOStandardMetadataFormat.java create mode 100644 awt/javax/imageio/metadata/IIOStandardMetadataFormatResources.properties create mode 100644 awt/javax/imageio/metadata/package.html create mode 100644 awt/javax/imageio/package.html create mode 100644 awt/javax/imageio/plugins/bmp/BMPImageWriteParam.java create mode 100644 awt/javax/imageio/plugins/bmp/package.html create mode 100644 awt/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java create mode 100644 awt/javax/imageio/plugins/jpeg/JPEGImageReadParam.java create mode 100644 awt/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java create mode 100644 awt/javax/imageio/plugins/jpeg/JPEGQTable.java create mode 100644 awt/javax/imageio/plugins/jpeg/package.html create mode 100644 awt/javax/imageio/spi/IIORegistry.java create mode 100644 awt/javax/imageio/spi/IIOServiceProvider.java create mode 100644 awt/javax/imageio/spi/ImageInputStreamSpi.java create mode 100644 awt/javax/imageio/spi/ImageOutputStreamSpi.java create mode 100644 awt/javax/imageio/spi/ImageReaderSpi.java create mode 100644 awt/javax/imageio/spi/ImageReaderWriterSpi.java create mode 100644 awt/javax/imageio/spi/ImageTranscoderSpi.java create mode 100644 awt/javax/imageio/spi/ImageWriterSpi.java create mode 100644 awt/javax/imageio/spi/RegisterableService.java create mode 100644 awt/javax/imageio/spi/ServiceRegistry.java create mode 100644 awt/javax/imageio/spi/package.html create mode 100644 awt/javax/imageio/stream/FileCacheImageInputStream.java create mode 100644 awt/javax/imageio/stream/FileCacheImageOutputStream.java create mode 100644 awt/javax/imageio/stream/FileImageInputStream.java create mode 100644 awt/javax/imageio/stream/FileImageOutputStream.java create mode 100644 awt/javax/imageio/stream/IIOByteBuffer.java create mode 100644 awt/javax/imageio/stream/ImageInputStream.java create mode 100644 awt/javax/imageio/stream/ImageInputStreamImpl.java create mode 100644 awt/javax/imageio/stream/ImageOutputStream.java create mode 100644 awt/javax/imageio/stream/ImageOutputStreamImpl.java create mode 100644 awt/javax/imageio/stream/MemoryCacheImageInputStream.java create mode 100644 awt/javax/imageio/stream/MemoryCacheImageOutputStream.java create mode 100644 awt/javax/imageio/stream/package.html create mode 100644 awt/org/apache/harmony/awt/ChoiceStyle.java create mode 100644 awt/org/apache/harmony/awt/ClipRegion.java create mode 100644 awt/org/apache/harmony/awt/ComponentInternals.java create mode 100644 awt/org/apache/harmony/awt/ContextStorage.java create mode 100644 awt/org/apache/harmony/awt/ContextThreadGroup.java create mode 100644 awt/org/apache/harmony/awt/ListenerList.java create mode 100644 awt/org/apache/harmony/awt/ReadOnlyIterator.java create mode 100644 awt/org/apache/harmony/awt/gl/AwtImageBackdoorAccessor.java create mode 100644 awt/org/apache/harmony/awt/gl/CommonGraphics2D.java create mode 100644 awt/org/apache/harmony/awt/gl/CommonGraphics2DFactory.java create mode 100644 awt/org/apache/harmony/awt/gl/CommonGraphicsEnvironment.java create mode 100644 awt/org/apache/harmony/awt/gl/Crossing.java create mode 100644 awt/org/apache/harmony/awt/gl/GLVolatileImage.java create mode 100644 awt/org/apache/harmony/awt/gl/ICompositeContext.java create mode 100644 awt/org/apache/harmony/awt/gl/ImageSurface.java create mode 100644 awt/org/apache/harmony/awt/gl/MultiRectArea.java create mode 100644 awt/org/apache/harmony/awt/gl/MultiRectAreaOp.java create mode 100644 awt/org/apache/harmony/awt/gl/Surface.java create mode 100644 awt/org/apache/harmony/awt/gl/TextRenderer.java create mode 100644 awt/org/apache/harmony/awt/gl/XORComposite.java create mode 100644 awt/org/apache/harmony/awt/gl/color/ColorConverter.java create mode 100644 awt/org/apache/harmony/awt/gl/color/ColorScaler.java create mode 100644 awt/org/apache/harmony/awt/gl/color/ICC_ProfileHelper.java create mode 100644 awt/org/apache/harmony/awt/gl/color/ICC_Transform.java create mode 100644 awt/org/apache/harmony/awt/gl/color/LUTColorConverter.java create mode 100644 awt/org/apache/harmony/awt/gl/color/NativeCMM.java create mode 100644 awt/org/apache/harmony/awt/gl/color/NativeImageFormat.java create mode 100644 awt/org/apache/harmony/awt/gl/font/AndroidFont.java create mode 100644 awt/org/apache/harmony/awt/gl/font/AndroidFontManager.java create mode 100644 awt/org/apache/harmony/awt/gl/font/AndroidFontProperty.java create mode 100644 awt/org/apache/harmony/awt/gl/font/AndroidGlyphVector.java create mode 100644 awt/org/apache/harmony/awt/gl/font/AndroidLineMetrics.java create mode 100644 awt/org/apache/harmony/awt/gl/font/BasicMetrics.java create mode 100644 awt/org/apache/harmony/awt/gl/font/CaretManager.java create mode 100644 awt/org/apache/harmony/awt/gl/font/CommonGlyphVector.java create mode 100644 awt/org/apache/harmony/awt/gl/font/CompositeFont.java create mode 100644 awt/org/apache/harmony/awt/gl/font/FontExtraMetrics.java create mode 100644 awt/org/apache/harmony/awt/gl/font/FontFinder.java create mode 100644 awt/org/apache/harmony/awt/gl/font/FontManager.java create mode 100644 awt/org/apache/harmony/awt/gl/font/FontMetricsImpl.java create mode 100644 awt/org/apache/harmony/awt/gl/font/FontPeerImpl.java create mode 100644 awt/org/apache/harmony/awt/gl/font/FontProperty.java create mode 100644 awt/org/apache/harmony/awt/gl/font/Glyph.java create mode 100644 awt/org/apache/harmony/awt/gl/font/LineMetricsImpl.java create mode 100644 awt/org/apache/harmony/awt/gl/font/TextDecorator.java create mode 100644 awt/org/apache/harmony/awt/gl/font/TextMetricsCalculator.java create mode 100644 awt/org/apache/harmony/awt/gl/font/TextRunBreaker.java create mode 100644 awt/org/apache/harmony/awt/gl/font/TextRunSegment.java create mode 100644 awt/org/apache/harmony/awt/gl/font/TextRunSegmentImpl.java create mode 100644 awt/org/apache/harmony/awt/gl/image/BufferedImageGraphics2D.java create mode 100644 awt/org/apache/harmony/awt/gl/image/BufferedImageSource.java create mode 100644 awt/org/apache/harmony/awt/gl/image/ByteArrayDecodingImageSource.java create mode 100644 awt/org/apache/harmony/awt/gl/image/DataBufferListener.java create mode 100644 awt/org/apache/harmony/awt/gl/image/DecodingImageSource.java create mode 100644 awt/org/apache/harmony/awt/gl/image/FileDecodingImageSource.java create mode 100644 awt/org/apache/harmony/awt/gl/image/GifDecoder.java create mode 100644 awt/org/apache/harmony/awt/gl/image/ImageDecoder.java create mode 100644 awt/org/apache/harmony/awt/gl/image/ImageLoader.java create mode 100644 awt/org/apache/harmony/awt/gl/image/JpegDecoder.java create mode 100644 awt/org/apache/harmony/awt/gl/image/OffscreenImage.java create mode 100644 awt/org/apache/harmony/awt/gl/image/OrdinaryWritableRaster.java create mode 100644 awt/org/apache/harmony/awt/gl/image/PngDecoder.java create mode 100644 awt/org/apache/harmony/awt/gl/image/PngDecoderJava.java create mode 100644 awt/org/apache/harmony/awt/gl/image/URLDecodingImageSource.java create mode 100644 awt/org/apache/harmony/awt/gl/render/Blitter.java create mode 100644 awt/org/apache/harmony/awt/gl/render/JavaArcRasterizer.java create mode 100644 awt/org/apache/harmony/awt/gl/render/JavaBlitter.java create mode 100644 awt/org/apache/harmony/awt/gl/render/JavaLineRasterizer.java create mode 100644 awt/org/apache/harmony/awt/gl/render/JavaShapeRasterizer.java create mode 100644 awt/org/apache/harmony/awt/gl/render/JavaTextRenderer.java create mode 100644 awt/org/apache/harmony/awt/gl/render/NativeImageBlitter.java create mode 100644 awt/org/apache/harmony/awt/gl/render/NullBlitter.java create mode 100644 awt/org/apache/harmony/awt/im/InputMethodContext.java create mode 100644 awt/org/apache/harmony/awt/internal/nls/Messages.java create mode 100644 awt/org/apache/harmony/awt/internal/nls/MsgHelp.java create mode 100644 awt/org/apache/harmony/awt/state/MenuItemState.java create mode 100644 awt/org/apache/harmony/awt/state/MenuState.java create mode 100644 awt/org/apache/harmony/awt/state/State.java create mode 100644 awt/org/apache/harmony/awt/wtk/CreationParams.java create mode 100644 awt/org/apache/harmony/awt/wtk/CursorFactory.java create mode 100644 awt/org/apache/harmony/awt/wtk/GraphicsFactory.java create mode 100644 awt/org/apache/harmony/awt/wtk/KeyInfo.java create mode 100644 awt/org/apache/harmony/awt/wtk/NativeCursor.java create mode 100644 awt/org/apache/harmony/awt/wtk/NativeEvent.java create mode 100644 awt/org/apache/harmony/awt/wtk/NativeEventQueue.java create mode 100644 awt/org/apache/harmony/awt/wtk/NativeEventThread.java create mode 100644 awt/org/apache/harmony/awt/wtk/NativeIM.java create mode 100644 awt/org/apache/harmony/awt/wtk/NativeMouseInfo.java create mode 100644 awt/org/apache/harmony/awt/wtk/NativeRobot.java create mode 100644 awt/org/apache/harmony/awt/wtk/NativeWindow.java create mode 100644 awt/org/apache/harmony/awt/wtk/ShutdownThread.java create mode 100644 awt/org/apache/harmony/awt/wtk/ShutdownWatchdog.java create mode 100644 awt/org/apache/harmony/awt/wtk/Synchronizer.java create mode 100644 awt/org/apache/harmony/awt/wtk/SystemProperties.java create mode 100644 awt/org/apache/harmony/awt/wtk/WTK.java create mode 100644 awt/org/apache/harmony/awt/wtk/WindowFactory.java create mode 100644 awt/org/apache/harmony/beans/internal/nls/Messages.java create mode 100644 awt/org/apache/harmony/beans/internal/nls/MsgHelp.java create mode 100644 awt/org/apache/harmony/x/imageio/internal/nls/Messages.java create mode 100644 awt/org/apache/harmony/x/imageio/internal/nls/messages.properties create mode 100644 awt/org/apache/harmony/x/imageio/metadata/IIOMetadataUtils.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/jpeg/IISDecodingImageSource.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGConsts.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageReader.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageReaderSpi.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageWriter.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageWriterSpi.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGSpiConsts.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/png/PNGImageReader.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/png/PNGImageReaderSpi.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriter.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriterParam.java create mode 100644 awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriterSpi.java create mode 100644 awt/org/apache/harmony/x/imageio/spi/FileIISSpi.java create mode 100644 awt/org/apache/harmony/x/imageio/spi/FileIOSSpi.java create mode 100644 awt/org/apache/harmony/x/imageio/spi/InputStreamIISSpi.java create mode 100644 awt/org/apache/harmony/x/imageio/spi/OutputStreamIOSSpi.java create mode 100644 awt/org/apache/harmony/x/imageio/spi/RAFIISSpi.java create mode 100644 awt/org/apache/harmony/x/imageio/spi/RAFIOSSpi.java create mode 100644 awt/org/apache/harmony/x/imageio/stream/RandomAccessMemoryCache.java create mode 100644 awt/resources/org/apache/harmony/awt/internal/nls/messages.properties create mode 100644 awt/resources/org/apache/harmony/beans/internals/nls/messages.properties create mode 100644 camera/libcameraservice/Android.mk create mode 100644 camera/libcameraservice/CameraHardwareStub.cpp create mode 100644 camera/libcameraservice/CameraHardwareStub.h create mode 100644 camera/libcameraservice/CameraService.cpp create mode 100644 camera/libcameraservice/CameraService.h create mode 100644 camera/libcameraservice/CannedJpeg.h create mode 100644 camera/libcameraservice/FakeCamera.cpp create mode 100644 camera/libcameraservice/FakeCamera.h create mode 100644 cmds/runtime/Android.mk create mode 100644 cmds/runtime/MODULE_LICENSE_APACHE2 create mode 100644 cmds/runtime/NOTICE create mode 100644 cmds/runtime/ServiceManager.cpp create mode 100644 cmds/runtime/ServiceManager.h create mode 100644 cmds/runtime/SignalHandler.cpp create mode 100644 cmds/runtime/SignalHandler.h create mode 100644 cmds/runtime/main_runtime.cpp create mode 100644 cmds/surfaceflinger/Android.mk create mode 100644 cmds/surfaceflinger/main_surfaceflinger.cpp create mode 100644 im/java/android/im/BrandingResourceIDs.java create mode 100644 im/java/android/im/IImPlugin.aidl create mode 100644 im/java/android/im/ImPluginConsts.java create mode 100644 include/pim/EventRecurrence.h create mode 100644 include/private/opengles/gl_context.h create mode 100644 include/private/ui/LayerState.h create mode 100644 include/private/ui/SharedState.h create mode 100644 include/private/ui/SurfaceFlingerSynchro.h create mode 100644 include/private/utils/Static.h create mode 100644 include/private/utils/binder_module.h create mode 100644 include/private/utils/futex_synchro.h create mode 100644 include/ui/Camera.h create mode 100644 include/ui/CameraHardwareInterface.h create mode 100644 include/ui/CameraParameters.h create mode 100644 include/ui/DisplayInfo.h create mode 100644 include/ui/EGLDisplaySurface.h create mode 100644 include/ui/EGLNativeSurface.h create mode 100644 include/ui/EGLNativeWindowSurface.h create mode 100644 include/ui/EventHub.h create mode 100644 include/ui/ICamera.h create mode 100644 include/ui/ICameraClient.h create mode 100644 include/ui/ICameraService.h create mode 100644 include/ui/IOverlay.h create mode 100644 include/ui/ISurface.h create mode 100644 include/ui/ISurfaceComposer.h create mode 100644 include/ui/ISurfaceFlingerClient.h create mode 100644 include/ui/KeyCharacterMap.h create mode 100644 include/ui/KeycodeLabels.h create mode 100644 include/ui/Overlay.h create mode 100644 include/ui/PixelFormat.h create mode 100644 include/ui/Point.h create mode 100644 include/ui/Rect.h create mode 100644 include/ui/Region.h create mode 100644 include/ui/Surface.h create mode 100644 include/ui/SurfaceComposerClient.h create mode 100644 include/utils.h create mode 100644 include/utils/AndroidUnicode.h create mode 100644 include/utils/Asset.h create mode 100644 include/utils/AssetDir.h create mode 100644 include/utils/AssetManager.h create mode 100644 include/utils/Atomic.h create mode 100644 include/utils/Binder.h create mode 100644 include/utils/BpBinder.h create mode 100644 include/utils/Buffer.h create mode 100644 include/utils/BufferedTextOutput.h create mode 100644 include/utils/ByteOrder.h create mode 100644 include/utils/CallStack.h create mode 100644 include/utils/Debug.h create mode 100644 include/utils/Endian.h create mode 100644 include/utils/Errors.h create mode 100644 include/utils/FileMap.h create mode 100644 include/utils/IBinder.h create mode 100644 include/utils/IInterface.h create mode 100644 include/utils/IMemory.h create mode 100644 include/utils/IPCThreadState.h create mode 100644 include/utils/IPermissionController.h create mode 100644 include/utils/IServiceManager.h create mode 100644 include/utils/KeyedVector.h create mode 100644 include/utils/List.h create mode 100644 include/utils/Log.h create mode 100644 include/utils/LogSocket.h create mode 100644 include/utils/MemoryBase.h create mode 100644 include/utils/MemoryDealer.h create mode 100644 include/utils/MemoryHeapBase.h create mode 100644 include/utils/MemoryHeapPmem.h create mode 100644 include/utils/Parcel.h create mode 100644 include/utils/Pipe.h create mode 100644 include/utils/ProcessState.h create mode 100644 include/utils/RefBase.h create mode 100644 include/utils/ResourceTypes.h create mode 100644 include/utils/SharedBuffer.h create mode 100644 include/utils/Socket.h create mode 100644 include/utils/SortedVector.h create mode 100644 include/utils/StopWatch.h create mode 100644 include/utils/String16.h create mode 100644 include/utils/String8.h create mode 100644 include/utils/SystemClock.h create mode 100644 include/utils/TextOutput.h create mode 100644 include/utils/TimeUtils.h create mode 100644 include/utils/TimerProbe.h create mode 100644 include/utils/Timers.h create mode 100644 include/utils/TypeHelpers.h create mode 100644 include/utils/Vector.h create mode 100644 include/utils/VectorImpl.h create mode 100644 include/utils/ZipEntry.h create mode 100644 include/utils/ZipFile.h create mode 100644 include/utils/ZipFileCRO.h create mode 100644 include/utils/ZipFileRO.h create mode 100644 include/utils/ZipUtils.h create mode 100644 include/utils/ashmem.h create mode 100644 include/utils/executablepath.h create mode 100644 include/utils/inet_address.h create mode 100644 include/utils/misc.h create mode 100644 include/utils/ported.h create mode 100644 include/utils/string_array.h create mode 100644 include/utils/threads.h create mode 100644 libs/audioflinger/A2dpAudioInterface.cpp create mode 100644 libs/audioflinger/A2dpAudioInterface.h create mode 100644 libs/audioflinger/Android.mk create mode 100644 libs/audioflinger/AudioBufferProvider.h create mode 100644 libs/audioflinger/AudioDumpInterface.cpp create mode 100644 libs/audioflinger/AudioDumpInterface.h create mode 100644 libs/audioflinger/AudioFlinger.cpp create mode 100644 libs/audioflinger/AudioFlinger.h create mode 100644 libs/audioflinger/AudioHardwareGeneric.cpp create mode 100644 libs/audioflinger/AudioHardwareGeneric.h create mode 100644 libs/audioflinger/AudioHardwareInterface.cpp create mode 100644 libs/audioflinger/AudioHardwareStub.cpp create mode 100644 libs/audioflinger/AudioHardwareStub.h create mode 100644 libs/audioflinger/AudioMixer.cpp create mode 100644 libs/audioflinger/AudioMixer.h create mode 100644 libs/audioflinger/AudioResampler.cpp create mode 100644 libs/audioflinger/AudioResampler.h create mode 100644 libs/audioflinger/AudioResamplerCubic.cpp create mode 100644 libs/audioflinger/AudioResamplerCubic.h create mode 100644 libs/audioflinger/AudioResamplerSinc.cpp create mode 100644 libs/audioflinger/AudioResamplerSinc.h create mode 100644 libs/surfaceflinger/Android.mk create mode 100644 libs/surfaceflinger/Barrier.h create mode 100644 libs/surfaceflinger/BlurFilter.cpp create mode 100644 libs/surfaceflinger/BlurFilter.h create mode 100644 libs/surfaceflinger/BootAnimation.cpp create mode 100644 libs/surfaceflinger/BootAnimation.h create mode 100644 libs/surfaceflinger/CPUGauge.cpp create mode 100644 libs/surfaceflinger/CPUGauge.h create mode 100644 libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp create mode 100644 libs/surfaceflinger/DisplayHardware/DisplayHardware.h create mode 100644 libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp create mode 100644 libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h create mode 100644 libs/surfaceflinger/GPUHardware/GPUHardware.cpp create mode 100644 libs/surfaceflinger/GPUHardware/GPUHardware.h create mode 100644 libs/surfaceflinger/Layer.cpp create mode 100644 libs/surfaceflinger/Layer.h create mode 100644 libs/surfaceflinger/LayerBase.cpp create mode 100644 libs/surfaceflinger/LayerBase.h create mode 100644 libs/surfaceflinger/LayerBitmap.cpp create mode 100644 libs/surfaceflinger/LayerBitmap.h create mode 100644 libs/surfaceflinger/LayerBlur.cpp create mode 100644 libs/surfaceflinger/LayerBlur.h create mode 100644 libs/surfaceflinger/LayerBuffer.cpp create mode 100644 libs/surfaceflinger/LayerBuffer.h create mode 100644 libs/surfaceflinger/LayerDim.cpp create mode 100644 libs/surfaceflinger/LayerDim.h create mode 100644 libs/surfaceflinger/LayerOrientationAnim.cpp create mode 100644 libs/surfaceflinger/LayerOrientationAnim.h create mode 100644 libs/surfaceflinger/MODULE_LICENSE_APACHE2 create mode 100644 libs/surfaceflinger/OrientationAnimation.cpp create mode 100644 libs/surfaceflinger/OrientationAnimation.h create mode 100644 libs/surfaceflinger/SurfaceFlinger.cpp create mode 100644 libs/surfaceflinger/SurfaceFlinger.h create mode 100644 libs/surfaceflinger/Tokenizer.cpp create mode 100644 libs/surfaceflinger/Tokenizer.h create mode 100644 libs/surfaceflinger/Transform.cpp create mode 100644 libs/surfaceflinger/Transform.h create mode 100644 libs/surfaceflinger/VRamHeap.cpp create mode 100644 libs/surfaceflinger/VRamHeap.h create mode 100644 libs/surfaceflinger/clz.cpp create mode 100644 libs/surfaceflinger/clz.h create mode 100644 libs/surfaceflinger/tests/Android.mk create mode 100644 libs/surfaceflinger/tests/overlays/Android.mk create mode 100644 libs/surfaceflinger/tests/overlays/overlays.cpp create mode 100644 libs/ui/Android.mk create mode 100644 libs/ui/Camera.cpp create mode 100644 libs/ui/CameraParameters.cpp create mode 100644 libs/ui/EGLDisplaySurface.cpp create mode 100644 libs/ui/EGLNativeWindowSurface.cpp create mode 100644 libs/ui/EventHub.cpp create mode 100644 libs/ui/EventRecurrence.cpp create mode 100644 libs/ui/ICamera.cpp create mode 100644 libs/ui/ICameraClient.cpp create mode 100644 libs/ui/ICameraService.cpp create mode 100644 libs/ui/IOverlay.cpp create mode 100644 libs/ui/ISurface.cpp create mode 100644 libs/ui/ISurfaceComposer.cpp create mode 100644 libs/ui/ISurfaceFlingerClient.cpp create mode 100644 libs/ui/KeyCharacterMap.cpp create mode 100644 libs/ui/KeyLayoutMap.cpp create mode 100644 libs/ui/KeyLayoutMap.h create mode 100644 libs/ui/LayerState.cpp create mode 100644 libs/ui/MODULE_LICENSE_APACHE2 create mode 100644 libs/ui/NOTICE create mode 100644 libs/ui/Overlay.cpp create mode 100644 libs/ui/PixelFormat.cpp create mode 100644 libs/ui/Point.cpp create mode 100644 libs/ui/Rect.cpp create mode 100644 libs/ui/Region.cpp create mode 100644 libs/ui/Surface.cpp create mode 100644 libs/ui/SurfaceComposerClient.cpp create mode 100644 libs/ui/SurfaceFlingerSynchro.cpp create mode 100644 libs/ui/Time.cpp create mode 100644 libs/utils/Android.mk create mode 100644 libs/utils/Asset.cpp create mode 100644 libs/utils/AssetDir.cpp create mode 100644 libs/utils/AssetManager.cpp create mode 100644 libs/utils/Binder.cpp create mode 100644 libs/utils/BpBinder.cpp create mode 100644 libs/utils/BufferedTextOutput.cpp create mode 100644 libs/utils/CallStack.cpp create mode 100644 libs/utils/Debug.cpp create mode 100644 libs/utils/FileMap.cpp create mode 100644 libs/utils/IDataConnection.cpp create mode 100644 libs/utils/IInterface.cpp create mode 100644 libs/utils/IMemory.cpp create mode 100644 libs/utils/IPCThreadState.cpp create mode 100644 libs/utils/IPermissionController.cpp create mode 100644 libs/utils/IServiceManager.cpp create mode 100644 libs/utils/InetAddress.cpp create mode 100644 libs/utils/LogSocket.cpp create mode 100644 libs/utils/MODULE_LICENSE_APACHE2 create mode 100644 libs/utils/MemoryBase.cpp create mode 100644 libs/utils/MemoryDealer.cpp create mode 100644 libs/utils/MemoryHeapBase.cpp create mode 100644 libs/utils/MemoryHeapPmem.cpp create mode 100644 libs/utils/NOTICE create mode 100644 libs/utils/Parcel.cpp create mode 100644 libs/utils/Pipe.cpp create mode 100644 libs/utils/ProcessState.cpp create mode 100644 libs/utils/README create mode 100644 libs/utils/RefBase.cpp create mode 100644 libs/utils/ResourceTypes.cpp create mode 100644 libs/utils/SharedBuffer.cpp create mode 100644 libs/utils/Socket.cpp create mode 100644 libs/utils/Static.cpp create mode 100644 libs/utils/StopWatch.cpp create mode 100644 libs/utils/String16.cpp create mode 100644 libs/utils/String8.cpp create mode 100644 libs/utils/SystemClock.cpp create mode 100644 libs/utils/TextOutput.cpp create mode 100644 libs/utils/Threads.cpp create mode 100644 libs/utils/TimerProbe.cpp create mode 100644 libs/utils/Timers.cpp create mode 100644 libs/utils/Unicode.cpp create mode 100644 libs/utils/VectorImpl.cpp create mode 100644 libs/utils/ZipEntry.cpp create mode 100644 libs/utils/ZipFile.cpp create mode 100644 libs/utils/ZipFileCRO.cpp create mode 100644 libs/utils/ZipFileRO.cpp create mode 100644 libs/utils/ZipUtils.cpp create mode 100644 libs/utils/characterData.h create mode 100644 libs/utils/executablepath_darwin.cpp create mode 100644 libs/utils/executablepath_linux.cpp create mode 100644 libs/utils/futex_synchro.c create mode 100644 libs/utils/misc.cpp create mode 100644 libs/utils/ported.cpp create mode 100644 opengl/include/EGL/egl.h create mode 100644 opengl/include/EGL/eglext.h create mode 100644 opengl/include/EGL/eglnatives.h create mode 100644 opengl/include/EGL/eglplatform.h create mode 100644 opengl/include/GLES/egl.h create mode 100644 opengl/include/GLES/gl.h create mode 100644 opengl/include/GLES/glext.h create mode 100644 opengl/include/GLES/glplatform.h create mode 100644 opengl/include/KHR/khrplatform.h create mode 100644 opengl/libagl/Android.mk create mode 100644 opengl/libagl/BufferObjectManager.cpp create mode 100644 opengl/libagl/BufferObjectManager.h create mode 100644 opengl/libagl/TextureObjectManager.cpp create mode 100644 opengl/libagl/TextureObjectManager.h create mode 100644 opengl/libagl/TokenManager.cpp create mode 100644 opengl/libagl/TokenManager.h create mode 100644 opengl/libagl/Tokenizer.cpp create mode 100644 opengl/libagl/Tokenizer.h create mode 100644 opengl/libagl/array.cpp create mode 100644 opengl/libagl/array.h create mode 100644 opengl/libagl/context.h create mode 100644 opengl/libagl/dxt.cpp create mode 100644 opengl/libagl/dxt.h create mode 100644 opengl/libagl/egl.cpp create mode 100644 opengl/libagl/fixed_asm.S create mode 100644 opengl/libagl/fp.cpp create mode 100644 opengl/libagl/fp.h create mode 100644 opengl/libagl/iterators.S create mode 100644 opengl/libagl/light.cpp create mode 100644 opengl/libagl/light.h create mode 100644 opengl/libagl/matrix.cpp create mode 100644 opengl/libagl/matrix.h create mode 100644 opengl/libagl/mipmap.cpp create mode 100644 opengl/libagl/primitives.cpp create mode 100644 opengl/libagl/primitives.h create mode 100644 opengl/libagl/state.cpp create mode 100644 opengl/libagl/state.h create mode 100644 opengl/libagl/texture.cpp create mode 100644 opengl/libagl/texture.h create mode 100644 opengl/libagl/vertex.cpp create mode 100644 opengl/libagl/vertex.h create mode 100644 opengl/libs/Android.mk create mode 100644 opengl/libs/EGL/egl.cpp create mode 100644 opengl/libs/EGL/gpu.cpp create mode 100644 opengl/libs/GLES_CM/gl.cpp create mode 100644 opengl/libs/GLES_CM/gl_api.in create mode 100644 opengl/libs/GLES_CM/gl_logger.cpp create mode 100644 opengl/libs/egl_entries.in create mode 100644 opengl/libs/egl_impl.h create mode 100644 opengl/libs/gl_entries.in create mode 100644 opengl/libs/gl_enums.in create mode 100644 opengl/libs/gl_logger.h create mode 100644 opengl/libs/hooks.h create mode 100644 opengl/libs/tools/enumextract.sh create mode 100644 opengl/tests/Android.mk create mode 100644 opengl/tests/angeles/Android.mk create mode 100644 opengl/tests/angeles/MODULE_LICENSE_BSD_OR_LGPL create mode 100644 opengl/tests/angeles/README.txt create mode 100644 opengl/tests/angeles/app-linux.c create mode 100644 opengl/tests/angeles/app.h create mode 100644 opengl/tests/angeles/cams.h create mode 100644 opengl/tests/angeles/demo.c create mode 100644 opengl/tests/angeles/gpustate.c create mode 100644 opengl/tests/angeles/include/GLES/egl.h create mode 100644 opengl/tests/angeles/include/GLES/egltypes.h create mode 100644 opengl/tests/angeles/include/GLES/gl.h create mode 100644 opengl/tests/angeles/license-BSD.txt create mode 100644 opengl/tests/angeles/license-LGPL.txt create mode 100644 opengl/tests/angeles/license.txt create mode 100644 opengl/tests/angeles/shapes.h create mode 100644 opengl/tests/filter/Android.mk create mode 100644 opengl/tests/filter/filter.c create mode 100644 opengl/tests/finish/Android.mk create mode 100644 opengl/tests/finish/finish.c create mode 100644 opengl/tests/textures/Android.mk create mode 100644 opengl/tests/textures/textures.c create mode 100644 opengl/tests/tritex/Android.mk create mode 100644 opengl/tests/tritex/tritex.c create mode 100755 opengl/tools/glgen/gen create mode 100644 opengl/tools/glgen/glspec-1.0 create mode 100644 opengl/tools/glgen/glspec-1.0ext create mode 100644 opengl/tools/glgen/glspec-1.1 create mode 100644 opengl/tools/glgen/glspec-1.1ext create mode 100644 opengl/tools/glgen/glspec-1.1extpack create mode 100644 opengl/tools/glgen/glspec-checks create mode 100644 opengl/tools/glgen/src/CFunc.java create mode 100644 opengl/tools/glgen/src/CType.java create mode 100644 opengl/tools/glgen/src/CodeEmitter.java create mode 100644 opengl/tools/glgen/src/GenerateGL.java create mode 100644 opengl/tools/glgen/src/JFunc.java create mode 100644 opengl/tools/glgen/src/JType.java create mode 100644 opengl/tools/glgen/src/JniCodeEmitter.java create mode 100644 opengl/tools/glgen/src/ParameterChecker.java create mode 100644 opengl/tools/glgen/stubs/GL10ExtHeader.java-if create mode 100644 opengl/tools/glgen/stubs/GL10Header.java-if create mode 100644 opengl/tools/glgen/stubs/GL11ExtHeader.java-if create mode 100644 opengl/tools/glgen/stubs/GL11ExtensionPackHeader.java-if create mode 100644 opengl/tools/glgen/stubs/GL11Header.java-if create mode 100644 opengl/tools/glgen/stubs/GL11ImplHeader.java-impl create mode 100644 opengl/tools/glgen/stubs/GLCHeader.cpp create mode 100644 opengl/tools/glgen/stubs/GLHeader.java-if create mode 100644 opengl/tools/glgen/stubs/GLImplHeader.java-impl create mode 100644 opengl/tools/glgen/stubs/glGetString.cpp create mode 100644 opengl/tools/glgen/stubs/glGetString.java-10-if create mode 100644 opengl/tools/glgen/stubs/glGetString.java-if create mode 100644 opengl/tools/glgen/stubs/glGetString.java-impl create mode 100644 opengl/tools/glgen/stubs/glGetString.nativeReg create mode 100644 services/Android.mk diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..267a6aafd --- /dev/null +++ b/NOTICE @@ -0,0 +1,222 @@ + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Android-specific code. == + ========================================================================= + +Android Code +Copyright 2005-2008 The Android Open Source Project + +This product includes software developed as part of +The Android Open Source Project (http://source.android.com). + + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for Apache Commons code. == + ========================================================================= + +Apache Commons +Copyright 1999-2004 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for Jakarta Commons Logging. == + ========================================================================= + +Jakarta Commons Logging (JCL) +Copyright 2005,2006 The Apache Software Foundation. + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Nuance code. == + ========================================================================= + +These files are Copyright 2007 Nuance Communications, but released under +the Apache2 License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/awt/Android.mk b/awt/Android.mk new file mode 100644 index 000000000..c7480f53a --- /dev/null +++ b/awt/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright (C) 2008 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. +# + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_JAVA_RESOURCE_DIRS := resources + +LOCAL_JAVA_LIBRARIES := core framework + +LOCAL_MODULE:= android.awt + +LOCAL_DX_FLAGS := --core-library + +include $(BUILD_JAVA_LIBRARY) diff --git a/awt/com/android/internal/awt/AndroidGraphics2D.java b/awt/com/android/internal/awt/AndroidGraphics2D.java new file mode 100644 index 000000000..9a8ae02a1 --- /dev/null +++ b/awt/com/android/internal/awt/AndroidGraphics2D.java @@ -0,0 +1,1354 @@ +/* + * Copyright 2007, 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.internal.awt; + +import com.android.internal.awt.AndroidGraphicsConfiguration; +import com.android.internal.graphics.NativeUtils; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.GeneralPath; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.PathIterator; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +import org.apache.harmony.awt.gl.ImageSurface; +import org.apache.harmony.awt.gl.MultiRectArea; +import org.apache.harmony.awt.gl.Surface; +import org.apache.harmony.awt.gl.font.AndroidGlyphVector; +import org.apache.harmony.awt.gl.font.FontMetricsImpl; +import org.apache.harmony.awt.gl.image.OffscreenImage; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; + +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.graphics.Typeface; +import android.graphics.PixelXorXfermode; +import android.view.Display; +import android.view.WindowManager; +import android.content.Context; + +public class AndroidGraphics2D extends Graphics2D { + + private int displayWidth, displayHeight; + + protected Surface dstSurf = null; + protected MultiRectArea clip = null; + + protected Composite composite = AlphaComposite.SrcOver; + protected AffineTransform transform = new AffineTransform(); + + private static AndroidGraphics2D mAg; + private static Canvas mC; + + // Android Paint + public static Paint mP; + + private static java.awt.Font mFnt; + + // Cached Matrix + public static Matrix mM; + private static FontMetrics mFm; + private static RenderingHints mRh; + private static Color mBc; + + private Area mCurrClip; + + public final static double RAD_360 = Math.PI / 180 * 360; + + // Image drawing + private AndroidJavaBlitter blitter; + private DirectColorModel cm; + private SinglePixelPackedSampleModel sm; + private WritableRaster wr; + + + public static AndroidGraphics2D getInstance() { + if (mAg == null) { + throw new RuntimeException("AndroidGraphics2D not instantiated!"); + } + return mAg; + } + + public static AndroidGraphics2D getInstance(Context ctx, Canvas c, Paint p) { + if (c == null || ctx == null) { + throw new RuntimeException( + "Illegal argument, Canvas cannot be null!"); + } + mAg = new AndroidGraphics2D(ctx, c, p); + return mAg; + } + + private AndroidGraphics2D(Context ctx, Canvas c, Paint p) { + super(); + mC = c; + mP = p; + mM = new Matrix(); + mM.reset(); + mM = mC.getMatrix(); + Rect r = mC.getClipBounds(); + int cl[] = {-1, r.top, r.left, -2, r.top, r.right, -2, r.bottom, r.right, -2, r.bottom, r.left}; + mCurrClip = new Area(createShape(cl)); + if(ctx != null) { + WindowManager wm = (WindowManager)ctx.getSystemService(Context.WINDOW_SERVICE); + Display d = wm.getDefaultDisplay(); + displayWidth = d.getWidth(); + displayHeight = d.getHeight(); + } + blitter = new AndroidJavaBlitter(c); + cm = new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000); + sm = new SinglePixelPackedSampleModel( + DataBuffer.TYPE_INT, displayWidth, displayHeight, cm.getMasks()); + wr = Raster.createWritableRaster(sm, null); + dstSurf = new ImageSurface(cm, wr); + } + + @Override + public void addRenderingHints(Map hints) { + if (mRh == null) { + mRh = (RenderingHints) hints; + } + mRh.add((RenderingHints) hints); + } + + public float[] getMatrix() { + float[] f = new float[9]; + mC.getMatrix().getValues(f); + return f; + } + + /** + * + * @return a Matrix in Android format + */ + public float[] getInverseMatrix() { + AffineTransform af = new AffineTransform(createAWTMatrix(getMatrix())); + try { + af = af.createInverse(); + } catch (NoninvertibleTransformException e) { + } + return createMatrix(af); + } + + private Path getPath(Shape s) { + Path path = new Path(); + PathIterator pi = s.getPathIterator(null); + while (pi.isDone() == false) { + getCurrentSegment(pi, path); + pi.next(); + } + return path; + } + + private void getCurrentSegment(PathIterator pi, Path path) { + float[] coordinates = new float[6]; + int type = pi.currentSegment(coordinates); + switch (type) { + case PathIterator.SEG_MOVETO: + path.moveTo(coordinates[0], coordinates[1]); + break; + case PathIterator.SEG_LINETO: + path.lineTo(coordinates[0], coordinates[1]); + break; + case PathIterator.SEG_QUADTO: + path.quadTo(coordinates[0], coordinates[1], coordinates[2], + coordinates[3]); + break; + case PathIterator.SEG_CUBICTO: + path.cubicTo(coordinates[0], coordinates[1], coordinates[2], + coordinates[3], coordinates[4], coordinates[5]); + break; + case PathIterator.SEG_CLOSE: + path.close(); + break; + default: + break; + } + } + + private Shape createShape(int[] arr) { + Shape s = new GeneralPath(); + for(int i = 0; i < arr.length; i++) { + int type = arr[i]; + switch (type) { + case -1: + //MOVETO + ((GeneralPath)s).moveTo(arr[++i], arr[++i]); + break; + case -2: + //LINETO + ((GeneralPath)s).lineTo(arr[++i], arr[++i]); + break; + case -3: + //QUADTO + ((GeneralPath)s).quadTo(arr[++i], arr[++i], arr[++i], + arr[++i]); + break; + case -4: + //CUBICTO + ((GeneralPath)s).curveTo(arr[++i], arr[++i], arr[++i], + arr[++i], arr[++i], arr[++i]); + break; + case -5: + //CLOSE + return s; + default: + break; + } + } + return s; + } + /* + public int[] getPixels() { + return mC.getPixels(); + }*/ + + public static float getRadian(float degree) { + return (float) ((Math.PI / 180) * degree); + } + + private Shape getShape() { + return null; + } + + public static float getDegree(float radian) { + return (float) ((180 / Math.PI) * radian); + } + + /* + * Degree in radian + */ + public static float getEllipsisX(float degree, float princAxis) { + return (float) Math.cos(degree) * princAxis; + } + + public static float getEllipsisY(float degree, float conAxis) { + return (float) Math.sin(degree) * conAxis; + } + + @Override + public void clip(Shape s) { + mC.clipPath(getPath(s)); + } + + public void setCanvas(Canvas c) { + mC = c; + } + + @Override + public void draw(Shape s) { + if (mP == null) { + mP = new Paint(); + } + Paint.Style tmp = mP.getStyle(); + mP.setStyle(Paint.Style.STROKE); + mC.drawPath(getPath(s), mP); + mP.setStyle(tmp); + } +/* + private ArrayList getSegments(Shape s) { + ArrayList arr = new ArrayList(); + PathIterator pi = s.getPathIterator(null); + while (pi.isDone() == false) { + getCurrentSegment(pi, arr); + pi.next(); + } + return arr; + } + + private void getCurrentSegment(PathIterator pi, ArrayList arr) { + float[] coordinates = new float[6]; + int type = pi.currentSegment(coordinates); + switch (type) { + case PathIterator.SEG_MOVETO: + arr.add(new Integer(-1)); + break; + case PathIterator.SEG_LINETO: + arr.add(new Integer(-2)); + break; + case PathIterator.SEG_QUADTO: + arr.add(new Integer(-3)); + break; + case PathIterator.SEG_CUBICTO: + arr.add(new Integer(-4)); + break; + case PathIterator.SEG_CLOSE: + arr.add(new Integer(-5)); + break; + default: + break; + } + } +*/ + /* + * Convenience method, not standard AWT + */ + public void draw(Path s) { + if (mP == null) { + mP = new Paint(); + } + Paint.Style tmp = mP.getStyle(); + mP.setStyle(Paint.Style.STROKE); + s.transform(mM); + mC.drawPath(s, mP); + mP.setStyle(tmp); + } + + @Override + public void drawGlyphVector(GlyphVector g, float x, float y) { + // TODO draw at x, y + // draw(g.getOutline()); + /* + Matrix matrix = new Matrix(); + matrix.setTranslate(x, y); + Path pth = getPath(g.getOutline()); + pth.transform(matrix); + draw(pth); + */ + Path path = new Path(); + char[] c = ((AndroidGlyphVector)g).getGlyphs(); + mP.getTextPath(c, 0, c.length, x, y, path); + mC.drawPath(path, mP); + } + + @Override + public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public void drawString(AttributedCharacterIterator iterator, float x, + float y) { + throw new RuntimeException("AttributedCharacterIterator not supported!"); + + } + + @Override + public void drawString(AttributedCharacterIterator iterator, int x, int y) { + throw new RuntimeException("AttributedCharacterIterator not supported!"); + + } + + @Override + public void drawString(String s, float x, float y) { + if (mP == null) { + mP = new Paint(); + } + Paint.Style tmp = mP.getStyle(); + + mP.setStyle(Paint.Style.FILL); + Path pth = new Path(); + mP.getTextPath(s, 0, s.length(), x, y, pth); + mC.drawPath(pth, mP); + mP.setStyle(tmp); + } + + @Override + public void drawString(String str, int x, int y) { + if (mP == null) { + mP = new Paint(); + } + Paint.Style tmp = mP.getStyle(); + mP.setStrokeWidth(0); + + mC.drawText(str.toCharArray(), 0, str.toCharArray().length, x, y, + mP); + mP.setStyle(tmp); + } + + @Override + public void fill(Shape s) { + if (mP == null) { + mP = new Paint(); + } + Paint.Style tmp = mP.getStyle(); + mP.setStyle(Paint.Style.FILL); + mC.drawPath(getPath(s), mP); + mP.setStyle(tmp); + } + + @Override + public Color getBackground() { + return mBc; + } + + @Override + public Composite getComposite() { + throw new RuntimeException("Composite not implemented!"); + } + + @Override + public GraphicsConfiguration getDeviceConfiguration() { + return new AndroidGraphicsConfiguration(); + } + + @Override + public FontRenderContext getFontRenderContext() { + return new FontRenderContext(getTransform(), mP.isAntiAlias(), true); + } + + @Override + public java.awt.Paint getPaint() { + throw new RuntimeException("AWT Paint not implemented in Android!"); + } + + public static Canvas getAndroidCanvas() { + return mC; + } + + public static Paint getAndroidPaint() { + return mP; + } + + @Override + public RenderingHints getRenderingHints() { + return mRh; + } + + @Override + public Stroke getStroke() { + if (mP != null) { + return new BasicStroke(mP.getStrokeWidth(), mP.getStrokeCap() + .ordinal(), mP.getStrokeJoin().ordinal()); + } + return null; + } + + @Override + public AffineTransform getTransform() { + return new AffineTransform(createAWTMatrix(getMatrix())); + } + + @Override + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { + // ???AWT TODO check if on stroke + return s.intersects(rect.getX(), rect.getY(), rect.getWidth(), rect + .getHeight()); + } + + @Override + public void rotate(double theta) { + mM.preRotate((float) AndroidGraphics2D + .getDegree((float) (RAD_360 - theta))); + mC.concat(mM); + } + + @Override + public void rotate(double theta, double x, double y) { + mM.preRotate((float) AndroidGraphics2D.getDegree((float) theta), + (float) x, (float) y); + mC.concat(mM); + } + + @Override + public void scale(double sx, double sy) { + mM.setScale((float) sx, (float) sy); + mC.concat(mM); + } + + @Override + public void setBackground(Color color) { + mBc = color; + mC.clipRect(new Rect(0, 0, mC.getWidth(), mC.getHeight())); + // TODO don't limit to current clip + mC.drawARGB(color.getAlpha(), color.getRed(), color.getGreen(), color + .getBlue()); + } + + @Override + public void setComposite(Composite comp) { + throw new RuntimeException("Composite not implemented!"); + } + + public void setSpaint(Paint paint) { + mP = paint; + } + + @Override + public void setPaint(java.awt.Paint paint) { + setColor((Color)paint); + } + + @Override + public Object getRenderingHint(RenderingHints.Key key) { + if (mRh == null) { + return null; + } + return mRh.get(key); + } + + @Override + public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) { + if (mRh == null) { + mRh = new RenderingHints(hintKey, hintValue); + } else { + mRh.put(hintKey, hintValue); + } + applyHints(); + } + + @Override + public void setRenderingHints(Map hints) { + mRh = (RenderingHints) hints; + applyHints(); + } + + private void applyHints() { + Object o; + + // TODO do something like this: + /* + * Set s = mRh.keySet(); Iterator it = s.iterator(); while(it.hasNext()) { + * o = it.next(); } + */ + + // ///////////////////////////////////////////////////////////////////// + // not supported in skia + /* + * o = mRh.get(RenderingHints.KEY_ALPHA_INTERPOLATION); if + * (o.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT)) { } else + * if (o.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY)) { } + * else if (o.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED)) { } + * + * o = mRh.get(RenderingHints.KEY_COLOR_RENDERING); if + * (o.equals(RenderingHints.VALUE_COLOR_RENDER_DEFAULT)) { } else if + * (o.equals(RenderingHints.VALUE_COLOR_RENDER_QUALITY)) { } else if + * (o.equals(RenderingHints.VALUE_COLOR_RENDER_SPEED)) { } + * + * o = mRh.get(RenderingHints.KEY_DITHERING); if + * (o.equals(RenderingHints.VALUE_DITHER_DEFAULT)) { } else if + * (o.equals(RenderingHints.VALUE_DITHER_DISABLE)) { } else if + * (o.equals(RenderingHints.VALUE_DITHER_ENABLE)) { } + * + * o = mRh.get(RenderingHints.KEY_FRACTIONALMETRICS); if + * (o.equals(RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT)) { } else + * if (o.equals(RenderingHints.VALUE_FRACTIONALMETRICS_OFF)) { } else if + * (o.equals(RenderingHints.VALUE_FRACTIONALMETRICS_ON)) { } + * + * o = mRh.get(RenderingHints.KEY_INTERPOLATION); if + * (o.equals(RenderingHints.VALUE_INTERPOLATION_BICUBIC)) { } else if + * (o.equals(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) { } else if + * (o .equals(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) { } + * + * o = mRh.get(RenderingHints.KEY_RENDERING); if + * (o.equals(RenderingHints.VALUE_RENDER_DEFAULT)) { } else if + * (o.equals(RenderingHints.VALUE_RENDER_QUALITY)) { } else if + * (o.equals(RenderingHints.VALUE_RENDER_SPEED)) { } + * + * o = mRh.get(RenderingHints.KEY_STROKE_CONTROL); if + * (o.equals(RenderingHints.VALUE_STROKE_DEFAULT)) { } else if + * (o.equals(RenderingHints.VALUE_STROKE_NORMALIZE)) { } else if + * (o.equals(RenderingHints.VALUE_STROKE_PURE)) { } + */ + + o = mRh.get(RenderingHints.KEY_ANTIALIASING); + if (o != null) { + if (o.equals(RenderingHints.VALUE_ANTIALIAS_DEFAULT)) { + mP.setAntiAlias(false); + } else if (o.equals(RenderingHints.VALUE_ANTIALIAS_OFF)) { + mP.setAntiAlias(false); + } else if (o.equals(RenderingHints.VALUE_ANTIALIAS_ON)) { + mP.setAntiAlias(true); + } + } + + o = mRh.get(RenderingHints.KEY_TEXT_ANTIALIASING); + if (o != null) { + if (o.equals(RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT)) { + mP.setAntiAlias(false); + } else if (o.equals(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF)) { + mP.setAntiAlias(false); + } else if (o.equals(RenderingHints.VALUE_TEXT_ANTIALIAS_ON)) { + mP.setAntiAlias(true); + } + } + } + + @Override + public void setStroke(Stroke s) { + if (mP == null) { + mP = new Paint(); + } + BasicStroke bs = (BasicStroke) s; + mP.setStyle(Paint.Style.STROKE); + mP.setStrokeWidth(bs.getLineWidth()); + + int cap = bs.getEndCap(); + if (cap == 0) { + mP.setStrokeCap(Paint.Cap.BUTT); + } else if (cap == 1) { + mP.setStrokeCap(Paint.Cap.ROUND); + } else if (cap == 2) { + mP.setStrokeCap(Paint.Cap.SQUARE); + } + + int join = bs.getLineJoin(); + if (join == 0) { + mP.setStrokeJoin(Paint.Join.MITER); + } else if (join == 1) { + mP.setStrokeJoin(Paint.Join.ROUND); + } else if (join == 2) { + mP.setStrokeJoin(Paint.Join.BEVEL); + } + } + + public static float[] createMatrix(AffineTransform Tx) { + double[] at = new double[9]; + Tx.getMatrix(at); + float[] f = new float[at.length]; + f[0] = (float) at[0]; + f[1] = (float) at[2]; + f[2] = (float) at[4]; + f[3] = (float) at[1]; + f[4] = (float) at[3]; + f[5] = (float) at[5]; + f[6] = 0; + f[7] = 0; + f[8] = 1; + return f; + } + + private float[] createAWTMatrix(float[] matrix) { + float[] at = new float[9]; + at[0] = matrix[0]; + at[1] = matrix[3]; + at[2] = matrix[1]; + at[3] = matrix[4]; + at[4] = matrix[2]; + at[5] = matrix[5]; + at[6] = 0; + at[7] = 0; + at[8] = 1; + return at; + } + + public static Matrix createMatrixObj(AffineTransform Tx) { + Matrix m = new Matrix(); + m.reset(); + m.setValues(createMatrix(Tx)); + return m; + } + + @Override + public void setTransform(AffineTransform Tx) { + mM.reset(); + /* + * if(Tx.isIdentity()) { mM = new Matrix(); } + */ + mM.setValues(createMatrix(Tx)); + Matrix m = new Matrix(); + m.setValues(getInverseMatrix()); + mC.concat(m); + mC.concat(mM); + } + + @Override + public void shear(double shx, double shy) { + mM.setSkew((float) shx, (float) shy); + mC.concat(mM); + } + + @Override + public void transform(AffineTransform Tx) { + Matrix m = new Matrix(); + m.setValues(createMatrix(Tx)); + mC.concat(m); + } + + @Override + public void translate(double tx, double ty) { + mM.setTranslate((float) tx, (float) ty); + mC.concat(mM); + } + + @Override + public void translate(int x, int y) { + mM.setTranslate((float) x, (float) y); + mC.concat(mM); + } + + @Override + public void clearRect(int x, int y, int width, int height) { + mC.clipRect(x, y, x + width, y + height); + if (mBc != null) { + mC.drawARGB(mBc.getAlpha(), mBc.getBlue(), mBc.getGreen(), mBc + .getRed()); + } else { + mC.drawARGB(0xff, 0xff, 0xff, 0xff); + } + } + + @Override + public void clipRect(int x, int y, int width, int height) { + int cl[] = {-1, x, y, -2, x, y + width, -2, x + height, y + width, -2, x + height, y}; + Shape shp = createShape(cl); + mCurrClip.intersect(new Area(shp)); + mC.clipRect(new Rect(x, y, x + width, y + height), Region.Op.INTERSECT); + } + + @Override + public void copyArea(int sx, int sy, int width, int height, int dx, int dy) { + copyArea(mC, sx, sy, width + dx, height + dy, dx, dy); + } + + @Override + public Graphics create() { + return this; + } + + @Override + public void dispose() { + mC = null; + mP = null; + } + + @Override + public void drawArc(int x, int y, int width, int height, int sa, int ea) { + if (mP == null) { + mP = new Paint(); + } + mP.setStrokeWidth(0); + mC.drawArc(new RectF(x, y, x + width, y + height), 360 - (ea + sa), + ea, true, mP); + } + + + // ???AWT: only used for debuging, delete in final version + public void drawBitmap(Bitmap bm, float x, float y, Paint p) { + mC.drawBitmap(bm, x, y, null); + } + + @Override + public boolean drawImage(Image image, int x, int y, Color bgcolor, + ImageObserver imageObserver) { + + if(image == null) { + return true; + } + + boolean done = false; + boolean somebits = false; + Surface srcSurf = null; + if(image instanceof OffscreenImage){ + OffscreenImage oi = (OffscreenImage) image; + if((oi.getState() & ImageObserver.ERROR) != 0) { + return false; + } + done = oi.prepareImage(imageObserver); + somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; + srcSurf = oi.getImageSurface(); + }else{ + done = true; + srcSurf = Surface.getImageSurface(image); + } + + if(done || somebits) { + int w = srcSurf.getWidth(); + int h = srcSurf.getHeight(); + + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, (AffineTransform) transform.clone(), + composite, bgcolor, clip); + } + return done; + } + + @Override + public boolean drawImage(Image image, int x, int y, ImageObserver imageObserver) { + return drawImage(image, x, y, null, imageObserver); + } + + @Override + public boolean drawImage(Image image, int x, int y, int width, int height, + Color bgcolor, ImageObserver imageObserver) { + + if(image == null) { + return true; + } + if(width == 0 || height == 0) { + return true; + } + + boolean done = false; + boolean somebits = false; + Surface srcSurf = null; + + if(image instanceof OffscreenImage){ + OffscreenImage oi = (OffscreenImage) image; + if((oi.getState() & ImageObserver.ERROR) != 0) { + return false; + } + done = oi.prepareImage(imageObserver); + somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; + srcSurf = oi.getImageSurface(); + }else{ + done = true; + srcSurf = Surface.getImageSurface(image); + } + + if(done || somebits) { + int w = srcSurf.getWidth(); + int h = srcSurf.getHeight(); + if(w == width && h == height){ + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, + (AffineTransform) transform.clone(), + composite, bgcolor, clip); + }else{ + AffineTransform xform = new AffineTransform(); + xform.setToScale((float)width / w, (float)height / h); + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, + (AffineTransform) transform.clone(), + xform, composite, bgcolor, clip); + } + } + return done; + } + + @Override + public boolean drawImage(Image image, int x, int y, int width, int height, + ImageObserver imageObserver) { + return drawImage(image, x, y, width, height, null, imageObserver); + } + + @Override + public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, Color bgcolor, + ImageObserver imageObserver) { + + if(image == null) { + return true; + } + if(dx1 == dx2 || dy1 == dy2 || sx1 == sx2 || sy1 == sy2) { + return true; + } + + boolean done = false; + boolean somebits = false; + Surface srcSurf = null; + if(image instanceof OffscreenImage){ + OffscreenImage oi = (OffscreenImage) image; + if((oi.getState() & ImageObserver.ERROR) != 0) { + return false; + } + done = oi.prepareImage(imageObserver); + somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; + srcSurf = oi.getImageSurface(); + }else{ + done = true; + srcSurf = Surface.getImageSurface(image); + } + + if(done || somebits) { + + int dstX = dx1; + int dstY = dy1; + int srcX = sx1; + int srcY = sy1; + + int dstW = dx2 - dx1; + int dstH = dy2 - dy1; + int srcW = sx2 - sx1; + int srcH = sy2 - sy1; + + if(srcW == dstW && srcH == dstH){ + blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH, + (AffineTransform) transform.clone(), + composite, bgcolor, clip); + }else{ + AffineTransform xform = new AffineTransform(); + xform.setToScale((float)dstW / srcW, (float)dstH / srcH); + blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH, + (AffineTransform) transform.clone(), + xform, composite, bgcolor, clip); + } + } + return done; + } + + @Override + public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, ImageObserver imageObserver) { + + return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, + imageObserver); + } + + @Override + public void drawImage(BufferedImage bufImage, BufferedImageOp op, + int x, int y) { + + if(bufImage == null) { + return; + } + + if(op == null) { + drawImage(bufImage, x, y, null); + } else if(op instanceof AffineTransformOp){ + AffineTransformOp atop = (AffineTransformOp) op; + AffineTransform xform = atop.getTransform(); + Surface srcSurf = Surface.getImageSurface(bufImage); + int w = srcSurf.getWidth(); + int h = srcSurf.getHeight(); + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, + (AffineTransform) transform.clone(), xform, + composite, null, clip); + } else { + bufImage = op.filter(bufImage, null); + Surface srcSurf = Surface.getImageSurface(bufImage); + int w = srcSurf.getWidth(); + int h = srcSurf.getHeight(); + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, + (AffineTransform) transform.clone(), + composite, null, clip); + } + } + + @Override + public boolean drawImage(Image image, AffineTransform trans, + ImageObserver imageObserver) { + + if(image == null) { + return true; + } + if(trans == null || trans.isIdentity()) { + return drawImage(image, 0, 0, imageObserver); + } + + boolean done = false; + boolean somebits = false; + Surface srcSurf = null; + if(image instanceof OffscreenImage){ + OffscreenImage oi = (OffscreenImage) image; + if((oi.getState() & ImageObserver.ERROR) != 0) { + return false; + } + done = oi.prepareImage(imageObserver); + somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; + srcSurf = oi.getImageSurface(); + }else{ + done = true; + srcSurf = Surface.getImageSurface(image); + } + + if(done || somebits) { + int w = srcSurf.getWidth(); + int h = srcSurf.getHeight(); + AffineTransform xform = (AffineTransform) transform.clone(); + xform.concatenate(trans); + blitter.blit(0, 0, srcSurf, 0, 0, dstSurf, w, h, xform, composite, + null, clip); + } + return done; + } + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + if (mP == null) { + mP = new Paint(); + } + mC.drawLine(x1, y1, x2, y2, mP); + } + + @Override + public void drawOval(int x, int y, int width, int height) { + if (mP == null) { + mP = new Paint(); + } + mP.setStyle(Paint.Style.STROKE); + mC.drawOval(new RectF(x, y, x + width, y + height), mP); + } + + @Override + public void drawPolygon(int[] xpoints, int[] ypoints, int npoints) { + if (mP == null) { + mP = new Paint(); + } + mC.drawLine(xpoints[npoints - 1], ypoints[npoints - 1], xpoints[0], + ypoints[0], mP); + for (int i = 0; i < npoints - 1; i++) { + mC.drawLine(xpoints[i], ypoints[i], xpoints[i + 1], + ypoints[i + 1], mP); + } + } + + @Override + public void drawPolyline(int[] xpoints, int[] ypoints, int npoints) { + for (int i = 0; i < npoints - 1; i++) { + drawLine(xpoints[i], ypoints[i], xpoints[i + 1], ypoints[i + 1]); + } + + } + + @Override + public void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) { + if (mP == null) { + mP = new Paint(); + } + mC.drawRoundRect(new RectF(x, y, width, height), arcWidth, + arcHeight, mP); + } + + @Override + public void fillArc(int x, int y, int width, int height, int sa, int ea) { + if (mP == null) { + mP = new Paint(); + } + + Paint.Style tmp = mP.getStyle(); + mP.setStyle(Paint.Style.FILL_AND_STROKE); + mC.drawArc(new RectF(x, y, x + width, y + height), 360 - (sa + ea), + ea, true, mP); + + mP.setStyle(tmp); + } + + @Override + public void fillOval(int x, int y, int width, int height) { + if (mP == null) { + mP = new Paint(); + } + Paint.Style tmp = mP.getStyle(); + mP.setStyle(Paint.Style.FILL); + mC.drawOval(new RectF(x, y, x + width, y + height), mP); + mP.setStyle(tmp); + } + + @Override + public void fillPolygon(int[] xpoints, int[] ypoints, int npoints) { + if (mP == null) { + mP = new Paint(); + } + Paint.Style tmp = mP.getStyle(); + mC.save(Canvas.CLIP_SAVE_FLAG); + + mP.setStyle(Paint.Style.FILL); + + GeneralPath filledPolygon = new GeneralPath( + GeneralPath.WIND_EVEN_ODD, npoints); + filledPolygon.moveTo(xpoints[0], ypoints[0]); + for (int index = 1; index < xpoints.length; index++) { + filledPolygon.lineTo(xpoints[index], ypoints[index]); + } + filledPolygon.closePath(); + Path path = getPath(filledPolygon); + mC.clipPath(path); + mC.drawPath(path, mP); + + mP.setStyle(tmp); + mC.restore(); + } + + @Override + public void fillRect(int x, int y, int width, int height) { + if (mP == null) { + mP = new Paint(); + } + Paint.Style tmp = mP.getStyle(); + mP.setStyle(Paint.Style.FILL); + mC.drawRect(new Rect(x, y, x + width, y + height), mP); + mP.setStyle(tmp); + } + + @Override + public void drawRect(int x, int y, int width, int height) { + int[] xpoints = { x, x, x + width, x + width }; + int[] ypoints = { y, y + height, y + height, y }; + drawPolygon(xpoints, ypoints, 4); + } + + @Override + public void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) { + if (mP == null) { + mP = new Paint(); + } + mP.setStyle(Paint.Style.FILL); + mC.drawRoundRect(new RectF(x, y, x + width, y + height), arcWidth, + arcHeight, mP); + } + + @Override + public Shape getClip() { + return mCurrClip; + } + + @Override + public Rectangle getClipBounds() { + Rect r = mC.getClipBounds(); + return new Rectangle(r.left, r.top, r.width(), r.height()); + } + + @Override + public Color getColor() { + if (mP != null) { + return new Color(mP.getColor()); + } + return null; + } + + @Override + public Font getFont() { + return mFnt; + } + + @Override + public FontMetrics getFontMetrics(Font font) { + mFm = new FontMetricsImpl(font); + return mFm; + } + + @Override + public void setClip(int x, int y, int width, int height) { + int cl[] = {-1, x, y, -2, x, y + width, -2, x + height, y + width, -2, x + height, y}; + mCurrClip = new Area(createShape(cl)); + mC.clipRect(x, y, x + width, y + height, Region.Op.REPLACE); + + } + + @Override + public void setClip(Shape clip) { + mCurrClip = new Area(clip); + mC.clipPath(getPath(clip), Region.Op.REPLACE); + } + + @Override + public void setColor(Color c) { + if (mP == null) { + mP = new Paint(); + } + mP.setColor(c.getRGB()); + } + + /** + * Font mapping: + * + * Family: + * + * Android AWT + * ------------------------------------- + * serif Serif / TimesRoman + * sans-serif SansSerif / Helvetica + * monospace Monospaced / Courier + * + * Style: + * + * Android AWT + * ------------------------------------- + * normal Plain + * bold bold + * italic italic + * + */ + @Override + public void setFont(Font font) { + if (font == null) { + return; + } + if (mP == null) { + mP = new Paint(); + } + + mFnt = font; + Typeface tf = null; + int sty = font.getStyle(); + String nam = font.getName(); + String aF = ""; + if (nam != null) { + if (nam.equalsIgnoreCase("Serif") + || nam.equalsIgnoreCase("TimesRoman")) { + aF = "serif"; + } else if (nam.equalsIgnoreCase("SansSerif") + || nam.equalsIgnoreCase("Helvetica")) { + aF = "sans-serif"; + } else if (nam.equalsIgnoreCase("Monospaced") + || nam.equalsIgnoreCase("Courier")) { + aF = "monospace"; + } + } + + switch (sty) { + case Font.PLAIN: + tf = Typeface.create(aF, Typeface.NORMAL); + break; + case Font.BOLD: + tf = Typeface.create(aF, Typeface.BOLD); + break; + case Font.ITALIC: + tf = Typeface.create(aF, Typeface.ITALIC); + break; + case Font.BOLD | Font.ITALIC: + tf = Typeface.create(aF, Typeface.BOLD_ITALIC); + break; + default: + tf = Typeface.DEFAULT; + } + + mP.setTextSize(font.getSize()); + mP.setTypeface(tf); + } + + @Override + public void drawBytes(byte[] data, int offset, int length, int x, int y) { + drawString(new String(data, offset, length), x, y); + } + + @Override + public void drawPolygon(Polygon p) { + drawPolygon(p.xpoints, p.ypoints, p.npoints); + } + + @Override + public void fillPolygon(Polygon p) { + fillPolygon(p.xpoints, p.ypoints, p.npoints); + } + + @Override + public Rectangle getClipBounds(Rectangle r) { + Shape clip = getClip(); + if (clip != null) { + Rectangle b = clip.getBounds(); + r.x = b.x; + r.y = b.y; + r.width = b.width; + r.height = b.height; + } + return r; + } + + @Override + public boolean hitClip(int x, int y, int width, int height) { + return getClipBounds().intersects(new Rectangle(x, y, width, height)); + } + + @Override + public void drawChars(char[] data, int offset, int length, int x, int y) { + mC.drawText(data, offset, length, x, y, mP); + } + + @Override + public void setPaintMode() { + if (mP == null) { + mP = new Paint(); + } + mP.setXfermode(null); + } + + @Override + public void setXORMode(Color color) { + if (mP == null) { + mP = new Paint(); + } + mP.setXfermode(new PixelXorXfermode(color.getRGB())); + } + + @Override + public void fill3DRect(int x, int y, int width, int height, boolean raised) { + Color color = getColor(); + Color colorUp, colorDown; + if (raised) { + colorUp = color.brighter(); + colorDown = color.darker(); + setColor(color); + } else { + colorUp = color.darker(); + colorDown = color.brighter(); + setColor(colorUp); + } + + width--; + height--; + fillRect(x+1, y+1, width-1, height-1); + + setColor(colorUp); + fillRect(x, y, width, 1); + fillRect(x, y+1, 1, height); + + setColor(colorDown); + fillRect(x+width, y, 1, height); + fillRect(x+1, y+height, width, 1); + } + + @Override + public void draw3DRect(int x, int y, int width, int height, boolean raised) { + Color color = getColor(); + Color colorUp, colorDown; + if (raised) { + colorUp = color.brighter(); + colorDown = color.darker(); + } else { + colorUp = color.darker(); + colorDown = color.brighter(); + } + + setColor(colorUp); + fillRect(x, y, width, 1); + fillRect(x, y+1, 1, height); + + setColor(colorDown); + fillRect(x+width, y, 1, height); + fillRect(x+1, y+height, width, 1); + } + + public void copyArea(Canvas canvas, int sx, int sy, int width, int height, int dx, int dy) { + sx += getTransform().getTranslateX(); + sy += getTransform().getTranslateY(); + + NativeUtils.nativeScrollRect(canvas, + new Rect(sx, sy, sx + width, sy + height), + dx, dy); + } +} diff --git a/awt/com/android/internal/awt/AndroidGraphicsConfiguration.java b/awt/com/android/internal/awt/AndroidGraphicsConfiguration.java new file mode 100644 index 000000000..0c888cd2d --- /dev/null +++ b/awt/com/android/internal/awt/AndroidGraphicsConfiguration.java @@ -0,0 +1,96 @@ +/* + * Copyright 2007, 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.internal.awt; + +import com.android.internal.awt.AndroidGraphics2D; + +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.VolatileImage; + +import android.graphics.Canvas; + +public class AndroidGraphicsConfiguration extends GraphicsConfiguration { + + @Override + public BufferedImage createCompatibleImage(int width, int height) { + // TODO Auto-generated method stub + return null; + } + + @Override + public BufferedImage createCompatibleImage(int width, int height, + int transparency) { + // TODO Auto-generated method stub + return null; + } + + @Override + public VolatileImage createCompatibleVolatileImage(int width, int height) { + // TODO Auto-generated method stub + return null; + } + + @Override + public VolatileImage createCompatibleVolatileImage(int width, int height, + int transparency) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Rectangle getBounds() { + Canvas c = AndroidGraphics2D.getAndroidCanvas(); + if(c != null) + return new Rectangle(0, 0, c.getWidth(), c.getHeight()); + return null; + } + + @Override + public ColorModel getColorModel() { + // TODO Auto-generated method stub + return null; + } + + @Override + public ColorModel getColorModel(int transparency) { + // TODO Auto-generated method stub + return null; + } + + @Override + public AffineTransform getDefaultTransform() { + return new AffineTransform(); + } + + @Override + public GraphicsDevice getDevice() { + // TODO Auto-generated method stub + return null; + } + + @Override + public AffineTransform getNormalizingTransform() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/awt/com/android/internal/awt/AndroidGraphicsFactory.java b/awt/com/android/internal/awt/AndroidGraphicsFactory.java new file mode 100644 index 000000000..ca255b559 --- /dev/null +++ b/awt/com/android/internal/awt/AndroidGraphicsFactory.java @@ -0,0 +1,87 @@ +/* + * Copyright 2007, 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.internal.awt; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.peer.FontPeer; + +import org.apache.harmony.awt.gl.MultiRectArea; +import org.apache.harmony.awt.gl.font.AndroidFont; +import org.apache.harmony.awt.gl.font.FontManager; +import org.apache.harmony.awt.gl.font.FontMetricsImpl; +import org.apache.harmony.awt.gl.font.AndroidFontManager; +import org.apache.harmony.awt.wtk.NativeWindow; +import org.apache.harmony.awt.wtk.WindowFactory; +import org.apache.harmony.awt.gl.CommonGraphics2DFactory; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.content.Context; + +public class AndroidGraphicsFactory extends CommonGraphics2DFactory { + + public GraphicsEnvironment createGraphicsEnvironment(WindowFactory wf) { + // TODO Auto-generated method stub + return null; + } + + public Font embedFont(String fontFilePath) { + // TODO Auto-generated method stub + return null; + } + + public FontManager getFontManager() { + return AndroidFontManager.inst; + } + + public FontMetrics getFontMetrics(Font font) { + return new FontMetricsImpl(font); + } + + public FontPeer getFontPeer(Font font) { + //return getFontManager().getFontPeer(font.getName(), font.getStyle(), font.getSize()); + return new AndroidFont(font.getName(), font.getStyle(), font.getSize()); + } + + public Graphics2D getGraphics2D(NativeWindow win, int translateX, + int translateY, MultiRectArea clip) { + // TODO Auto-generated method stub + return null; + } + + public Graphics2D getGraphics2D(NativeWindow win, int translateX, + int translateY, int width, int height) { + // TODO Auto-generated method stub + return null; + } + + public Graphics2D getGraphics2D(Context ctx, Canvas c, Paint p) { + return AndroidGraphics2D.getInstance(ctx, c, p); + } + + public Graphics2D getGraphics2D(Canvas c, Paint p) { + throw new RuntimeException("Not supported!"); + } + + public Graphics2D getGraphics2D() { + return AndroidGraphics2D.getInstance(); + } + +} diff --git a/awt/com/android/internal/awt/AndroidImageDecoder.java b/awt/com/android/internal/awt/AndroidImageDecoder.java new file mode 100644 index 000000000..81b2e1a8c --- /dev/null +++ b/awt/com/android/internal/awt/AndroidImageDecoder.java @@ -0,0 +1,274 @@ +/* + * Copyright 2007, 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.internal.awt; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import java.awt.Transparency; +import java.awt.color.ColorSpace; +//import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.IndexColorModel; +import java.io.IOException; +import java.io.InputStream; +import java.util.Hashtable; + +import org.apache.harmony.awt.gl.image.DecodingImageSource; +import org.apache.harmony.awt.gl.image.ImageDecoder; +import org.apache.harmony.awt.internal.nls.Messages; + +public class AndroidImageDecoder extends ImageDecoder { + + private static final int hintflags = + ImageConsumer.SINGLEFRAME | // PNG is a static image + ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible + ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines + + // Each pixel is a grayscale sample. + private static final int PNG_COLOR_TYPE_GRAY = 0; + // Each pixel is an R,G,B triple. + private static final int PNG_COLOR_TYPE_RGB = 2; + // Each pixel is a palette index, a PLTE chunk must appear. + private static final int PNG_COLOR_TYPE_PLTE = 3; + // Each pixel is a grayscale sample, followed by an alpha sample. + private static final int PNG_COLOR_TYPE_GRAY_ALPHA = 4; + // Each pixel is an R,G,B triple, followed by an alpha sample. + private static final int PNG_COLOR_TYPE_RGBA = 6; + + private static final int NB_OF_LINES_PER_CHUNK = 1; // 0 = full image + + Bitmap bm; // The image as decoded by Android + + // Header information + int imageWidth; // Image size + int imageHeight; + int colorType; // One of the PNG_ constants from above + int bitDepth; // Number of bits per color + byte cmap[]; // The color palette for index color models + ColorModel model; // The corresponding AWT color model + + boolean transferInts; // Is transfer of type int or byte? + int dataElementsPerPixel; + + // Buffers for decoded image data + byte byteOut[]; + int intOut[]; + + + public AndroidImageDecoder(DecodingImageSource src, InputStream is) { + super(src, is); + dataElementsPerPixel = 1; + } + + @Override + /** + * All the decoding is done in Android + * + * AWT???: Method returns only once the image is completly + * decoded; decoding is not done asynchronously + */ + public void decodeImage() throws IOException { + try { + bm = BitmapFactory.decodeStream(inputStream); + if (bm == null) { + throw new IOException("Input stream empty and no image cached"); + } + + // Check size + imageWidth = bm.getWidth(); + imageHeight = bm.getHeight(); + if (imageWidth < 0 || imageHeight < 0 ) { + throw new RuntimeException("Illegal image size: " + + imageWidth + ", " + imageHeight); + } + + // We got the image fully decoded; now send all image data to AWT + setDimensions(imageWidth, imageHeight); + model = createColorModel(); + setColorModel(model); + setHints(hintflags); + setProperties(new Hashtable()); // Empty + sendPixels(NB_OF_LINES_PER_CHUNK != 0 ? NB_OF_LINES_PER_CHUNK : imageHeight); + imageComplete(ImageConsumer.STATICIMAGEDONE); + } catch (IOException e) { + throw e; + } catch (RuntimeException e) { + imageComplete(ImageConsumer.IMAGEERROR); + throw e; + } finally { + closeStream(); + } + } + + /** + * Create the AWT color model + * + * ???AWT: Android Bitmaps are always of type: ARGB-8888-Direct color model + * + * However, we leave the code here for a more powerfull decoder + * that returns a native model, and the conversion is then handled + * in AWT. With such a decoder, we would need to get the colorType, + * the bitDepth, (and the color palette for an index color model) + * from the image and construct the correct color model here. + */ + private ColorModel createColorModel() { + ColorModel cm = null; + int bmModel = 5; // TODO This doesn't exist: bm.getColorModel(); + cmap = null; + + switch (bmModel) { + // A1_MODEL + case 1: + colorType = PNG_COLOR_TYPE_GRAY; + bitDepth = 1; + break; + + // A8_MODEL + case 2: + colorType = PNG_COLOR_TYPE_GRAY_ALPHA; + bitDepth = 8; + break; + + // INDEX8_MODEL + // RGB_565_MODEL + // ARGB_8888_MODEL + case 3: + case 4: + case 5: + colorType = bm.hasAlpha() ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB; + bitDepth = 8; + break; + + default: + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + switch (colorType) { + + case PNG_COLOR_TYPE_GRAY: { + if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) { + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + // Create gray color model + int numEntries = 1 << bitDepth; + int scaleFactor = 255 / (numEntries-1); + byte comps[] = new byte[numEntries]; + for (int i = 0; i < numEntries; i++) { + comps[i] = (byte) (i * scaleFactor); + } + cm = new IndexColorModel(bitDepth, numEntries, comps, comps, comps); + + transferInts = false; + break; + } + + case PNG_COLOR_TYPE_RGB: { + if (bitDepth != 8) { + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + cm = new DirectColorModel(24, 0xff0000, 0xFF00, 0xFF); + + transferInts = true; + break; + } + + case PNG_COLOR_TYPE_PLTE: { + if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) { + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + if (cmap == null) { + throw new IllegalStateException("Palette color type is not supported"); + } + + cm = new IndexColorModel(bitDepth, cmap.length / 3, cmap, 0, false); + + transferInts = false; + break; + } + + case PNG_COLOR_TYPE_GRAY_ALPHA: { + if (bitDepth != 8) { + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), + true, false, + Transparency.TRANSLUCENT, + DataBuffer.TYPE_BYTE); + + transferInts = false; + dataElementsPerPixel = 2; + break; + } + + case PNG_COLOR_TYPE_RGBA: { + if (bitDepth != 8) { + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + cm = ColorModel.getRGBdefault(); + + transferInts = true; + break; + } + default: + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + return cm; + } + + private void sendPixels(int nbOfLinesPerChunk) { + int w = imageWidth; + int h = imageHeight; + int n = 1; + if (nbOfLinesPerChunk > 0 && nbOfLinesPerChunk <= h) { + n = nbOfLinesPerChunk; + } + + if (transferInts) { + // Create output buffer + intOut = new int[w * n]; + for (int yi = 0; yi < h; yi += n) { + // Last chunk might contain less liness + if (n > 1 && h - yi < n ) { + n = h - yi; + } + bm.getPixels(intOut, 0, w, 0, yi, w, n); + setPixels(0, yi, w, n, model, intOut, 0, w); + } + } else { + // Android bitmaps always store ints (ARGB-8888 direct model) + throw new RuntimeException("Byte transfer not supported"); + } + } + +} diff --git a/awt/com/android/internal/awt/AndroidJavaBlitter.java b/awt/com/android/internal/awt/AndroidJavaBlitter.java new file mode 100644 index 000000000..423b534cb --- /dev/null +++ b/awt/com/android/internal/awt/AndroidJavaBlitter.java @@ -0,0 +1,536 @@ +/* + * Copyright 2007, 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.internal.awt; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import org.apache.harmony.awt.gl.MultiRectArea; +import org.apache.harmony.awt.gl.Surface; +import org.apache.harmony.awt.gl.XORComposite; +import org.apache.harmony.awt.gl.render.Blitter; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +public class AndroidJavaBlitter implements Blitter { + + private Canvas canvas; + private Paint paint; + private int colorCache; + + public AndroidJavaBlitter(Canvas c) { + this.canvas = c; + this.paint = new Paint(); + this.paint.setStrokeWidth(1); + } + + /** + * Instead of multiplication and division we are using values from + * Lookup tables. + */ + static byte mulLUT[][]; // Lookup table for multiplication + static byte divLUT[][]; // Lookup table for division + + static{ + mulLUT = new byte[256][256]; + for(int i = 0; i < 256; i++){ + for(int j = 0; j < 256; j++){ + mulLUT[i][j] = (byte)((float)(i * j)/255 + 0.5f); + } + } + divLUT = new byte[256][256]; + for(int i = 1; i < 256; i++){ + for(int j = 0; j < i; j++){ + divLUT[i][j] = (byte)(((float)j / 255) / ((float)i/ 255) * 255 + 0.5f); + } + for(int j = i; j < 256; j++){ + divLUT[i][j] = (byte)255; + } + } + } + + final static int AlphaCompositeMode = 1; + final static int XORMode = 2; + + public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY, + Surface dstSurf, int width, int height, AffineTransform sysxform, + AffineTransform xform, Composite comp, Color bgcolor, + MultiRectArea clip) { + + if(xform == null){ + blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width, height, + sysxform, comp, bgcolor, clip); + }else{ + double scaleX = xform.getScaleX(); + double scaleY = xform.getScaleY(); + double scaledX = dstX / scaleX; + double scaledY = dstY / scaleY; + AffineTransform at = new AffineTransform(); + at.setToTranslation(scaledX, scaledY); + xform.concatenate(at); + sysxform.concatenate(xform); + blit(srcX, srcY, srcSurf, 0, 0, dstSurf, width, height, + sysxform, comp, bgcolor, clip); + } + + } + + public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY, + Surface dstSurf, int width, int height, AffineTransform sysxform, + Composite comp, Color bgcolor, MultiRectArea clip) { + + if(sysxform == null) { + sysxform = new AffineTransform(); + } + int type = sysxform.getType(); + switch(type){ + case AffineTransform.TYPE_TRANSLATION: + dstX += sysxform.getTranslateX(); + dstY += sysxform.getTranslateY(); + case AffineTransform.TYPE_IDENTITY: + simpleBlit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, + width, height, comp, bgcolor, clip); + break; + default: + int srcW = srcSurf.getWidth(); + int srcH = srcSurf.getHeight(); + + int w = srcX + width < srcW ? width : srcW - srcX; + int h = srcY + height < srcH ? height : srcH - srcY; + + ColorModel srcCM = srcSurf.getColorModel(); + Raster srcR = srcSurf.getRaster().createChild(srcX, srcY, + w, h, 0, 0, null); + + ColorModel dstCM = dstSurf.getColorModel(); + WritableRaster dstR = dstSurf.getRaster(); + + transformedBlit(srcCM, srcR, 0, 0, dstCM, dstR, dstX, dstY, w, h, + sysxform, comp, bgcolor, clip); + + } + } + + public void simpleBlit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY, + Surface dstSurf, int width, int height, Composite comp, + Color bgcolor, MultiRectArea clip) { + + // TODO It's possible, though unlikely that we might encounter non-int[] + // data buffers. In this case the following code needs to have several + // branches that take this into account. + data = (DataBufferInt)srcSurf.getRaster().getDataBuffer(); + int[] pixels = data.getData(); + if (!srcSurf.getColorModel().hasAlpha()) { + // This wouldn't be necessary if Android supported RGB_888. + for (int i = 0; i < pixels.length; i++) { + pixels[i] = pixels[i] | 0xff000000; + } + } + bmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); + canvas.drawBitmap(bmap, dstX, dstY, paint); + } + + public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY, + Surface dstSurf, int width, int height, Composite comp, + Color bgcolor, MultiRectArea clip) { + + javaBlt(srcX, srcY, srcSurf.getWidth(), srcSurf.getHeight(), + srcSurf.getColorModel(), srcSurf.getRaster(), dstX, dstY, + dstSurf.getWidth(), dstSurf.getHeight(), + dstSurf.getColorModel(), dstSurf.getRaster(), + width, height, comp, bgcolor, clip); + + } + + public void javaBlt(int srcX, int srcY, int srcW, int srcH, + ColorModel srcCM, Raster srcRast, int dstX, int dstY, + int dstW, int dstH, ColorModel dstCM, WritableRaster dstRast, + int width, int height, Composite comp, Color bgcolor, + MultiRectArea clip){ + + int srcX2 = srcW - 1; + int srcY2 = srcH - 1; + int dstX2 = dstW - 1; + int dstY2 = dstH - 1; + + if(srcX < 0){ + width += srcX; + srcX = 0; + } + if(srcY < 0){ + height += srcY; + srcY = 0; + } + + if(dstX < 0){ + width += dstX; + srcX -= dstX; + dstX = 0; + } + if(dstY < 0){ + height += dstY; + srcY -= dstY; + dstY = 0; + } + + if(srcX > srcX2 || srcY > srcY2) { + return; + } + if(dstX > dstX2 || dstY > dstY2) { + return; + } + + if(srcX + width > srcX2) { + width = srcX2 - srcX + 1; + } + if(srcY + height > srcY2) { + height = srcY2 - srcY + 1; + } + if(dstX + width > dstX2) { + width = dstX2 - dstX + 1; + } + if(dstY + height > dstY2) { + height = dstY2 - dstY + 1; + } + + if(width <= 0 || height <= 0) { + return; + } + + int clipRects[]; + if(clip != null) { + clipRects = clip.rect; + } else { + clipRects = new int[]{5, 0, 0, dstW - 1, dstH - 1}; + } + + boolean isAlphaComp = false; + int rule = 0; + float alpha = 0; + boolean isXORComp = false; + Color xorcolor = null; + CompositeContext cont = null; + + if(comp instanceof AlphaComposite){ + isAlphaComp = true; + AlphaComposite ac = (AlphaComposite) comp; + rule = ac.getRule(); + alpha = ac.getAlpha(); + }else if(comp instanceof XORComposite){ + isXORComp = true; + XORComposite xcomp = (XORComposite) comp; + xorcolor = xcomp.getXORColor(); + }else{ + cont = comp.createContext(srcCM, dstCM, null); + } + + for(int i = 1; i < clipRects[0]; i += 4){ + int _sx = srcX; + int _sy = srcY; + + int _dx = dstX; + int _dy = dstY; + + int _w = width; + int _h = height; + + int cx = clipRects[i]; // Clipping left top X + int cy = clipRects[i + 1]; // Clipping left top Y + int cx2 = clipRects[i + 2]; // Clipping right bottom X + int cy2 = clipRects[i + 3]; // Clipping right bottom Y + + if(_dx > cx2 || _dy > cy2 || dstX2 < cx || dstY2 < cy) { + continue; + } + + if(cx > _dx){ + int shx = cx - _dx; + _w -= shx; + _dx = cx; + _sx += shx; + } + + if(cy > _dy){ + int shy = cy - _dy; + _h -= shy; + _dy = cy; + _sy += shy; + } + + if(_dx + _w > cx2 + 1){ + _w = cx2 - _dx + 1; + } + + if(_dy + _h > cy2 + 1){ + _h = cy2 - _dy + 1; + } + + if(_sx > srcX2 || _sy > srcY2) { + continue; + } + + if(isAlphaComp){ + alphaCompose(_sx, _sy, srcCM, srcRast, _dx, _dy, + dstCM, dstRast, _w, _h, rule, alpha, bgcolor); + }else if(isXORComp){ + xorCompose(_sx, _sy, srcCM, srcRast, _dx, _dy, + dstCM, dstRast, _w, _h, xorcolor); + }else{ + Raster sr = srcRast.createChild(_sx, _sy, _w, _h, 0, 0, null); + WritableRaster dr = dstRast.createWritableChild(_dx, _dy, + _w, _h, 0, 0, null); + cont.compose(sr, dr, dr); + } + } + + } + + DataBufferInt data; + Bitmap bmap, bmp; + + void alphaCompose(int srcX, int srcY, ColorModel srcCM, Raster srcRast, + int dstX, int dstY, ColorModel dstCM, WritableRaster dstRast, + int width, int height, int rule, float alpha, Color bgcolor){ + + Object srcPixel = getTransferArray(srcRast, 1); + data = (DataBufferInt)srcRast.getDataBuffer(); + int pix[] = data.getData(); + bmap = Bitmap.createBitmap(pix, width, height, Bitmap.Config.RGB_565); + canvas.drawBitmap(bmap, dstX, dstY, paint); + } + + void render(int[] img, int x, int y, int width, int height) { + canvas.drawBitmap(Bitmap.createBitmap(img, width, height, Bitmap.Config.ARGB_8888), x, y, paint); + } + + void xorCompose(int srcX, int srcY, ColorModel srcCM, Raster srcRast, + int dstX, int dstY, ColorModel dstCM, WritableRaster dstRast, + int width, int height, Color xorcolor){ + + data = (DataBufferInt)srcRast.getDataBuffer(); + int pix[] = data.getData(); + bmap = Bitmap.createBitmap(pix, width, height, Bitmap.Config.RGB_565); + canvas.drawBitmap(bmap, dstX, dstY, paint); + } + + private void transformedBlit(ColorModel srcCM, Raster srcR, int srcX, int srcY, + ColorModel dstCM, WritableRaster dstR, int dstX, int dstY, + int width, int height, AffineTransform at, Composite comp, + Color bgcolor, MultiRectArea clip) { + + data = (DataBufferInt)srcR.getDataBuffer(); + int[] pixels = data.getData(); + if (!srcCM.hasAlpha()) { + // This wouldn't be necessary if Android supported RGB_888. + for (int i = 0; i < pixels.length; i++) { + pixels[i] = pixels[i] | 0xff000000; + } + } + bmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); + + Matrix tm = new Matrix(); + tm.setConcat(canvas.getMatrix(), AndroidGraphics2D.createMatrixObj(at)); + if(at.getType() > 1) { + bmp = Bitmap.createBitmap(bmap, 0, 0, width, height, tm, true); + } else { + bmp = Bitmap.createBitmap(bmap, 0, 0, width, height, tm, false); + } + canvas.drawBitmap(bmp, dstX + (float)at.getTranslateX(), dstY + (float)at.getTranslateY(), paint); + } + + private Rectangle2D getBounds2D(AffineTransform at, Rectangle r) { + int x = r.x; + int y = r.y; + int width = r.width; + int height = r.height; + + float[] corners = { + x, y, + x + width, y, + x + width, y + height, + x, y + height + }; + + at.transform(corners, 0, corners, 0, 4); + + Rectangle2D.Float bounds = new Rectangle2D.Float(corners[0], corners[1], 0 , 0); + bounds.add(corners[2], corners[3]); + bounds.add(corners[4], corners[5]); + bounds.add(corners[6], corners[7]); + + return bounds; + } + + private int compose(int srcRGB, boolean isSrcAlphaPre, + int dstRGB, boolean dstHasAlpha, boolean isDstAlphaPre, + int rule, int srcConstAlpha){ + + int sa, sr, sg, sb, da, dr, dg, db; + + sa = (srcRGB >> 24) & 0xff; + sr = (srcRGB >> 16) & 0xff; + sg = (srcRGB >> 8) & 0xff; + sb = srcRGB & 0xff; + + if(isSrcAlphaPre){ + sa = mulLUT[srcConstAlpha][sa] & 0xff; + sr = mulLUT[srcConstAlpha][sr] & 0xff; + sg = mulLUT[srcConstAlpha][sg] & 0xff; + sb = mulLUT[srcConstAlpha][sb] & 0xff; + }else{ + sa = mulLUT[srcConstAlpha][sa] & 0xff; + sr = mulLUT[sa][sr] & 0xff; + sg = mulLUT[sa][sg] & 0xff; + sb = mulLUT[sa][sb] & 0xff; + } + + da = (dstRGB >> 24) & 0xff; + dr = (dstRGB >> 16) & 0xff; + dg = (dstRGB >> 8) & 0xff; + db = dstRGB & 0xff; + + if(!isDstAlphaPre){ + dr = mulLUT[da][dr] & 0xff; + dg = mulLUT[da][dg] & 0xff; + db = mulLUT[da][db] & 0xff; + } + + int Fs = 0; + int Fd = 0; + switch(rule){ + case AlphaComposite.CLEAR: + break; + + case AlphaComposite.DST: + Fd = 255; + break; + + case AlphaComposite.DST_ATOP: + Fs = 255 - da; + Fd = sa; + break; + + case AlphaComposite.DST_IN: + Fd = sa; + break; + + case AlphaComposite.DST_OUT: + Fd = 255 - sa; + break; + + case AlphaComposite.DST_OVER: + Fs = 255 - da; + Fd = 255; + break; + + case AlphaComposite.SRC: + Fs = 255; + break; + + case AlphaComposite.SRC_ATOP: + Fs = da; + Fd = 255 - sa; + break; + + case AlphaComposite.SRC_IN: + Fs = da; + break; + + case AlphaComposite.SRC_OUT: + Fs = 255 - da; + break; + + case AlphaComposite.SRC_OVER: + Fs = 255; + Fd = 255 - sa; + break; + + case AlphaComposite.XOR: + Fs = 255 - da; + Fd = 255 - sa; + break; + } + dr = (mulLUT[sr][Fs] & 0xff) + (mulLUT[dr][Fd] & 0xff); + dg = (mulLUT[sg][Fs] & 0xff) + (mulLUT[dg][Fd] & 0xff); + db = (mulLUT[sb][Fs] & 0xff) + (mulLUT[db][Fd] & 0xff); + + da = (mulLUT[sa][Fs] & 0xff) + (mulLUT[da][Fd] & 0xff); + + if(!isDstAlphaPre){ + if(da != 255){ + dr = divLUT[da][dr] & 0xff; + dg = divLUT[da][dg] & 0xff; + db = divLUT[da][db] & 0xff; + } + } + if(!dstHasAlpha) { + da = 0xff; + } + dstRGB = (da << 24) | (dr << 16) | (dg << 8) | db; + + return dstRGB; + + } + + /** + * Allocate an array that can be use to store the result for a + * Raster.getDataElements call. + * @param raster Raster (type) where the getDataElements call will be made. + * @param nbPixels How many pixels to store in the array at most + * @return the result array or null + */ + private Object getTransferArray(Raster raster, int nbPixels) { + int transferType = raster.getTransferType(); + int nbDataElements = raster.getSampleModel().getNumDataElements(); + int n = nbDataElements * nbPixels; + switch (transferType) { + case DataBuffer.TYPE_BYTE: + return new byte[n]; + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + return new short[n]; + case DataBuffer.TYPE_INT: + return new int[n]; + case DataBuffer.TYPE_FLOAT: + return new float[n]; + case DataBuffer.TYPE_DOUBLE: + return new double[n]; + case DataBuffer.TYPE_UNDEFINED: + default: + return null; + } + } + + /** + * Draw a pixel + */ + private void dot(int x, int y, int clr) { + if (colorCache != clr) { + paint.setColor(clr); + colorCache = clr; + } + canvas.drawLine(x, y, x + 1, y + 1, paint); + } +} diff --git a/awt/com/android/internal/awt/AndroidNativeEventQueue.java b/awt/com/android/internal/awt/AndroidNativeEventQueue.java new file mode 100644 index 000000000..fc3061457 --- /dev/null +++ b/awt/com/android/internal/awt/AndroidNativeEventQueue.java @@ -0,0 +1,75 @@ +/* + * Copyright 2007, 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.internal.awt; + +import org.apache.harmony.awt.wtk.NativeEventQueue; + +public class AndroidNativeEventQueue extends NativeEventQueue { + + private Object eventMonitor; + + public AndroidNativeEventQueue() { + super(); + eventMonitor = getEventMonitor(); + } + + @Override + public void awake() { + synchronized (eventMonitor) { + eventMonitor.notify(); + } + } + + @Override + public void dispatchEvent() { + //???AWT + System.out.println(getClass()+": empty method called"); + } + + @Override + public long getJavaWindow() { + //???AWT + System.out.println(getClass()+": empty method called"); + return 0; + } + + @Override + public void performLater(Task task) { + //???AWT + System.out.println(getClass()+": empty method called"); + } + + @Override + public void performTask(Task task) { + //???AWT + System.out.println(getClass()+": empty method called"); + } + + @Override + public boolean waitEvent() { + while (isEmpty() ) { + synchronized (eventMonitor) { + try { + eventMonitor.wait(1000); + } catch (InterruptedException ignore) { + } + } + } + return false; + } + +} diff --git a/awt/com/android/internal/awt/AndroidWTK.java b/awt/com/android/internal/awt/AndroidWTK.java new file mode 100644 index 000000000..1609d119e --- /dev/null +++ b/awt/com/android/internal/awt/AndroidWTK.java @@ -0,0 +1,88 @@ +/* + * Copyright 2007, 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.internal.awt; + +import java.awt.GraphicsDevice; + +import org.apache.harmony.awt.wtk.CursorFactory; +import org.apache.harmony.awt.wtk.GraphicsFactory; +import org.apache.harmony.awt.wtk.NativeEventQueue; +import org.apache.harmony.awt.wtk.NativeIM; +import org.apache.harmony.awt.wtk.NativeMouseInfo; +import org.apache.harmony.awt.wtk.NativeRobot; +import org.apache.harmony.awt.wtk.SystemProperties; +import org.apache.harmony.awt.wtk.WTK; +import org.apache.harmony.awt.wtk.WindowFactory; + +public class AndroidWTK extends WTK { + + private AndroidGraphicsFactory mAgf; + private AndroidNativeEventQueue mAneq; + + @Override + public CursorFactory getCursorFactory() { + // TODO Auto-generated method stub + return null; + } + + @Override + public GraphicsFactory getGraphicsFactory() { + if(mAgf == null) { + mAgf = new AndroidGraphicsFactory(); + } + return mAgf; + } + + @Override + public NativeEventQueue getNativeEventQueue() { + if(mAneq == null) { + mAneq = new AndroidNativeEventQueue(); + } + return mAneq; + } + + @Override + public NativeIM getNativeIM() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NativeMouseInfo getNativeMouseInfo() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NativeRobot getNativeRobot(GraphicsDevice screen) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SystemProperties getSystemProperties() { + // TODO Auto-generated method stub + return null; + } + + @Override + public WindowFactory getWindowFactory() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/awt/com/android/internal/awt/AwtFactory.java b/awt/com/android/internal/awt/AwtFactory.java new file mode 100644 index 000000000..6e667b234 --- /dev/null +++ b/awt/com/android/internal/awt/AwtFactory.java @@ -0,0 +1,52 @@ +/* + * Copyright 2007, 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.internal.awt; + +import java.awt.Graphics2D; +import java.awt.Toolkit; + +import org.apache.harmony.awt.wtk.GraphicsFactory; + +import android.graphics.Canvas; +import android.graphics.Paint; + +public class AwtFactory { + + private static GraphicsFactory gf; + + /** + * Use this method to get acces to AWT drawing primitives and to + * render into the surface area of a Android widget. Origin and + * clip of the returned graphics object are the same as in the + * corresponding Android widget. + * + * @param c Canvas of the android widget to draw into + * @param p The default drawing parameters such as font, + * stroke, foreground and background colors, etc. + * @return The AWT Graphics object that makes all AWT + * drawing primitives available in the androind world. + */ + public static Graphics2D getAwtGraphics(Canvas c, Paint p) { + // AWT?? TODO: test it! + if (null == gf) { + Toolkit tk = Toolkit.getDefaultToolkit(); + gf = tk.getGraphicsFactory(); + } + return gf.getGraphics2D(c, p); + } + +} diff --git a/awt/com/android/internal/awt/ImageOutputStreamWrapper.java b/awt/com/android/internal/awt/ImageOutputStreamWrapper.java new file mode 100644 index 000000000..92185fde3 --- /dev/null +++ b/awt/com/android/internal/awt/ImageOutputStreamWrapper.java @@ -0,0 +1,66 @@ +/* + * Copyright 2007, 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.internal.awt; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.imageio.stream.ImageOutputStream; + +public class ImageOutputStreamWrapper extends OutputStream { + + protected ImageOutputStream mIos; + + private byte[] mBuff; + + public ImageOutputStreamWrapper(ImageOutputStream ios) { + if (null == ios) { + throw new IllegalArgumentException("ImageOutputStream must not be null"); + } + this.mIos = ios; + this.mBuff = new byte[1]; + } + + public ImageOutputStream getImageOutputStream() { + return mIos; + } + + @Override + public void write(int oneByte) throws IOException { + mBuff[0] = (byte)oneByte; + mIos.write(mBuff, 0, 1); + } + + public void write(byte[] b) throws IOException { + mIos.write(b, 0, b.length); + } + + public void write(byte[] b, int off, int len) throws IOException { + mIos.write(b, off, len); + } + + public void flush() throws IOException { + mIos.flush(); + } + + public void close() throws IOException { + if (mIos == null) { + throw new IOException("Stream already closed"); + } + mIos = null; + } +} diff --git a/awt/java/awt/AWTEvent.java b/awt/java/awt/AWTEvent.java new file mode 100644 index 000000000..a8dc83aaf --- /dev/null +++ b/awt/java/awt/AWTEvent.java @@ -0,0 +1,681 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev, Michael Danilov + * @version $Revision$ + */ + +package java.awt; + +import java.util.EventObject; +import java.util.Hashtable; +import java.util.EventListener; + +import java.awt.event.*; + +/** + * The abstract class AWTEvent is the base class for all AWT events. This class + * and its subclasses supersede the original java.awt.Event class. + * + * @since Android 1.0 + */ +public abstract class AWTEvent extends EventObject { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -1825314779160409405L; + + /** + * The Constant COMPONENT_EVENT_MASK indicates the event relates to a + * component. + */ + public static final long COMPONENT_EVENT_MASK = 1; + + /** + * The Constant CONTAINER_EVENT_MASK indicates the event relates to a + * container. + */ + public static final long CONTAINER_EVENT_MASK = 2; + + /** + * The Constant FOCUS_EVENT_MASK indicates the event relates to the focus. + */ + public static final long FOCUS_EVENT_MASK = 4; + + /** + * The Constant KEY_EVENT_MASK indicates the event relates to a key. + */ + public static final long KEY_EVENT_MASK = 8; + + /** + * The Constant MOUSE_EVENT_MASK indicates the event relates to the mouse. + */ + public static final long MOUSE_EVENT_MASK = 16; + + /** + * The Constant MOUSE_MOTION_EVENT_MASK indicates the event relates to a + * mouse motion. + */ + public static final long MOUSE_MOTION_EVENT_MASK = 32; + + /** + * The Constant WINDOW_EVENT_MASK indicates the event relates to a window. + */ + public static final long WINDOW_EVENT_MASK = 64; + + /** + * The Constant ACTION_EVENT_MASK indicates the event relates to an action. + */ + public static final long ACTION_EVENT_MASK = 128; + + /** + * The Constant ADJUSTMENT_EVENT_MASK indicates the event relates to an + * adjustment. + */ + public static final long ADJUSTMENT_EVENT_MASK = 256; + + /** + * The Constant ITEM_EVENT_MASK indicates the event relates to an item. + */ + public static final long ITEM_EVENT_MASK = 512; + + /** + * The Constant TEXT_EVENT_MASK indicates the event relates to text. + */ + public static final long TEXT_EVENT_MASK = 1024; + + /** + * The Constant INPUT_METHOD_EVENT_MASK indicates the event relates to an + * input method. + */ + public static final long INPUT_METHOD_EVENT_MASK = 2048; + + /** + * The Constant PAINT_EVENT_MASK indicates the event relates to a paint + * method. + */ + public static final long PAINT_EVENT_MASK = 8192; + + /** + * The Constant INVOCATION_EVENT_MASK indicates the event relates to a + * method invocation. + */ + public static final long INVOCATION_EVENT_MASK = 16384; + + /** + * The Constant HIERARCHY_EVENT_MASK indicates the event relates to a + * hierarchy. + */ + public static final long HIERARCHY_EVENT_MASK = 32768; + + /** + * The Constant HIERARCHY_BOUNDS_EVENT_MASK indicates the event relates to + * hierarchy bounds. + */ + public static final long HIERARCHY_BOUNDS_EVENT_MASK = 65536; + + /** + * The Constant MOUSE_WHEEL_EVENT_MASK indicates the event relates to the + * mouse wheel. + */ + public static final long MOUSE_WHEEL_EVENT_MASK = 131072; + + /** + * The Constant WINDOW_STATE_EVENT_MASK indicates the event relates to a + * window state. + */ + public static final long WINDOW_STATE_EVENT_MASK = 262144; + + /** + * The Constant WINDOW_FOCUS_EVENT_MASK indicates the event relates to a + * window focus. + */ + public static final long WINDOW_FOCUS_EVENT_MASK = 524288; + + /** + * The Constant RESERVED_ID_MAX indicates the maximum value for reserved AWT + * event IDs. + */ + public static final int RESERVED_ID_MAX = 1999; + + /** + * The Constant eventsMap. + */ + private static final Hashtable eventsMap = new Hashtable(); + + /** + * The converter. + */ + private static EventConverter converter; + + /** + * The ID of the event. + */ + protected int id; + + /** + * The consumed indicates whether or not the event is sent back down to the + * peer once the source has processed it (false means it's sent to the peer, + * true means it's not). + */ + protected boolean consumed; + + /** + * The dispatched by kfm. + */ + boolean dispatchedByKFM; + + /** + * The is posted. + */ + transient boolean isPosted; + + static { + eventsMap.put(new Integer(KeyEvent.KEY_TYPED), new EventDescriptor(KEY_EVENT_MASK, + KeyListener.class)); + eventsMap.put(new Integer(KeyEvent.KEY_PRESSED), new EventDescriptor(KEY_EVENT_MASK, + KeyListener.class)); + eventsMap.put(new Integer(KeyEvent.KEY_RELEASED), new EventDescriptor(KEY_EVENT_MASK, + KeyListener.class)); + eventsMap.put(new Integer(MouseEvent.MOUSE_CLICKED), new EventDescriptor(MOUSE_EVENT_MASK, + MouseListener.class)); + eventsMap.put(new Integer(MouseEvent.MOUSE_PRESSED), new EventDescriptor(MOUSE_EVENT_MASK, + MouseListener.class)); + eventsMap.put(new Integer(MouseEvent.MOUSE_RELEASED), new EventDescriptor(MOUSE_EVENT_MASK, + MouseListener.class)); + eventsMap.put(new Integer(MouseEvent.MOUSE_MOVED), new EventDescriptor( + MOUSE_MOTION_EVENT_MASK, MouseMotionListener.class)); + eventsMap.put(new Integer(MouseEvent.MOUSE_ENTERED), new EventDescriptor(MOUSE_EVENT_MASK, + MouseListener.class)); + eventsMap.put(new Integer(MouseEvent.MOUSE_EXITED), new EventDescriptor(MOUSE_EVENT_MASK, + MouseListener.class)); + eventsMap.put(new Integer(MouseEvent.MOUSE_DRAGGED), new EventDescriptor( + MOUSE_MOTION_EVENT_MASK, MouseMotionListener.class)); + eventsMap.put(new Integer(MouseEvent.MOUSE_WHEEL), new EventDescriptor( + MOUSE_WHEEL_EVENT_MASK, MouseWheelListener.class)); + eventsMap.put(new Integer(ComponentEvent.COMPONENT_MOVED), new EventDescriptor( + COMPONENT_EVENT_MASK, ComponentListener.class)); + eventsMap.put(new Integer(ComponentEvent.COMPONENT_RESIZED), new EventDescriptor( + COMPONENT_EVENT_MASK, ComponentListener.class)); + eventsMap.put(new Integer(ComponentEvent.COMPONENT_SHOWN), new EventDescriptor( + COMPONENT_EVENT_MASK, ComponentListener.class)); + eventsMap.put(new Integer(ComponentEvent.COMPONENT_HIDDEN), new EventDescriptor( + COMPONENT_EVENT_MASK, ComponentListener.class)); + eventsMap.put(new Integer(FocusEvent.FOCUS_GAINED), new EventDescriptor(FOCUS_EVENT_MASK, + FocusListener.class)); + eventsMap.put(new Integer(FocusEvent.FOCUS_LOST), new EventDescriptor(FOCUS_EVENT_MASK, + FocusListener.class)); + eventsMap.put(new Integer(PaintEvent.PAINT), new EventDescriptor(PAINT_EVENT_MASK, null)); + eventsMap.put(new Integer(PaintEvent.UPDATE), new EventDescriptor(PAINT_EVENT_MASK, null)); + eventsMap.put(new Integer(WindowEvent.WINDOW_OPENED), new EventDescriptor( + WINDOW_EVENT_MASK, WindowListener.class)); + eventsMap.put(new Integer(WindowEvent.WINDOW_CLOSING), new EventDescriptor( + WINDOW_EVENT_MASK, WindowListener.class)); + eventsMap.put(new Integer(WindowEvent.WINDOW_CLOSED), new EventDescriptor( + WINDOW_EVENT_MASK, WindowListener.class)); + eventsMap.put(new Integer(WindowEvent.WINDOW_DEICONIFIED), new EventDescriptor( + WINDOW_EVENT_MASK, WindowListener.class)); + eventsMap.put(new Integer(WindowEvent.WINDOW_ICONIFIED), new EventDescriptor( + WINDOW_EVENT_MASK, WindowListener.class)); + eventsMap.put(new Integer(WindowEvent.WINDOW_STATE_CHANGED), new EventDescriptor( + WINDOW_STATE_EVENT_MASK, WindowStateListener.class)); + eventsMap.put(new Integer(WindowEvent.WINDOW_LOST_FOCUS), new EventDescriptor( + WINDOW_FOCUS_EVENT_MASK, WindowFocusListener.class)); + eventsMap.put(new Integer(WindowEvent.WINDOW_GAINED_FOCUS), new EventDescriptor( + WINDOW_FOCUS_EVENT_MASK, WindowFocusListener.class)); + eventsMap.put(new Integer(WindowEvent.WINDOW_DEACTIVATED), new EventDescriptor( + WINDOW_EVENT_MASK, WindowListener.class)); + eventsMap.put(new Integer(WindowEvent.WINDOW_ACTIVATED), new EventDescriptor( + WINDOW_EVENT_MASK, WindowListener.class)); + eventsMap.put(new Integer(HierarchyEvent.HIERARCHY_CHANGED), new EventDescriptor( + HIERARCHY_EVENT_MASK, HierarchyListener.class)); + eventsMap.put(new Integer(HierarchyEvent.ANCESTOR_MOVED), new EventDescriptor( + HIERARCHY_BOUNDS_EVENT_MASK, HierarchyBoundsListener.class)); + eventsMap.put(new Integer(HierarchyEvent.ANCESTOR_RESIZED), new EventDescriptor( + HIERARCHY_BOUNDS_EVENT_MASK, HierarchyBoundsListener.class)); + eventsMap.put(new Integer(ContainerEvent.COMPONENT_ADDED), new EventDescriptor( + CONTAINER_EVENT_MASK, ContainerListener.class)); + eventsMap.put(new Integer(ContainerEvent.COMPONENT_REMOVED), new EventDescriptor( + CONTAINER_EVENT_MASK, ContainerListener.class)); + eventsMap.put(new Integer(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED), new EventDescriptor( + INPUT_METHOD_EVENT_MASK, InputMethodListener.class)); + eventsMap.put(new Integer(InputMethodEvent.CARET_POSITION_CHANGED), new EventDescriptor( + INPUT_METHOD_EVENT_MASK, InputMethodListener.class)); + eventsMap.put(new Integer(InvocationEvent.INVOCATION_DEFAULT), new EventDescriptor( + INVOCATION_EVENT_MASK, null)); + eventsMap.put(new Integer(ItemEvent.ITEM_STATE_CHANGED), new EventDescriptor( + ITEM_EVENT_MASK, ItemListener.class)); + eventsMap.put(new Integer(TextEvent.TEXT_VALUE_CHANGED), new EventDescriptor( + TEXT_EVENT_MASK, TextListener.class)); + eventsMap.put(new Integer(ActionEvent.ACTION_PERFORMED), new EventDescriptor( + ACTION_EVENT_MASK, ActionListener.class)); + eventsMap.put(new Integer(AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED), new EventDescriptor( + ADJUSTMENT_EVENT_MASK, AdjustmentListener.class)); + converter = new EventConverter(); + } + + /** + * Instantiates a new AWT event from the specified Event object. + * + * @param event + * the Event object. + */ + public AWTEvent(Event event) { + this(event.target, event.id); + } + + /** + * Instantiates a new AWT event with the specified object and type. + * + * @param source + * the source Object. + * @param id + * the event's type. + */ + public AWTEvent(Object source, int id) { + super(source); + this.id = id; + consumed = false; + } + + /** + * Gets the event's type. + * + * @return the event type ID. + */ + public int getID() { + return id; + } + + /** + * Sets a new source for the AWTEvent. + * + * @param newSource + * the new source Object for the AWTEvent. + */ + public void setSource(Object newSource) { + source = newSource; + } + + /** + * Returns a String representation of the AWTEvent. + * + * @return the String representation of the AWTEvent. + */ + @Override + public String toString() { + /* + * The format is based on 1.5 release behavior which can be revealed by + * the following code: AWTEvent event = new AWTEvent(new Component(){}, + * 1){}; System.out.println(event); + */ + String name = ""; //$NON-NLS-1$ + + if (source instanceof Component && (source != null)) { + Component comp = (Component)getSource(); + name = comp.getName(); + if (name == null) { + name = ""; //$NON-NLS-1$ + } + } + + return (getClass().getName() + "[" + paramString() + "]" //$NON-NLS-1$ //$NON-NLS-2$ + + " on " + (name.length() > 0 ? name : source)); //$NON-NLS-1$ + } + + /** + * Returns a string representation of the AWTEvent state. + * + * @return a string representation of the AWTEvent state. + */ + public String paramString() { + // nothing to implement: all event types must override this method + return ""; //$NON-NLS-1$ + } + + /** + * Checks whether or not this AWTEvent has been consumed. + * + * @return true, if this AWTEvent has been consumed, false otherwise. + */ + protected boolean isConsumed() { + return consumed; + } + + /** + * Consumes the AWTEvent. + */ + protected void consume() { + consumed = true; + } + + /** + * Convert AWTEvent object to a corresponding (deprecated) Event object. + * + * @return new Event object which is a converted AWTEvent object or null if + * the conversion is not possible + */ + Event getEvent() { + + if (id == ActionEvent.ACTION_PERFORMED) { + ActionEvent ae = (ActionEvent)this; + return converter.convertActionEvent(ae); + + } else if (id == AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED) { + AdjustmentEvent ae = (AdjustmentEvent)this; + return converter.convertAdjustmentEvent(ae); + + // ???AWT + // } else if (id == ComponentEvent.COMPONENT_MOVED + // && source instanceof Window) { + // //the only type of Component events is COMPONENT_MOVED on window + // ComponentEvent ce = (ComponentEvent) this; + // return converter.convertComponentEvent(ce); + + } else if (id >= FocusEvent.FOCUS_FIRST && id <= FocusEvent.FOCUS_LAST) { + // nothing to convert + + // ???AWT + // } else if (id == ItemEvent.ITEM_STATE_CHANGED) { + // ItemEvent ie = (ItemEvent) this; + // return converter.convertItemEvent(ie); + + } else if (id == KeyEvent.KEY_PRESSED || id == KeyEvent.KEY_RELEASED) { + KeyEvent ke = (KeyEvent)this; + return converter.convertKeyEvent(ke); + } else if (id >= MouseEvent.MOUSE_FIRST && id <= MouseEvent.MOUSE_LAST) { + MouseEvent me = (MouseEvent)this; + return converter.convertMouseEvent(me); + } else if (id == WindowEvent.WINDOW_CLOSING || id == WindowEvent.WINDOW_ICONIFIED + || id == WindowEvent.WINDOW_DEICONIFIED) { + // nothing to convert + } else { + return null; + } + return new Event(source, id, null); + } + + /** + * The class EventDescriptor. + */ + static final class EventDescriptor { + + /** + * The event mask. + */ + final long eventMask; + + /** + * The listener type. + */ + final Class listenerType; + + /** + * Instantiates a new event descriptor. + * + * @param eventMask + * the event mask. + * @param listenerType + * the listener type. + */ + EventDescriptor(long eventMask, Class listenerType) { + this.eventMask = eventMask; + this.listenerType = listenerType; + } + + } + + /** + * The class EventTypeLookup. + */ + static final class EventTypeLookup { + + /** + * The last event. + */ + private AWTEvent lastEvent = null; + + /** + * The last event descriptor. + */ + private EventDescriptor lastEventDescriptor = null; + + /** + * Gets the event descriptor. + * + * @param event + * the event. + * @return the event descriptor. + */ + EventDescriptor getEventDescriptor(AWTEvent event) { + synchronized (this) { + if (event != lastEvent) { + lastEvent = event; + lastEventDescriptor = eventsMap.get(new Integer(event.id)); + } + + return lastEventDescriptor; + } + } + + /** + * Gets the event mask. + * + * @param event + * the event. + * @return the event mask. + */ + long getEventMask(AWTEvent event) { + final EventDescriptor ed = getEventDescriptor(event); + return ed == null ? -1 : ed.eventMask; + } + } + + /** + * The class EventConverter. + */ + static final class EventConverter { + + /** + * The constant OLD_MOD_MASK. + */ + static final int OLD_MOD_MASK = Event.ALT_MASK | Event.CTRL_MASK | Event.META_MASK + | Event.SHIFT_MASK; + + /** + * Convert action event. + * + * @param ae + * the ae. + * @return the event. + */ + Event convertActionEvent(ActionEvent ae) { + Event evt = new Event(ae.getSource(), ae.getID(), ae.getActionCommand()); + evt.when = ae.getWhen(); + evt.modifiers = ae.getModifiers() & OLD_MOD_MASK; + + /* + * if (source instanceof Button) { arg = ((Button) + * source).getLabel(); } else if (source instanceof Checkbox) { arg + * = new Boolean(((Checkbox) source).getState()); } else if (source + * instanceof CheckboxMenuItem) { arg = ((CheckboxMenuItem) + * source).getLabel(); } else if (source instanceof Choice) { arg = + * ((Choice) source).getSelectedItem(); } else if (source instanceof + * List) { arg = ((List) source).getSelectedItem(); } else if + * (source instanceof MenuItem) { arg = ((MenuItem) + * source).getLabel(); } else if (source instanceof TextField) { arg + * = ((TextField) source).getText(); } + */ + return evt; + } + + /** + * Convert adjustment event. + * + * @param ae + * the ae. + * @return the event. + */ + Event convertAdjustmentEvent(AdjustmentEvent ae) { + // TODO: Event.SCROLL_BEGIN/SCROLL_END + return new Event(ae.source, ae.id + ae.getAdjustmentType() - 1, new Integer(ae + .getValue())); + } + + /** + * Convert component event. + * + * @param ce + * the ce. + * @return the event. + */ + Event convertComponentEvent(ComponentEvent ce) { + Component comp = ce.getComponent(); + Event evt = new Event(comp, Event.WINDOW_MOVED, null); + evt.x = comp.getX(); + evt.y = comp.getY(); + return evt; + } + + // ???AWT + /* + * Event convertItemEvent(ItemEvent ie) { int oldId = ie.id + + * ie.getStateChange() - 1; Object source = ie.source; int idx = -1; if + * (source instanceof List) { List list = (List) source; idx = + * list.getSelectedIndex(); } else if (source instanceof Choice) { + * Choice choice = (Choice) source; idx = choice.getSelectedIndex(); } + * Object arg = idx >= 0 ? new Integer(idx) : null; return new + * Event(source, oldId, arg); } + */ + + /** + * Convert key event. + * + * @param ke + * the ke. + * @return the event. + */ + Event convertKeyEvent(KeyEvent ke) { + int oldId = ke.id; + // leave only old Event's modifiers + + int mod = ke.getModifiers() & OLD_MOD_MASK; + Component comp = ke.getComponent(); + char keyChar = ke.getKeyChar(); + int keyCode = ke.getKeyCode(); + int key = convertKey(keyChar, keyCode); + if (key >= Event.HOME && key <= Event.INSERT) { + oldId += 2; // non-ASCII key -> action key + } + return new Event(comp, ke.getWhen(), oldId, 0, 0, key, mod); + } + + /** + * Convert mouse event. + * + * @param me + * the me. + * @return the event. + */ + Event convertMouseEvent(MouseEvent me) { + int id = me.id; + if (id != MouseEvent.MOUSE_CLICKED) { + Event evt = new Event(me.source, id, null); + evt.x = me.getX(); + evt.y = me.getY(); + int mod = me.getModifiers(); + // in Event modifiers mean button number for mouse events: + evt.modifiers = mod & (Event.ALT_MASK | Event.META_MASK); + if (id == MouseEvent.MOUSE_PRESSED) { + evt.clickCount = me.getClickCount(); + } + return evt; + } + return null; + } + + /** + * Convert key. + * + * @param keyChar + * the key char. + * @param keyCode + * the key code. + * @return the int. + */ + int convertKey(char keyChar, int keyCode) { + int key; + // F1 - F12 + if (keyCode >= KeyEvent.VK_F1 && keyCode <= KeyEvent.VK_F12) { + key = Event.F1 + keyCode - KeyEvent.VK_F1; + } else { + switch (keyCode) { + default: // non-action key + key = keyChar; + break; + // action keys: + case KeyEvent.VK_HOME: + key = Event.HOME; + break; + case KeyEvent.VK_END: + key = Event.END; + break; + case KeyEvent.VK_PAGE_UP: + key = Event.PGUP; + break; + case KeyEvent.VK_PAGE_DOWN: + key = Event.PGDN; + break; + case KeyEvent.VK_UP: + key = Event.UP; + break; + case KeyEvent.VK_DOWN: + key = Event.DOWN; + break; + case KeyEvent.VK_LEFT: + key = Event.LEFT; + break; + case KeyEvent.VK_RIGHT: + key = Event.RIGHT; + break; + case KeyEvent.VK_PRINTSCREEN: + key = Event.PRINT_SCREEN; + break; + case KeyEvent.VK_SCROLL_LOCK: + key = Event.SCROLL_LOCK; + break; + case KeyEvent.VK_CAPS_LOCK: + key = Event.CAPS_LOCK; + break; + case KeyEvent.VK_NUM_LOCK: + key = Event.NUM_LOCK; + break; + case KeyEvent.VK_PAUSE: + key = Event.PAUSE; + break; + case KeyEvent.VK_INSERT: + key = Event.INSERT; + break; + } + } + return key; + } + + } + +} diff --git a/awt/java/awt/AWTException.java b/awt/java/awt/AWTException.java new file mode 100644 index 000000000..6590b73d1 --- /dev/null +++ b/awt/java/awt/AWTException.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ + +package java.awt; + +/** + * The AWTException class is used to provide notification and information about + * AWT errors. + * + * @since Android 1.0 + */ +public class AWTException extends Exception { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -1900414231151323879L; + + /** + * Instantiates a new AWT exception with the specified message. + * + * @param msg + * the specific message for current exception. + */ + public AWTException(String msg) { + super(msg); + } + +} diff --git a/awt/java/awt/AWTKeyStroke.java b/awt/java/awt/AWTKeyStroke.java new file mode 100644 index 000000000..f01f6f00e --- /dev/null +++ b/awt/java/awt/AWTKeyStroke.java @@ -0,0 +1,712 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ + +package java.awt; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The AWTKeyStroke holds all of the information for the complete act of + * typing a character. This includes the events that are generated when + * the key is pressed, released, or typed (pressed and released generating + * a Unicode character result) which are associated with the event + * objects KeyEvent.KEY_PRESSED, KeyEvent.KEY_RELEASED, or KeyEvent.KEY_TYPED. + * It also holds information about which modifiers (such as control or + * shift) were used in conjunction with the keystroke. The following masks + * are available to identify the modifiers: + *
    + *
  • java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
  • + *
  • java.awt.event.InputEvent.ALT_DOWN_MASK
  • + *
  • java.awt.event.InputEvent.CTRL_DOWN_MASK
  • + *
  • java.awt.event.InputEvent.META_DOWN_MASK
  • + *
  • java.awt.event.InputEvent.SHIFT_DOWN_MASK
  • + *
  • java.awt.event.InputEvent.ALT_GRAPH_MASK
  • + *
  • java.awt.event.InputEvent.ALT_MASK
  • + *
  • java.awt.event.InputEvent.CTRL_MASK
  • + *
  • java.awt.event.InputEvent.META_MASK
  • + *
  • java.awt.event.InputEvent.SHIFT_MASK
  • + *
+ *
+ * The AWTKeyStroke is unique, and applications should not create their own + * instances of AWTKeyStroke. All applications should use getAWTKeyStroke + * methods for obtaining instances of AWTKeyStroke. + * + * @since Android 1.0 + */ +public class AWTKeyStroke implements Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -6430539691155161871L; + + /** + * The Constant cache. + */ + private static final Map cache = new HashMap(); // Map + + // < + // AWTKeyStroke + // , + // ? + // extends + // AWTKeyStroke + // > + + /** + * The Constant keyEventTypesMap. + */ + private static final Map keyEventTypesMap = new HashMap(); // Map + + // < + // int + // , + // String + // > + + private static Constructor subConstructor; + + static { + keyEventTypesMap.put(new Integer(KeyEvent.KEY_PRESSED), "pressed"); //$NON-NLS-1$ + keyEventTypesMap.put(new Integer(KeyEvent.KEY_RELEASED), "released"); //$NON-NLS-1$ + keyEventTypesMap.put(new Integer(KeyEvent.KEY_TYPED), "typed"); //$NON-NLS-1$ + } + + /** + * The key char. + */ + private char keyChar; + + /** + * The key code. + */ + private int keyCode; + + /** + * The modifiers. + */ + private int modifiers; + + /** + * The on key release. + */ + private boolean onKeyRelease; + + /** + * Instantiates a new AWTKeyStroke. getAWTKeyStroke method should be used by + * applications code. + * + * @param keyChar + * the key char. + * @param keyCode + * the key code. + * @param modifiers + * the modifiers. + * @param onKeyRelease + * true if AWTKeyStroke is for a key release, false otherwise. + */ + protected AWTKeyStroke(char keyChar, int keyCode, int modifiers, boolean onKeyRelease) { + setAWTKeyStroke(keyChar, keyCode, modifiers, onKeyRelease); + } + + /** + * Sets the AWT key stroke. + * + * @param keyChar + * the key char. + * @param keyCode + * the key code. + * @param modifiers + * the modifiers. + * @param onKeyRelease + * the on key release. + */ + private void setAWTKeyStroke(char keyChar, int keyCode, int modifiers, boolean onKeyRelease) { + this.keyChar = keyChar; + this.keyCode = keyCode; + this.modifiers = modifiers; + this.onKeyRelease = onKeyRelease; + } + + /** + * Instantiates a new AWTKeyStroke with default parameters: + * KeyEvent.CHAR_UNDEFINED key char, KeyEvent.VK_UNDEFINED key code, without + * modifiers and false key realized value. + */ + protected AWTKeyStroke() { + this(KeyEvent.CHAR_UNDEFINED, KeyEvent.VK_UNDEFINED, 0, false); + } + + /** + * Returns the unique number value for AWTKeyStroke object. + * + * @return the integer unique value of the AWTKeyStroke object. + */ + @Override + public int hashCode() { + return modifiers + (keyCode != KeyEvent.VK_UNDEFINED ? keyCode : keyChar) + + (onKeyRelease ? -1 : 0); + } + + /** + * Gets the set of modifiers for the AWTKeyStroke object. + * + * @return the integer value which contains modifiers. + */ + public final int getModifiers() { + return modifiers; + } + + /** + * Compares this AWTKeyStroke object to the specified object. + * + * @param anObject + * the specified AWTKeyStroke object to compare with this + * instance. + * @return true if objects are identical, false otherwise. + */ + @Override + public final boolean equals(Object anObject) { + if (anObject instanceof AWTKeyStroke) { + AWTKeyStroke key = (AWTKeyStroke)anObject; + return ((key.keyCode == keyCode) && (key.keyChar == keyChar) + && (key.modifiers == modifiers) && (key.onKeyRelease == onKeyRelease)); + } + return false; + } + + /** + * Returns the string representation of the AWTKeyStroke. This string should + * contain key stroke properties. + * + * @return the string representation of the AWTKeyStroke. + */ + @Override + public String toString() { + int type = getKeyEventType(); + return InputEvent.getModifiersExText(getModifiers()) + " " + //$NON-NLS-1$ + keyEventTypesMap.get(new Integer(type)) + " " + //$NON-NLS-1$ + (type == KeyEvent.KEY_TYPED ? new String(new char[] { + keyChar + }) : KeyEvent.getKeyText(keyCode)); + } + + /** + * Gets the key code for the AWTKeyStroke object. + * + * @return the key code for the AWTKeyStroke object. + */ + public final int getKeyCode() { + return keyCode; + } + + /** + * Gets the key character for the AWTKeyStroke object. + * + * @return the key character for the AWTKeyStroke object. + */ + public final char getKeyChar() { + return keyChar; + } + + /** + * Gets the AWT key stroke. + * + * @param keyChar + * the key char. + * @param keyCode + * the key code. + * @param modifiers + * the modifiers. + * @param onKeyRelease + * the on key release. + * @return the AWT key stroke. + */ + private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode, int modifiers, + boolean onKeyRelease) { + AWTKeyStroke key = newInstance(keyChar, keyCode, modifiers, onKeyRelease); + + AWTKeyStroke value = cache.get(key); + if (value == null) { + value = key; + cache.put(key, value); + } + return value; + } + + /** + * New instance. + * + * @param keyChar + * the key char. + * @param keyCode + * the key code. + * @param modifiers + * the modifiers. + * @param onKeyRelease + * the on key release. + * @return the AWT key stroke. + */ + private static AWTKeyStroke newInstance(char keyChar, int keyCode, int modifiers, + boolean onKeyRelease) { + AWTKeyStroke key; + // ???AWT + // if (subConstructor == null) { + key = new AWTKeyStroke(); + // ???AWT + // } else { + // try { + // key = (AWTKeyStroke) subConstructor.newInstance(); + // } catch (Exception e) { + // throw new RuntimeException(e); + // } + // } + int allModifiers = getAllModifiers(modifiers); + key.setAWTKeyStroke(keyChar, keyCode, allModifiers, onKeyRelease); + return key; + } + + /** + * Adds the mask. + * + * @param mod + * the mod. + * @param mask + * the mask. + * @return the int. + */ + private static int addMask(int mod, int mask) { + return ((mod & mask) != 0) ? (mod | mask) : mod; + } + + /** + * Return all (old & new) modifiers corresponding to. + * + * @param mod + * old or new modifiers. + * @return old and new modifiers together. + */ + static int getAllModifiers(int mod) { + int allMod = mod; + int shift = (InputEvent.SHIFT_MASK | InputEvent.SHIFT_DOWN_MASK); + int ctrl = (InputEvent.CTRL_MASK | InputEvent.CTRL_DOWN_MASK); + int meta = (InputEvent.META_MASK | InputEvent.META_DOWN_MASK); + int alt = (InputEvent.ALT_MASK | InputEvent.ALT_DOWN_MASK); + int altGr = (InputEvent.ALT_GRAPH_MASK | InputEvent.ALT_GRAPH_DOWN_MASK); + // button modifiers are not converted between old & new + + allMod = addMask(allMod, shift); + allMod = addMask(allMod, ctrl); + allMod = addMask(allMod, meta); + allMod = addMask(allMod, alt); + allMod = addMask(allMod, altGr); + + return allMod; + } + + /** + * Returns an instance of AWTKeyStroke for parsed string. The string must + * have the following syntax: + *

+ * <modifiers>* (<typedID> | <pressedReleasedID>) + *

+ * modifiers := shift | control | ctrl | meta | alt | altGraph
+ * typedID := typed
+ * typedKey := string of length 1 giving the Unicode character.
+ * pressedReleasedID := (pressed | released)
+ * key := KeyEvent key code name, i.e. the name following "VK_". + *

+ * + * @param s + * the String which contains key stroke parameters. + * @return the AWTKeyStroke for string. + * @throws IllegalArgumentException + * if string has incorrect format or null. + */ + public static AWTKeyStroke getAWTKeyStroke(String s) { + if (s == null) { + // awt.65=null argument + throw new IllegalArgumentException(Messages.getString("awt.65")); //$NON-NLS-1$ + } + + StringTokenizer tokenizer = new StringTokenizer(s); + + Boolean release = null; + int modifiers = 0; + int keyCode = KeyEvent.VK_UNDEFINED; + char keyChar = KeyEvent.CHAR_UNDEFINED; + boolean typed = false; + long modifier = 0; + String token = null; + do { + token = getNextToken(tokenizer); + modifier = parseModifier(token); + modifiers |= modifier; + } while (modifier > 0); + + typed = parseTypedID(token); + + if (typed) { + token = getNextToken(tokenizer); + keyChar = parseTypedKey(token); + + } + if (keyChar == KeyEvent.CHAR_UNDEFINED) { + release = parsePressedReleasedID(token); + if (release != null) { + token = getNextToken(tokenizer); + } + keyCode = parseKey(token); + } + if (tokenizer.hasMoreTokens()) { + // awt.66=Invalid format + throw new IllegalArgumentException(Messages.getString("awt.66")); //$NON-NLS-1$ + } + + return getAWTKeyStroke(keyChar, keyCode, modifiers, release == Boolean.TRUE); + } + + /** + * Gets the next token. + * + * @param tokenizer + * the tokenizer. + * @return the next token. + */ + private static String getNextToken(StringTokenizer tokenizer) { + try { + return tokenizer.nextToken(); + } catch (NoSuchElementException exception) { + // awt.66=Invalid format + throw new IllegalArgumentException(Messages.getString("awt.66")); //$NON-NLS-1$ + } + } + + /** + * Gets the key code. + * + * @param s + * the s. + * @return the key code. + */ + static int getKeyCode(String s) { + try { + Field vk = KeyEvent.class.getField("VK_" + s); //$NON-NLS-1$ + return vk.getInt(null); + } catch (Exception e) { + if (s.length() != 1) { + // awt.66=Invalid format + throw new IllegalArgumentException(Messages.getString("awt.66")); //$NON-NLS-1$ + } + return KeyEvent.VK_UNDEFINED; + } + } + + /** + * Gets an instance of the AWTKeyStroke for specified character. + * + * @param keyChar + * the keyboard character value. + * @return a AWTKeyStroke for specified character. + */ + public static AWTKeyStroke getAWTKeyStroke(char keyChar) { + return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false); + } + + /** + * Returns an instance of AWTKeyStroke for a given key code, set of + * modifiers, and specified key released flag value. The key codes are + * defined in java.awt.event.KeyEvent class. The set of modifiers is given + * as a bitwise combination of masks taken from the following list: + *

    + *
  • java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
  • + * java.awt.event.InputEvent.ALT_DOWN_MASK
  • + * java.awt.event.InputEvent.CTRL_DOWN_MASK
  • + * java.awt.event.InputEvent.META_DOWN_MASK
  • + * java.awt.event.InputEvent.SHIFT_DOWN_MASK
  • + * java.awt.event.InputEvent.ALT_GRAPH_MASK
  • + * java.awt.event.InputEvent.ALT_MASK
  • + * java.awt.event.InputEvent.CTRL_MASK
  • + * java.awt.event.InputEvent.META_MASK
  • + * java.awt.event.InputEvent.SHIFT_MASK
  • + *
+ *
+ * + * @param keyCode + * the specified key code of keyboard. + * @param modifiers + * the bit set of modifiers. + * @param onKeyRelease + * the value which represents whether this AWTKeyStroke shall + * represents a key release. + * @return the AWTKeyStroke. + */ + public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers, boolean onKeyRelease) { + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers, onKeyRelease); + } + + /** + * Returns AWTKeyStroke for a specified character and set of modifiers. The + * set of modifiers is given as a bitwise combination of masks taken from + * the following list: + *
    + *
  • java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
  • + * java.awt.event.InputEvent.ALT_DOWN_MASK
  • + * java.awt.event.InputEvent.CTRL_DOWN_MASK
  • + * java.awt.event.InputEvent.META_DOWN_MASK
  • + * java.awt.event.InputEvent.SHIFT_DOWN_MASK
  • + * java.awt.event.InputEvent.ALT_GRAPH_MASK
  • + * java.awt.event.InputEvent.ALT_MASK
  • + * java.awt.event.InputEvent.CTRL_MASK
  • + * java.awt.event.InputEvent.META_MASK
  • + * java.awt.event.InputEvent.SHIFT_MASK
  • + *
+ * + * @param keyChar + * the Character object which represents keyboard character + * value. + * @param modifiers + * the bit set of modifiers. + * @return the AWTKeyStroke object. + * @throws IllegalArgumentException + * if keyChar value is null. + */ + public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers) { + if (keyChar == null) { + // awt.01='{0}' parameter is null + throw new IllegalArgumentException(Messages.getString("awt.01", "keyChar")); //$NON-NLS-1$ //$NON-NLS-2$ + } + return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED, modifiers, false); + } + + /** + * Returns an instance of AWTKeyStroke for a specified key code and set of + * modifiers. The key codes are defined in java.awt.event.KeyEvent class. + * The set of modifiers is given as a bitwise combination of masks taken + * from the following list: + *
    + *
  • java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
  • + * java.awt.event.InputEvent.ALT_DOWN_MASK
  • + * java.awt.event.InputEvent.CTRL_DOWN_MASK
  • + * java.awt.event.InputEvent.META_DOWN_MASK
  • + * java.awt.event.InputEvent.SHIFT_DOWN_MASK
  • + * java.awt.event.InputEvent.ALT_GRAPH_MASK
  • + * java.awt.event.InputEvent.ALT_MASK
  • + * java.awt.event.InputEvent.CTRL_MASK
  • + * java.awt.event.InputEvent.META_MASK
  • + * java.awt.event.InputEvent.SHIFT_MASK
  • + *
+ * + * @param keyCode + * the specified key code of keyboard. + * @param modifiers + * the bit set of modifiers. + * @return the AWTKeyStroke. + */ + public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) { + return getAWTKeyStroke(keyCode, modifiers, false); + } + + /** + * Gets the AWTKeyStroke for a key event. This method obtains the key char + * and key code from the specified key event. + * + * @param anEvent + * the key event which identifies the desired AWTKeyStroke. + * @return the AWTKeyStroke for the key event. + */ + public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent anEvent) { + int id = anEvent.getID(); + char undef = KeyEvent.CHAR_UNDEFINED; + char keyChar = (id == KeyEvent.KEY_TYPED ? anEvent.getKeyChar() : undef); + int keyCode = (keyChar == undef ? anEvent.getKeyCode() : KeyEvent.VK_UNDEFINED); + return getAWTKeyStroke(keyChar, keyCode, anEvent.getModifiersEx(), + id == KeyEvent.KEY_RELEASED); + } + + /** + * Gets the key event type for the AWTKeyStroke object. + * + * @return the key event type: KeyEvent.KEY_PRESSED, KeyEvent.KEY_TYPED, or + * KeyEvent.KEY_RELEASED. + */ + public final int getKeyEventType() { + if (keyCode == KeyEvent.VK_UNDEFINED) { + return KeyEvent.KEY_TYPED; + } + return (onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED); + } + + /** + * Returns true if the key event is associated with the AWTKeyStroke is + * KEY_RELEASED, false otherwise. + * + * @return true, if if the key event associated with the AWTKeyStroke is + * KEY_RELEASED, false otherwise. + */ + public final boolean isOnKeyRelease() { + return onKeyRelease; + } + + /** + * Read resolve. + * + * @return the object. + * @throws ObjectStreamException + * the object stream exception. + */ + protected Object readResolve() throws ObjectStreamException { + return getAWTKeyStroke(this.keyChar, this.keyCode, this.modifiers, this.onKeyRelease); + } + + /** + * Register subclass. + * + * @param subclass + * the subclass. + */ + protected static void registerSubclass(Class subclass) { + // ???AWT + /* + * if (subclass == null) { // awt.01='{0}' parameter is null throw new + * IllegalArgumentException(Messages.getString("awt.01", "subclass")); + * //$NON-NLS-1$ //$NON-NLS-2$ } if (! + * AWTKeyStroke.class.isAssignableFrom(subclass)) { // awt.67=subclass + * is not derived from AWTKeyStroke throw new + * ClassCastException(Messages.getString("awt.67")); //$NON-NLS-1$ } try + * { subConstructor = subclass.getDeclaredConstructor(); + * subConstructor.setAccessible(true); } catch (SecurityException e) { + * throw new RuntimeException(e); } catch (NoSuchMethodException e) { // + * awt.68=subclass could not be instantiated throw new + * IllegalArgumentException(Messages.getString("awt.68")); //$NON-NLS-1$ + * } cache.clear(); //flush the cache + */ + } + + /** + * Parses the modifier. + * + * @param strMod + * the str mod. + * @return the long. + */ + private static long parseModifier(String strMod) { + long modifiers = 0l; + if (strMod.equals("shift")) { //$NON-NLS-1$ + modifiers |= InputEvent.SHIFT_DOWN_MASK; + } else if (strMod.equals("control") || strMod.equals("ctrl")) { //$NON-NLS-1$ //$NON-NLS-2$ + modifiers |= InputEvent.CTRL_DOWN_MASK; + } else if (strMod.equals("meta")) { //$NON-NLS-1$ + modifiers |= InputEvent.META_DOWN_MASK; + } else if (strMod.equals("alt")) { //$NON-NLS-1$ + modifiers |= InputEvent.ALT_DOWN_MASK; + } else if (strMod.equals("altGraph")) { //$NON-NLS-1$ + modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK; + } else if (strMod.equals("button1")) { //$NON-NLS-1$ + modifiers |= InputEvent.BUTTON1_DOWN_MASK; + } else if (strMod.equals("button2")) { //$NON-NLS-1$ + modifiers |= InputEvent.BUTTON2_DOWN_MASK; + } else if (strMod.equals("button3")) { //$NON-NLS-1$ + modifiers |= InputEvent.BUTTON3_DOWN_MASK; + } + return modifiers; + } + + /** + * Parses the typed id. + * + * @param strTyped + * the str typed. + * @return true, if successful. + */ + private static boolean parseTypedID(String strTyped) { + if (strTyped.equals("typed")) { //$NON-NLS-1$ + return true; + } + + return false; + } + + /** + * Parses the typed key. + * + * @param strChar + * the str char. + * @return the char. + */ + private static char parseTypedKey(String strChar) { + char keyChar = KeyEvent.CHAR_UNDEFINED; + + if (strChar.length() != 1) { + // awt.66=Invalid format + throw new IllegalArgumentException(Messages.getString("awt.66")); //$NON-NLS-1$ + } + keyChar = strChar.charAt(0); + return keyChar; + } + + /** + * Parses the pressed released id. + * + * @param str + * the str. + * @return the boolean. + */ + private static Boolean parsePressedReleasedID(String str) { + + if (str.equals("pressed")) { //$NON-NLS-1$ + return Boolean.FALSE; + } else if (str.equals("released")) { //$NON-NLS-1$ + return Boolean.TRUE; + } + return null; + } + + /** + * Parses the key. + * + * @param strCode + * the str code. + * @return the int. + */ + private static int parseKey(String strCode) { + int keyCode = KeyEvent.VK_UNDEFINED; + + keyCode = getKeyCode(strCode); + + if (keyCode == KeyEvent.VK_UNDEFINED) { + // awt.66=Invalid format + throw new IllegalArgumentException(Messages.getString("awt.66")); //$NON-NLS-1$ + } + return keyCode; + } +} diff --git a/awt/java/awt/AWTListenerList.java b/awt/java/awt/AWTListenerList.java new file mode 100644 index 000000000..3327d63b5 --- /dev/null +++ b/awt/java/awt/AWTListenerList.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.awt; + +import java.util.EventListener; + +import org.apache.harmony.awt.ListenerList; + +final class AWTListenerList extends ListenerList { + private static final long serialVersionUID = -2622077171532840953L; + + private final Component owner; + + AWTListenerList() { + super(); + this.owner = null; + } + + AWTListenerList(Component owner) { + super(); + this.owner = owner; + } + + @Override + public void addUserListener(T listener) { + super.addUserListener(listener); + + if (owner != null) { + owner.deprecatedEventHandler = false; + } + } +} diff --git a/awt/java/awt/AWTPermission.java b/awt/java/awt/AWTPermission.java new file mode 100644 index 000000000..4bd835777 --- /dev/null +++ b/awt/java/awt/AWTPermission.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ + +package java.awt; + +import java.security.BasicPermission; + +/** + * The AWTPermission specifies the name of the permission and the corresponding + * action list. + * + * @since Android 1.0 + */ +public final class AWTPermission extends BasicPermission { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 8890392402588814465L; + + /** + * Instantiates a new AWTPermission with defined name and actions. + * + * @param name + * the name of a new AWTPermission. + * @param actions + * the actions of a new AWTPermission. + */ + public AWTPermission(String name, String actions) { + super(name, actions); + } + + /** + * Instantiates a new AWT permission with the defined name. + * + * @param name + * the name of a new AWTPermission. + */ + public AWTPermission(String name) { + super(name); + } + +} diff --git a/awt/java/awt/ActiveEvent.java b/awt/java/awt/ActiveEvent.java new file mode 100644 index 000000000..704462390 --- /dev/null +++ b/awt/java/awt/ActiveEvent.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ + +package java.awt; + +/** + * This interface defines events that know how to dispatch themselves. Such + * event can be placed upon the event queue and its dispatch method will be + * called when the event is dispatched. + * + * @since Android 1.0 + */ +public interface ActiveEvent { + + /** + * Dispatches the event to the listeners of the event's source, or does + * whatever it is this event is supposed to do. + */ + public void dispatch(); + +} diff --git a/awt/java/awt/Adjustable.java b/awt/java/awt/Adjustable.java new file mode 100644 index 000000000..baf80f7c7 --- /dev/null +++ b/awt/java/awt/Adjustable.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ + +package java.awt; + +import java.awt.event.AdjustmentListener; + +/** + * The Adjustable interface represents an adjustable numeric value contained + * within a bounded range of values, such as the current location in scrollable + * region or the value of a gauge. + * + * @since Android 1.0 + */ +public interface Adjustable { + + /** + * The Constant HORIZONTAL indicates that the Adjustable's orientation is + * horizontal. + */ + public static final int HORIZONTAL = 0; + + /** + * The Constant VERTICAL indicates that the Adjustable's orientation is + * vertical. + */ + public static final int VERTICAL = 1; + + /** + * The Constant NO_ORIENTATION indicates that the Adjustable has no + * orientation. + */ + public static final int NO_ORIENTATION = 2; + + /** + * Gets the value of the Adjustable. + * + * @return the current value of the Adjustable. + */ + public int getValue(); + + /** + * Sets the value to the Adjustable object. + * + * @param a0 + * the new value of the Adjustable object. + */ + public void setValue(int a0); + + /** + * Adds the AdjustmentListener to current Adjustment. + * + * @param a0 + * the AdjustmentListener object. + */ + public void addAdjustmentListener(AdjustmentListener a0); + + /** + * Gets the block increment of the Adjustable. + * + * @return the block increment of the Adjustable. + */ + public int getBlockIncrement(); + + /** + * Gets the maximum value of the Adjustable. + * + * @return the maximum value of the Adjustable. + */ + public int getMaximum(); + + /** + * Gets the minimum value of the Adjustable. + * + * @return the minimum value of the Adjustable. + */ + public int getMinimum(); + + /** + * Gets the orientation of the Adjustable. + * + * @return the orientation of the Adjustable. + */ + public int getOrientation(); + + /** + * Gets the unit increment of the Adjustable. + * + * @return the unit increment of the Adjustable. + */ + public int getUnitIncrement(); + + /** + * Gets the visible amount of the Adjustable. + * + * @return the visible amount of the Adjustable. + */ + public int getVisibleAmount(); + + /** + * Removes the adjustment listener of the Adjustable. + * + * @param a0 + * the specified AdjustmentListener to be removed. + */ + public void removeAdjustmentListener(AdjustmentListener a0); + + /** + * Sets the block increment for the Adjustable. + * + * @param a0 + * the new block increment. + */ + public void setBlockIncrement(int a0); + + /** + * Sets the maximum value of the Adjustable. + * + * @param a0 + * the new maximum of the Adjustable. + */ + public void setMaximum(int a0); + + /** + * Sets the minimum value of the Adjustable. + * + * @param a0 + * the new minimum of the Adjustable. + */ + public void setMinimum(int a0); + + /** + * Sets the unit increment of the Adjustable. + * + * @param a0 + * the new unit increment of the Adjustable. + */ + public void setUnitIncrement(int a0); + + /** + * Sets the visible amount of the Adjustable. + * + * @param a0 + * the new visible amount of the Adjustable. + */ + public void setVisibleAmount(int a0); + +} diff --git a/awt/java/awt/AlphaComposite.java b/awt/java/awt/AlphaComposite.java new file mode 100644 index 000000000..8389eb462 --- /dev/null +++ b/awt/java/awt/AlphaComposite.java @@ -0,0 +1,352 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt; + +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.RenderingHints; +import java.awt.image.ColorModel; + +import org.apache.harmony.awt.gl.ICompositeContext; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The AlphaComposite class defines a basic alpha compositing rules for + * combining source and destination colors to achieve blending and transparency + * effects with graphics and images. + * + * @since Android 1.0 + */ +public final class AlphaComposite implements Composite { + + /** + * The Constant CLEAR indicates that both the color and the alpha of the + * destination are cleared (Porter-Duff Clear rule). + */ + public static final int CLEAR = 1; + + /** + * The Constant SRC indicates that the source is copied to the destination + * (Porter-Duff Source rule). + */ + public static final int SRC = 2; + + /** + * The Constant DST indicates that the destination is left untouched + * (Porter-Duff Destination rule). + */ + public static final int DST = 9; + + /** + * The Constant SRC_OVER indicates that the source is composited over the + * destination (Porter-Duff Source Over Destination rule). + */ + public static final int SRC_OVER = 3; + + /** + * The Constant DST_OVER indicates that The destination is composited over + * the source and the result replaces the destination (Porter-Duff + * Destination Over Source rule). + */ + public static final int DST_OVER = 4; + + /** + * The Constant SRC_IN indicates that the part of the source lying inside of + * the destination replaces the destination (Porter-Duff Source In + * Destination rule). + */ + public static final int SRC_IN = 5; + + /** + * The Constant DST_IN indicates that the part of the destination lying + * inside of the source replaces the destination (Porter-Duff Destination In + * Source rule). + */ + public static final int DST_IN = 6; + + /** + * The Constant SRC_OUT indicates that the part of the source lying outside + * of the destination replaces the destination (Porter-Duff Source Held Out + * By Destination rule). + */ + public static final int SRC_OUT = 7; + + /** + * The Constant DST_OUT indicates that the part of the destination lying + * outside of the source replaces the destination (Porter-Duff Destination + * Held Out By Source rule). + */ + public static final int DST_OUT = 8; + + /** + * The Constant SRC_ATOP indicates that the part of the source lying inside + * of the destination is composited onto the destination (Porter-Duff Source + * Atop Destination rule). + */ + public static final int SRC_ATOP = 10; + + /** + * The Constant DST_ATOP indicates that the part of the destination lying + * inside of the source is composited over the source and replaces the + * destination (Porter-Duff Destination Atop Source rule). + */ + public static final int DST_ATOP = 11; + + /** + * The Constant XOR indicates that the part of the source that lies outside + * of the destination is combined with the part of the destination that lies + * outside of the source (Porter-Duff Source Xor Destination rule). + */ + public static final int XOR = 12; + + /** + * AlphaComposite object with the opaque CLEAR rule and an alpha of 1.0f. + */ + public static final AlphaComposite Clear = new AlphaComposite(CLEAR); + + /** + * AlphaComposite object with the opaque SRC rule and an alpha of 1.0f. + */ + public static final AlphaComposite Src = new AlphaComposite(SRC); + + /** + * AlphaComposite object with the opaque DST rule and an alpha of 1.0f. + */ + public static final AlphaComposite Dst = new AlphaComposite(DST); + + /** + * AlphaComposite object with the opaque SRC_OVER rule and an alpha of 1.0f. + */ + public static final AlphaComposite SrcOver = new AlphaComposite(SRC_OVER); + + /** + * AlphaComposite object with the opaque DST_OVER rule and an alpha of 1.0f. + */ + public static final AlphaComposite DstOver = new AlphaComposite(DST_OVER); + + /** + * AlphaComposite object with the opaque SRC_IN rule and an alpha of 1.0f. + */ + public static final AlphaComposite SrcIn = new AlphaComposite(SRC_IN); + + /** + * AlphaComposite object with the opaque DST_IN rule and an alpha of 1.0f. + */ + public static final AlphaComposite DstIn = new AlphaComposite(DST_IN); + + /** + * AlphaComposite object with the opaque SRC_OUT rule and an alpha of 1.0f. + */ + public static final AlphaComposite SrcOut = new AlphaComposite(SRC_OUT); + + /** + * AlphaComposite object with the opaque DST_OUT rule and an alpha of 1.0f. + */ + public static final AlphaComposite DstOut = new AlphaComposite(DST_OUT); + + /** + * AlphaComposite object with the opaque SRC_ATOP rule and an alpha of 1.0f. + */ + public static final AlphaComposite SrcAtop = new AlphaComposite(SRC_ATOP); + + /** + * AlphaComposite object with the opaque DST_ATOP rule and an alpha of 1.0f. + */ + public static final AlphaComposite DstAtop = new AlphaComposite(DST_ATOP); + + /** + * AlphaComposite object with the opaque XOR rule and an alpha of 1.0f. + */ + public static final AlphaComposite Xor = new AlphaComposite(XOR); + + /** + * The rule. + */ + private int rule; + + /** + * The alpha. + */ + private float alpha; + + /** + * Instantiates a new alpha composite. Creates a context for the compositing + * operation. The context contains state that is used in performing the + * compositing operation. + * + * @param rule + * the rule. + * @param alpha + * the alpha. + */ + private AlphaComposite(int rule, float alpha) { + if (rule < CLEAR || rule > XOR) { + // awt.11D=Unknown rule + throw new IllegalArgumentException(Messages.getString("awt.11D")); //$NON-NLS-1$ + } + if (alpha < 0.0f || alpha > 1.0f) { + // awt.11E=Wrong alpha value + throw new IllegalArgumentException(Messages.getString("awt.11E")); //$NON-NLS-1$ + } + + this.rule = rule; + this.alpha = alpha; + } + + /** + * Instantiates a new alpha composite. + * + * @param rule + * the rule. + */ + private AlphaComposite(int rule) { + this(rule, 1.0f); + } + + /** + * Creates a CompositeContext object with the specified source ColorModel, + * destination ColorModel and RenderingHints parameters for a composing + * operation. + * + * @param srcColorModel + * the source's ColorModel. + * @param dstColorModel + * the destination's ColorModel. + * @param hints + * the RenderingHints object. + * @return the CompositeContext object. + * @see java.awt.Composite#createContext(java.awt.image.ColorModel, + * java.awt.image.ColorModel, java.awt.RenderingHints) + */ + public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, + RenderingHints hints) { + return new ICompositeContext(this, srcColorModel, dstColorModel); + } + + /** + * Compares the AlphaComposite object with the specified object. + * + * @param obj + * the Object to be compared. + * @return true, if the AlphaComposite object is equal to the specified + * object. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AlphaComposite)) { + return false; + } + AlphaComposite other = (AlphaComposite)obj; + return (this.rule == other.getRule() && this.alpha == other.getAlpha()); + } + + /** + * Returns the hash code of the AlphaComposite object. + * + * @return the hash code of the AlphaComposite object. + */ + @Override + public int hashCode() { + int hash = Float.floatToIntBits(alpha); + int tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= rule; + return hash; + } + + /** + * Gets the compositing rule of this AlphaComposite object. + * + * @return the compositing rule of this AlphaComposite object. + */ + public int getRule() { + return rule; + } + + /** + * Gets the alpha value of this AlphaComposite object; returns 1.0 if this + * AlphaComposite object doesn't have alpha value. + * + * @return the alpha value of this AlphaComposite object or 1.0 if this + * AlphaComposite object doesn't have alpha value. + */ + public float getAlpha() { + return alpha; + } + + /** + * Gets the AlphaComposite instance with the specified rule and alpha value. + * + * @param rule + * the compositing rule. + * @param alpha + * the alpha value. + * @return the AlphaComposite instance. + */ + public static AlphaComposite getInstance(int rule, float alpha) { + if (alpha == 1.0f) { + return getInstance(rule); + } + return new AlphaComposite(rule, alpha); + } + + /** + * Gets the AlphaComposite instance with the specified rule. + * + * @param rule + * the compositing rule. + * @return the AlphaComposite instance. + */ + public static AlphaComposite getInstance(int rule) { + switch (rule) { + case CLEAR: + return Clear; + case SRC: + return Src; + case DST: + return Dst; + case SRC_OVER: + return SrcOver; + case DST_OVER: + return DstOver; + case SRC_IN: + return SrcIn; + case DST_IN: + return DstIn; + case SRC_OUT: + return SrcOut; + case DST_OUT: + return DstOut; + case SRC_ATOP: + return SrcAtop; + case DST_ATOP: + return DstAtop; + case XOR: + return Xor; + default: + // awt.11D=Unknown rule + throw new IllegalArgumentException(Messages.getString("awt.11D")); //$NON-NLS-1$ + } + } + +} diff --git a/awt/java/awt/BasicStroke.java b/awt/java/awt/BasicStroke.java new file mode 100644 index 000000000..245781570 --- /dev/null +++ b/awt/java/awt/BasicStroke.java @@ -0,0 +1,2443 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt; + +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; + +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.misc.HashCode; + +/** + * The BasicStroke class specifies a set of rendering attributes for the + * outlines of graphics primitives. The BasicStroke attributes describe the + * shape of the pen which draws the outline of a Shape and the decorations + * applied at the ends and joins of path segments of the Shape. The BasicStroke + * has the following rendering attributes: + *

+ *

    + *
  • line width -the pen width which draws the outlines.
  • + *
  • end caps - indicates the decoration applied to the ends of unclosed + * subpaths and dash segments. The BasicStroke defines three different + * decorations: CAP_BUTT, CAP_ROUND, and CAP_SQUARE.
  • + *
  • line joins - indicates the decoration applied at the intersection of two + * path segments and at the intersection of the endpoints of a subpath. The + * BasicStroke defines three decorations: JOIN_BEVEL, JOIN_MITER, and + * JOIN_ROUND.
  • + *
  • miter limit - the limit to trim a line join that has a JOIN_MITER + * decoration.
  • + *
  • dash attributes - the definition of how to make a dash pattern by + * alternating between opaque and transparent sections
  • + *
+ *

+ * + * @since Android 1.0 + */ +public class BasicStroke implements Stroke { + + /** + * The Constant CAP_BUTT indicates the ends of unclosed subpaths and dash + * segments have no added decoration. + */ + public static final int CAP_BUTT = 0; + + /** + * The Constant CAP_ROUND indicates the ends of unclosed subpaths and dash + * segments have a round decoration. + */ + public static final int CAP_ROUND = 1; + + /** + * The Constant CAP_SQUARE indicates the ends of unclosed subpaths and dash + * segments have a square projection. + */ + public static final int CAP_SQUARE = 2; + + /** + * The Constant JOIN_MITER indicates that path segments are joined by + * extending their outside edges until they meet. + */ + public static final int JOIN_MITER = 0; + + /** + * The Constant JOIN_ROUND indicates that path segments are joined by + * rounding off the corner at a radius of half the line width. + */ + public static final int JOIN_ROUND = 1; + + /** + * The Constant JOIN_BEVEL indicates that path segments are joined by + * connecting the outer corners of their wide outlines with a straight + * segment. + */ + public static final int JOIN_BEVEL = 2; + + /** + * Constants for calculating. + */ + static final int MAX_LEVEL = 20; // Maximal deepness of curve subdivision + + /** + * The Constant CURVE_DELTA. + */ + static final double CURVE_DELTA = 2.0; // Width tolerance + + /** + * The Constant CORNER_ANGLE. + */ + static final double CORNER_ANGLE = 4.0; // Minimum corner angle + + /** + * The Constant CORNER_ZERO. + */ + static final double CORNER_ZERO = 0.01; // Zero angle + + /** + * The Constant CUBIC_ARC. + */ + static final double CUBIC_ARC = 4.0 / 3.0 * (Math.sqrt(2.0) - 1); + + /** + * Stroke width. + */ + float width; + + /** + * Stroke cap type. + */ + int cap; + + /** + * Stroke join type. + */ + int join; + + /** + * Stroke miter limit. + */ + float miterLimit; + + /** + * Stroke dashes array. + */ + float dash[]; + + /** + * Stroke dash phase. + */ + float dashPhase; + + /** + * The temporary pre-calculated values. + */ + double curveDelta; + + /** + * The corner delta. + */ + double cornerDelta; + + /** + * The zero delta. + */ + double zeroDelta; + + /** + * The w2. + */ + double w2; + + /** + * The fmy. + */ + double fmx, fmy; + + /** + * The smy. + */ + double scx, scy, smx, smy; + + /** + * The cy. + */ + double mx, my, cx, cy; + + /** + * The temporary indicators. + */ + boolean isMove; + + /** + * The is first. + */ + boolean isFirst; + + /** + * The check move. + */ + boolean checkMove; + + /** + * The temporary and destination work paths. + */ + BufferedPath dst, lp, rp, sp; + + /** + * Stroke dasher class. + */ + Dasher dasher; + + /** + * Instantiates a new BasicStroke with default width, cap, join, limit, dash + * attributes parameters. The default parameters are a solid line of width + * 1.0, CAP_SQUARE, JOIN_MITER, a miter limit of 10.0, null dash attributes, + * and a dash phase of 0.0f. + */ + public BasicStroke() { + this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f); + } + + /** + * Instantiates a new BasicStroke with the specified width, caps, joins, + * limit, dash attributes, dash phase parameters. + * + * @param width + * the width of BasikStroke. + * @param cap + * the end decoration of BasikStroke. + * @param join + * the join segments decoration. + * @param miterLimit + * the limit to trim the miter join. + * @param dash + * the array with the dashing pattern. + * @param dashPhase + * the offset to start the dashing pattern. + */ + public BasicStroke(float width, int cap, int join, float miterLimit, float[] dash, + float dashPhase) { + if (width < 0.0f) { + // awt.133=Negative width + throw new IllegalArgumentException(Messages.getString("awt.133")); //$NON-NLS-1$ + } + if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) { + // awt.134=Illegal cap + throw new IllegalArgumentException(Messages.getString("awt.134")); //$NON-NLS-1$ + } + if (join != JOIN_MITER && join != JOIN_ROUND && join != JOIN_BEVEL) { + // awt.135=Illegal join + throw new IllegalArgumentException(Messages.getString("awt.135")); //$NON-NLS-1$ + } + if (join == JOIN_MITER && miterLimit < 1.0f) { + // awt.136=miterLimit less than 1.0f + throw new IllegalArgumentException(Messages.getString("awt.136")); //$NON-NLS-1$ + } + if (dash != null) { + if (dashPhase < 0.0f) { + // awt.137=Negative dashPhase + throw new IllegalArgumentException(Messages.getString("awt.137")); //$NON-NLS-1$ + } + if (dash.length == 0) { + // awt.138=Zero dash length + throw new IllegalArgumentException(Messages.getString("awt.138")); //$NON-NLS-1$ + } + ZERO: { + for (int i = 0; i < dash.length; i++) { + if (dash[i] < 0.0) { + // awt.139=Negative dash[{0}] + throw new IllegalArgumentException(Messages.getString("awt.139", i)); //$NON-NLS-1$ + } + if (dash[i] > 0.0) { + break ZERO; + } + } + // awt.13A=All dash lengths zero + throw new IllegalArgumentException(Messages.getString("awt.13A")); //$NON-NLS-1$ + } + } + this.width = width; + this.cap = cap; + this.join = join; + this.miterLimit = miterLimit; + this.dash = dash; + this.dashPhase = dashPhase; + } + + /** + * Instantiates a new BasicStroke with specified width, cap, join, limit and + * default dash attributes parameters. + * + * @param width + * the width of BasikStroke. + * @param cap + * the end decoration of BasikStroke. + * @param join + * the join segments decoration. + * @param miterLimit + * the limit to trim the miter join. + */ + public BasicStroke(float width, int cap, int join, float miterLimit) { + this(width, cap, join, miterLimit, null, 0.0f); + } + + /** + * Instantiates a new BasicStroke with specified width, cap, join and + * default limit and dash attributes parameters. + * + * @param width + * the width of BasikStroke. + * @param cap + * the end decoration of BasikStroke. + * @param join + * the join segments decoration. + */ + public BasicStroke(float width, int cap, int join) { + this(width, cap, join, 10.0f, null, 0.0f); + } + + /** + * Instantiates a new BasicStroke with specified width and default cap, + * join, limit, dash attributes parameters. + * + * @param width + * the width of BasicStroke. + */ + public BasicStroke(float width) { + this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f); + } + + /** + * Gets the line width of the BasicStroke. + * + * @return the line width of the BasicStroke. + */ + public float getLineWidth() { + return width; + } + + /** + * Gets the end cap style of the BasicStroke. + * + * @return the end cap style of the BasicStroke. + */ + public int getEndCap() { + return cap; + } + + /** + * Gets the line join style of the BasicStroke. + * + * @return the line join style of the BasicStroke. + */ + public int getLineJoin() { + return join; + } + + /** + * Gets the miter limit of the BasicStroke (the limit to trim the miter + * join). + * + * @return the miter limit of the BasicStroke. + */ + public float getMiterLimit() { + return miterLimit; + } + + /** + * Gets the dash attributes array of the BasicStroke. + * + * @return the dash attributes array of the BasicStroke. + */ + public float[] getDashArray() { + return dash; + } + + /** + * Gets the dash phase of the BasicStroke. + * + * @return the dash phase of the BasicStroke. + */ + public float getDashPhase() { + return dashPhase; + } + + /** + * Returns hash code of this BasicStroke. + * + * @return the hash code of this BasicStroke. + */ + @Override + public int hashCode() { + HashCode hash = new HashCode(); + hash.append(width); + hash.append(cap); + hash.append(join); + hash.append(miterLimit); + if (dash != null) { + hash.append(dashPhase); + for (float element : dash) { + hash.append(element); + } + } + return hash.hashCode(); + } + + /** + * Compares this BasicStroke object with the specified Object. + * + * @param obj + * the Object to be compared. + * @return true, if the Object is a BasicStroke with the same data values as + * this BasicStroke; false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof BasicStroke) { + BasicStroke bs = (BasicStroke)obj; + return bs.width == width && bs.cap == cap && bs.join == join + && bs.miterLimit == miterLimit && bs.dashPhase == dashPhase + && java.util.Arrays.equals(bs.dash, dash); + } + return false; + } + + /** + * Calculates allowable curve derivation. + * + * @param width + * the width. + * @return the curve delta. + */ + double getCurveDelta(double width) { + double a = width + CURVE_DELTA; + double cos = 1.0 - 2.0 * width * width / (a * a); + double sin = Math.sqrt(1.0 - cos * cos); + return Math.abs(sin / cos); + } + + /** + * Calculates the value to detect a small angle. + * + * @param width + * the width. + * @return the corner delta. + */ + double getCornerDelta(double width) { + return width * width * Math.sin(Math.PI * CORNER_ANGLE / 180.0); + } + + /** + * Calculates value to detect a zero angle. + * + * @param width + * the width. + * @return the zero delta. + */ + double getZeroDelta(double width) { + return width * width * Math.sin(Math.PI * CORNER_ZERO / 180.0); + } + + /** + * Creates a Shape from the outline of the specified shape drawn with this + * BasicStroke. + * + * @param s + * the specified Shape to be stroked. + * @return the Shape of the stroked outline. + * @see java.awt.Stroke#createStrokedShape(java.awt.Shape) + */ + public Shape createStrokedShape(Shape s) { + w2 = width / 2.0; + curveDelta = getCurveDelta(w2); + cornerDelta = getCornerDelta(w2); + zeroDelta = getZeroDelta(w2); + + dst = new BufferedPath(); + lp = new BufferedPath(); + rp = new BufferedPath(); + + if (dash == null) { + createSolidShape(s.getPathIterator(null)); + } else { + createDashedShape(s.getPathIterator(null)); + } + + return dst.createGeneralPath(); + } + + /** + * Generates a shape with a solid (not dashed) outline. + * + * @param p + * the PathIterator of source shape. + */ + void createSolidShape(PathIterator p) { + double coords[] = new double[6]; + mx = my = cx = cy = 0.0; + isMove = false; + isFirst = false; + checkMove = true; + boolean isClosed = true; + + while (!p.isDone()) { + switch (p.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + if (!isClosed) { + closeSolidShape(); + } + rp.clean(); + mx = cx = coords[0]; + my = cy = coords[1]; + isMove = true; + isClosed = false; + break; + case PathIterator.SEG_LINETO: + addLine(cx, cy, cx = coords[0], cy = coords[1], true); + break; + case PathIterator.SEG_QUADTO: + addQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3]); + break; + case PathIterator.SEG_CUBICTO: + addCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4], + cy = coords[5]); + break; + case PathIterator.SEG_CLOSE: + addLine(cx, cy, mx, my, false); + addJoin(lp, mx, my, lp.xMove, lp.yMove, true); + addJoin(rp, mx, my, rp.xMove, rp.yMove, false); + lp.closePath(); + rp.closePath(); + lp.appendReverse(rp); + isClosed = true; + break; + } + p.next(); + } + if (!isClosed) { + closeSolidShape(); + } + + dst = lp; + } + + /** + * Closes solid shape path. + */ + void closeSolidShape() { + addCap(lp, cx, cy, rp.xLast, rp.yLast); + lp.combine(rp); + addCap(lp, mx, my, lp.xMove, lp.yMove); + lp.closePath(); + } + + /** + * Generates dashed stroked shape. + * + * @param p + * the PathIterator of source shape. + */ + void createDashedShape(PathIterator p) { + double coords[] = new double[6]; + mx = my = cx = cy = 0.0; + smx = smy = scx = scy = 0.0; + isMove = false; + checkMove = false; + boolean isClosed = true; + + while (!p.isDone()) { + switch (p.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + + if (!isClosed) { + closeDashedShape(); + } + + dasher = new Dasher(dash, dashPhase); + lp.clean(); + rp.clean(); + sp = null; + isFirst = true; + isMove = true; + isClosed = false; + mx = cx = coords[0]; + my = cy = coords[1]; + break; + case PathIterator.SEG_LINETO: + addDashLine(cx, cy, cx = coords[0], cy = coords[1]); + break; + case PathIterator.SEG_QUADTO: + addDashQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3]); + break; + case PathIterator.SEG_CUBICTO: + addDashCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], + cx = coords[4], cy = coords[5]); + break; + case PathIterator.SEG_CLOSE: + addDashLine(cx, cy, cx = mx, cy = my); + + if (dasher.isConnected()) { + // Connect current and head segments + addJoin(lp, fmx, fmy, sp.xMove, sp.yMove, true); + lp.join(sp); + addJoin(lp, fmx, fmy, rp.xLast, rp.yLast, true); + lp.combine(rp); + addCap(lp, smx, smy, lp.xMove, lp.yMove); + lp.closePath(); + dst.append(lp); + sp = null; + } else { + closeDashedShape(); + } + + isClosed = true; + break; + } + p.next(); + } + + if (!isClosed) { + closeDashedShape(); + } + + } + + /** + * Closes dashed shape path. + */ + void closeDashedShape() { + // Add head segment + if (sp != null) { + addCap(sp, fmx, fmy, sp.xMove, sp.yMove); + sp.closePath(); + dst.append(sp); + } + if (lp.typeSize > 0) { + // Close current segment + if (!dasher.isClosed()) { + addCap(lp, scx, scy, rp.xLast, rp.yLast); + lp.combine(rp); + addCap(lp, smx, smy, lp.xMove, lp.yMove); + lp.closePath(); + } + dst.append(lp); + } + } + + /** + * Adds cap to the work path. + * + * @param p + * the BufferedPath object of work path. + * @param x0 + * the x coordinate of the source path. + * @param y0 + * the y coordinate on the source path. + * @param x2 + * the x coordinate of the next point on the work path. + * @param y2 + * the y coordinate of the next point on the work path. + */ + void addCap(BufferedPath p, double x0, double y0, double x2, double y2) { + double x1 = p.xLast; + double y1 = p.yLast; + double x10 = x1 - x0; + double y10 = y1 - y0; + double x20 = x2 - x0; + double y20 = y2 - y0; + + switch (cap) { + case CAP_BUTT: + p.lineTo(x2, y2); + break; + case CAP_ROUND: + double mx = x10 * CUBIC_ARC; + double my = y10 * CUBIC_ARC; + + double x3 = x0 + y10; + double y3 = y0 - x10; + + x10 *= CUBIC_ARC; + y10 *= CUBIC_ARC; + x20 *= CUBIC_ARC; + y20 *= CUBIC_ARC; + + p.cubicTo(x1 + y10, y1 - x10, x3 + mx, y3 + my, x3, y3); + p.cubicTo(x3 - mx, y3 - my, x2 - y20, y2 + x20, x2, y2); + break; + case CAP_SQUARE: + p.lineTo(x1 + y10, y1 - x10); + p.lineTo(x2 - y20, y2 + x20); + p.lineTo(x2, y2); + break; + } + } + + /** + * Adds bevel and miter join to the work path. + * + * @param p + * the BufferedPath object of work path. + * @param x0 + * the x coordinate of the source path. + * @param y0 + * the y coordinate on the source path. + * @param x2 + * the x coordinate of the next point on the work path. + * @param y2 + * the y coordinate of the next point on the work path. + * @param isLeft + * the orientation of work path, true if work path lies to the + * left from source path, false otherwise. + */ + void addJoin(BufferedPath p, double x0, double y0, double x2, double y2, boolean isLeft) { + double x1 = p.xLast; + double y1 = p.yLast; + double x10 = x1 - x0; + double y10 = y1 - y0; + double x20 = x2 - x0; + double y20 = y2 - y0; + double sin0 = x10 * y20 - y10 * x20; + + // Small corner + if (-cornerDelta < sin0 && sin0 < cornerDelta) { + double cos0 = x10 * x20 + y10 * y20; + if (cos0 > 0.0) { + // if zero corner do nothing + if (-zeroDelta > sin0 || sin0 > zeroDelta) { + double x3 = x0 + w2 * w2 * (y20 - y10) / sin0; + double y3 = y0 + w2 * w2 * (x10 - x20) / sin0; + p.setLast(x3, y3); + } + return; + } + // Zero corner + if (-zeroDelta < sin0 && sin0 < zeroDelta) { + p.lineTo(x2, y2); + } + return; + } + + if (isLeft ^ (sin0 < 0.0)) { + // Twisted corner + p.lineTo(x0, y0); + p.lineTo(x2, y2); + } else { + switch (join) { + case JOIN_BEVEL: + p.lineTo(x2, y2); + break; + case JOIN_MITER: + double s1 = x1 * x10 + y1 * y10; + double s2 = x2 * x20 + y2 * y20; + double x3 = (s1 * y20 - s2 * y10) / sin0; + double y3 = (s2 * x10 - s1 * x20) / sin0; + double x30 = x3 - x0; + double y30 = y3 - y0; + double miterLength = Math.sqrt(x30 * x30 + y30 * y30); + if (miterLength < miterLimit * w2) { + p.lineTo(x3, y3); + } + p.lineTo(x2, y2); + break; + case JOIN_ROUND: + addRoundJoin(p, x0, y0, x2, y2, isLeft); + break; + } + } + } + + /** + * Adds round join to the work path. + * + * @param p + * the BufferedPath object of work path. + * @param x0 + * the x coordinate of the source path. + * @param y0 + * the y coordinate on the source path. + * @param x2 + * the x coordinate of the next point on the work path. + * @param y2 + * the y coordinate of the next point on the work path. + * @param isLeft + * the orientation of work path, true if work path lies to the + * left from source path, false otherwise. + */ + void addRoundJoin(BufferedPath p, double x0, double y0, double x2, double y2, boolean isLeft) { + double x1 = p.xLast; + double y1 = p.yLast; + double x10 = x1 - x0; + double y10 = y1 - y0; + double x20 = x2 - x0; + double y20 = y2 - y0; + + double x30 = x10 + x20; + double y30 = y10 + y20; + + double l30 = Math.sqrt(x30 * x30 + y30 * y30); + + if (l30 < 1E-5) { + p.lineTo(x2, y2); + return; + } + + double w = w2 / l30; + + x30 *= w; + y30 *= w; + + double x3 = x0 + x30; + double y3 = y0 + y30; + + double cos = x10 * x20 + y10 * y20; + double a = Math.acos(cos / (w2 * w2)); + if (cos >= 0.0) { + double k = 4.0 / 3.0 * Math.tan(a / 4.0); + if (isLeft) { + k = -k; + } + + x10 *= k; + y10 *= k; + x20 *= k; + y20 *= k; + + p.cubicTo(x1 - y10, y1 + x10, x2 + y20, y2 - x20, x2, y2); + } else { + double k = 4.0 / 3.0 * Math.tan(a / 8.0); + if (isLeft) { + k = -k; + } + + x10 *= k; + y10 *= k; + x20 *= k; + y20 *= k; + x30 *= k; + y30 *= k; + + p.cubicTo(x1 - y10, y1 + x10, x3 + y30, y3 - x30, x3, y3); + p.cubicTo(x3 - y30, y3 + x30, x2 + y20, y2 - x20, x2, y2); + } + + } + + /** + * Adds solid line segment to the work path. + * + * @param x1 + * the x coordinate of the start line point. + * @param y1 + * the y coordinate of the start line point. + * @param x2 + * the x coordinate of the end line point. + * @param y2 + * the y coordinate of the end line point. + * @param zero + * if true it's allowable to add zero length line segment. + */ + void addLine(double x1, double y1, double x2, double y2, boolean zero) { + double dx = x2 - x1; + double dy = y2 - y1; + + if (dx == 0.0 && dy == 0.0) { + if (!zero) { + return; + } + dx = w2; + dy = 0; + } else { + double w = w2 / Math.sqrt(dx * dx + dy * dy); + dx *= w; + dy *= w; + } + + double lx1 = x1 - dy; + double ly1 = y1 + dx; + double rx1 = x1 + dy; + double ry1 = y1 - dx; + + if (checkMove) { + if (isMove) { + isMove = false; + lp.moveTo(lx1, ly1); + rp.moveTo(rx1, ry1); + } else { + addJoin(lp, x1, y1, lx1, ly1, true); + addJoin(rp, x1, y1, rx1, ry1, false); + } + } + + lp.lineTo(x2 - dy, y2 + dx); + rp.lineTo(x2 + dy, y2 - dx); + } + + /** + * Adds solid quad segment to the work path. + * + * @param x1 + * the x coordinate of the first control point. + * @param y1 + * the y coordinate of the first control point. + * @param x2 + * the x coordinate of the second control point. + * @param y2 + * the y coordinate of the second control point. + * @param x3 + * the x coordinate of the third control point. + * @param y3 + * the y coordinate of the third control point. + */ + void addQuad(double x1, double y1, double x2, double y2, double x3, double y3) { + double x21 = x2 - x1; + double y21 = y2 - y1; + double x23 = x2 - x3; + double y23 = y2 - y3; + + double l21 = Math.sqrt(x21 * x21 + y21 * y21); + double l23 = Math.sqrt(x23 * x23 + y23 * y23); + + if (l21 == 0.0 && l23 == 0.0) { + addLine(x1, y1, x3, y3, false); + return; + } + + if (l21 == 0.0) { + addLine(x2, y2, x3, y3, false); + return; + } + + if (l23 == 0.0) { + addLine(x1, y1, x2, y2, false); + return; + } + + double w; + w = w2 / l21; + double mx1 = -y21 * w; + double my1 = x21 * w; + w = w2 / l23; + double mx3 = y23 * w; + double my3 = -x23 * w; + + double lx1 = x1 + mx1; + double ly1 = y1 + my1; + double rx1 = x1 - mx1; + double ry1 = y1 - my1; + + if (checkMove) { + if (isMove) { + isMove = false; + lp.moveTo(lx1, ly1); + rp.moveTo(rx1, ry1); + } else { + addJoin(lp, x1, y1, lx1, ly1, true); + addJoin(rp, x1, y1, rx1, ry1, false); + } + } + + if (x21 * y23 - y21 * x23 == 0.0) { + // On line curve + if (x21 * x23 + y21 * y23 > 0.0) { + // Twisted curve + if (l21 == l23) { + double px = x1 + (x21 + x23) / 4.0; + double py = y1 + (y21 + y23) / 4.0; + lp.lineTo(px + mx1, py + my1); + rp.lineTo(px - mx1, py - my1); + lp.lineTo(px - mx1, py - my1); + rp.lineTo(px + mx1, py + my1); + lp.lineTo(x3 - mx1, y3 - my1); + rp.lineTo(x3 + mx1, y3 + my1); + } else { + double px1, py1; + double k = l21 / (l21 + l23); + double px = x1 + (x21 + x23) * k * k; + double py = y1 + (y21 + y23) * k * k; + px1 = (x1 + px) / 2.0; + py1 = (y1 + py) / 2.0; + lp.quadTo(px1 + mx1, py1 + my1, px + mx1, py + my1); + rp.quadTo(px1 - mx1, py1 - my1, px - mx1, py - my1); + lp.lineTo(px - mx1, py - my1); + rp.lineTo(px + mx1, py + my1); + px1 = (x3 + px) / 2.0; + py1 = (y3 + py) / 2.0; + lp.quadTo(px1 - mx1, py1 - my1, x3 - mx1, y3 - my1); + rp.quadTo(px1 + mx1, py1 + my1, x3 + mx1, y3 + my1); + } + } else { + // Simple curve + lp.quadTo(x2 + mx1, y2 + my1, x3 + mx3, y3 + my3); + rp.quadTo(x2 - mx1, y2 - my1, x3 - mx3, y3 - my3); + } + } else { + addSubQuad(x1, y1, x2, y2, x3, y3, 0); + } + } + + /** + * Subdivides solid quad curve to make outline for source quad segment and + * adds it to work path. + * + * @param x1 + * the x coordinate of the first control point. + * @param y1 + * the y coordinate of the first control point. + * @param x2 + * the x coordinate of the second control point. + * @param y2 + * the y coordinate of the second control point. + * @param x3 + * the x coordinate of the third control point. + * @param y3 + * the y coordinate of the third control point. + * @param level + * the maximum level of subdivision deepness. + */ + void addSubQuad(double x1, double y1, double x2, double y2, double x3, double y3, int level) { + double x21 = x2 - x1; + double y21 = y2 - y1; + double x23 = x2 - x3; + double y23 = y2 - y3; + + double cos = x21 * x23 + y21 * y23; + double sin = x21 * y23 - y21 * x23; + + if (level < MAX_LEVEL && (cos >= 0.0 || (Math.abs(sin / cos) > curveDelta))) { + double c1x = (x2 + x1) / 2.0; + double c1y = (y2 + y1) / 2.0; + double c2x = (x2 + x3) / 2.0; + double c2y = (y2 + y3) / 2.0; + double c3x = (c1x + c2x) / 2.0; + double c3y = (c1y + c2y) / 2.0; + addSubQuad(x1, y1, c1x, c1y, c3x, c3y, level + 1); + addSubQuad(c3x, c3y, c2x, c2y, x3, y3, level + 1); + } else { + double w; + double l21 = Math.sqrt(x21 * x21 + y21 * y21); + double l23 = Math.sqrt(x23 * x23 + y23 * y23); + w = w2 / sin; + double mx2 = (x21 * l23 + x23 * l21) * w; + double my2 = (y21 * l23 + y23 * l21) * w; + w = w2 / l23; + double mx3 = y23 * w; + double my3 = -x23 * w; + lp.quadTo(x2 + mx2, y2 + my2, x3 + mx3, y3 + my3); + rp.quadTo(x2 - mx2, y2 - my2, x3 - mx3, y3 - my3); + } + } + + /** + * Adds solid cubic segment to the work path. + * + * @param x1 + * the x coordinate of the first control point. + * @param y1 + * the y coordinate of the first control point. + * @param x2 + * the x coordinate of the second control point. + * @param y2 + * the y coordinate of the second control point. + * @param x3 + * the x coordinate of the third control point. + * @param y3 + * the y coordinate of the third control point. + * @param x4 + * the x coordinate of the fours control point. + * @param y4 + * the y coordinate of the fours control point. + */ + void addCubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4, + double y4) { + double x12 = x1 - x2; + double y12 = y1 - y2; + double x23 = x2 - x3; + double y23 = y2 - y3; + double x34 = x3 - x4; + double y34 = y3 - y4; + + double l12 = Math.sqrt(x12 * x12 + y12 * y12); + double l23 = Math.sqrt(x23 * x23 + y23 * y23); + double l34 = Math.sqrt(x34 * x34 + y34 * y34); + + // All edges are zero + if (l12 == 0.0 && l23 == 0.0 && l34 == 0.0) { + addLine(x1, y1, x4, y4, false); + return; + } + + // One zero edge + if (l12 == 0.0 && l23 == 0.0) { + addLine(x3, y3, x4, y4, false); + return; + } + + if (l23 == 0.0 && l34 == 0.0) { + addLine(x1, y1, x2, y2, false); + return; + } + + if (l12 == 0.0 && l34 == 0.0) { + addLine(x2, y2, x3, y3, false); + return; + } + + double w, mx1, my1, mx4, my4; + boolean onLine; + + if (l12 == 0.0) { + w = w2 / l23; + mx1 = y23 * w; + my1 = -x23 * w; + w = w2 / l34; + mx4 = y34 * w; + my4 = -x34 * w; + onLine = -x23 * y34 + y23 * x34 == 0.0; // sin3 + } else if (l34 == 0.0) { + w = w2 / l12; + mx1 = y12 * w; + my1 = -x12 * w; + w = w2 / l23; + mx4 = y23 * w; + my4 = -x23 * w; + onLine = -x12 * y23 + y12 * x23 == 0.0; // sin2 + } else { + w = w2 / l12; + mx1 = y12 * w; + my1 = -x12 * w; + w = w2 / l34; + mx4 = y34 * w; + my4 = -x34 * w; + if (l23 == 0.0) { + onLine = -x12 * y34 + y12 * x34 == 0.0; + } else { + onLine = -x12 * y34 + y12 * x34 == 0.0 && -x12 * y23 + y12 * x23 == 0.0 && // sin2 + -x23 * y34 + y23 * x34 == 0.0; // sin3 + } + } + + double lx1 = x1 + mx1; + double ly1 = y1 + my1; + double rx1 = x1 - mx1; + double ry1 = y1 - my1; + + if (checkMove) { + if (isMove) { + isMove = false; + lp.moveTo(lx1, ly1); + rp.moveTo(rx1, ry1); + } else { + addJoin(lp, x1, y1, lx1, ly1, true); + addJoin(rp, x1, y1, rx1, ry1, false); + } + } + + if (onLine) { + if ((x1 == x2 && y1 < y2) || x1 < x2) { + l12 = -l12; + } + if ((x2 == x3 && y2 < y3) || x2 < x3) { + l23 = -l23; + } + if ((x3 == x4 && y3 < y4) || x3 < x4) { + l34 = -l34; + } + double d = l23 * l23 - l12 * l34; + double roots[] = new double[3]; + int rc = 0; + if (d == 0.0) { + double t = (l12 - l23) / (l12 + l34 - l23 - l23); + if (0.0 < t && t < 1.0) { + roots[rc++] = t; + } + } else if (d > 0.0) { + d = Math.sqrt(d); + double z = l12 + l34 - l23 - l23; + double t; + t = (l12 - l23 + d) / z; + if (0.0 < t && t < 1.0) { + roots[rc++] = t; + } + t = (l12 - l23 - d) / z; + if (0.0 < t && t < 1.0) { + roots[rc++] = t; + } + } + + if (rc > 0) { + // Sort roots + if (rc == 2 && roots[0] > roots[1]) { + double tmp = roots[0]; + roots[0] = roots[1]; + roots[1] = tmp; + } + roots[rc++] = 1.0; + + double ax = -x34 - x12 + x23 + x23; + double ay = -y34 - y12 + y23 + y23; + double bx = 3.0 * (-x23 + x12); + double by = 3.0 * (-y23 + y12); + double cx = 3.0 * (-x12); + double cy = 3.0 * (-y12); + double xPrev = x1; + double yPrev = y1; + for (int i = 0; i < rc; i++) { + double t = roots[i]; + double px = t * (t * (t * ax + bx) + cx) + x1; + double py = t * (t * (t * ay + by) + cy) + y1; + double px1 = (xPrev + px) / 2.0; + double py1 = (yPrev + py) / 2.0; + lp.cubicTo(px1 + mx1, py1 + my1, px1 + mx1, py1 + my1, px + mx1, py + my1); + rp.cubicTo(px1 - mx1, py1 - my1, px1 - mx1, py1 - my1, px - mx1, py - my1); + if (i < rc - 1) { + lp.lineTo(px - mx1, py - my1); + rp.lineTo(px + mx1, py + my1); + } + xPrev = px; + yPrev = py; + mx1 = -mx1; + my1 = -my1; + } + } else { + lp.cubicTo(x2 + mx1, y2 + my1, x3 + mx4, y3 + my4, x4 + mx4, y4 + my4); + rp.cubicTo(x2 - mx1, y2 - my1, x3 - mx4, y3 - my4, x4 - mx4, y4 - my4); + } + } else { + addSubCubic(x1, y1, x2, y2, x3, y3, x4, y4, 0); + } + } + + /** + * Subdivides solid cubic curve to make outline for source quad segment and + * adds it to work path. + * + * @param x1 + * the x coordinate of the first control point. + * @param y1 + * the y coordinate of the first control point. + * @param x2 + * the x coordinate of the second control point. + * @param y2 + * the y coordinate of the second control point. + * @param x3 + * the x coordinate of the third control point. + * @param y3 + * the y coordinate of the third control point. + * @param x4 + * the x coordinate of the fours control point. + * @param y4 + * the y coordinate of the fours control point. + * @param level + * the maximum level of subdivision deepness. + */ + void addSubCubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4, + double y4, int level) { + double x12 = x1 - x2; + double y12 = y1 - y2; + double x23 = x2 - x3; + double y23 = y2 - y3; + double x34 = x3 - x4; + double y34 = y3 - y4; + + double cos2 = -x12 * x23 - y12 * y23; + double cos3 = -x23 * x34 - y23 * y34; + double sin2 = -x12 * y23 + y12 * x23; + double sin3 = -x23 * y34 + y23 * x34; + double sin0 = -x12 * y34 + y12 * x34; + double cos0 = -x12 * x34 - y12 * y34; + + if (level < MAX_LEVEL + && (sin2 != 0.0 || sin3 != 0.0 || sin0 != 0.0) + && (cos2 >= 0.0 || cos3 >= 0.0 || cos0 >= 0.0 + || (Math.abs(sin2 / cos2) > curveDelta) + || (Math.abs(sin3 / cos3) > curveDelta) || (Math.abs(sin0 / cos0) > curveDelta))) { + double cx = (x2 + x3) / 2.0; + double cy = (y2 + y3) / 2.0; + double lx2 = (x2 + x1) / 2.0; + double ly2 = (y2 + y1) / 2.0; + double rx3 = (x3 + x4) / 2.0; + double ry3 = (y3 + y4) / 2.0; + double lx3 = (cx + lx2) / 2.0; + double ly3 = (cy + ly2) / 2.0; + double rx2 = (cx + rx3) / 2.0; + double ry2 = (cy + ry3) / 2.0; + cx = (lx3 + rx2) / 2.0; + cy = (ly3 + ry2) / 2.0; + addSubCubic(x1, y1, lx2, ly2, lx3, ly3, cx, cy, level + 1); + addSubCubic(cx, cy, rx2, ry2, rx3, ry3, x4, y4, level + 1); + } else { + double w, mx1, my1, mx2, my2, mx3, my3, mx4, my4; + double l12 = Math.sqrt(x12 * x12 + y12 * y12); + double l23 = Math.sqrt(x23 * x23 + y23 * y23); + double l34 = Math.sqrt(x34 * x34 + y34 * y34); + + if (l12 == 0.0) { + w = w2 / l23; + mx1 = y23 * w; + my1 = -x23 * w; + w = w2 / l34; + mx4 = y34 * w; + my4 = -x34 * w; + } else if (l34 == 0.0) { + w = w2 / l12; + mx1 = y12 * w; + my1 = -x12 * w; + w = w2 / l23; + mx4 = y23 * w; + my4 = -x23 * w; + } else { + // Common case + w = w2 / l12; + mx1 = y12 * w; + my1 = -x12 * w; + w = w2 / l34; + mx4 = y34 * w; + my4 = -x34 * w; + } + + if (sin2 == 0.0) { + mx2 = mx1; + my2 = my1; + } else { + w = w2 / sin2; + mx2 = -(x12 * l23 - x23 * l12) * w; + my2 = -(y12 * l23 - y23 * l12) * w; + } + if (sin3 == 0.0) { + mx3 = mx4; + my3 = my4; + } else { + w = w2 / sin3; + mx3 = -(x23 * l34 - x34 * l23) * w; + my3 = -(y23 * l34 - y34 * l23) * w; + } + + lp.cubicTo(x2 + mx2, y2 + my2, x3 + mx3, y3 + my3, x4 + mx4, y4 + my4); + rp.cubicTo(x2 - mx2, y2 - my2, x3 - mx3, y3 - my3, x4 - mx4, y4 - my4); + } + } + + /** + * Adds dashed line segment to the work path. + * + * @param x1 + * the x coordinate of the start line point. + * @param y1 + * the y coordinate of the start line point. + * @param x2 + * the x coordinate of the end line point. + * @param y2 + * the y coordinate of the end line point. + */ + void addDashLine(double x1, double y1, double x2, double y2) { + double x21 = x2 - x1; + double y21 = y2 - y1; + + double l21 = Math.sqrt(x21 * x21 + y21 * y21); + + if (l21 == 0.0) { + return; + } + + double px1, py1; + px1 = py1 = 0.0; + double w = w2 / l21; + double mx = -y21 * w; + double my = x21 * w; + + dasher.init(new DashIterator.Line(l21)); + + while (!dasher.eof()) { + double t = dasher.getValue(); + scx = x1 + t * x21; + scy = y1 + t * y21; + + if (dasher.isOpen()) { + px1 = scx; + py1 = scy; + double lx1 = px1 + mx; + double ly1 = py1 + my; + double rx1 = px1 - mx; + double ry1 = py1 - my; + if (isMove) { + isMove = false; + smx = px1; + smy = py1; + rp.clean(); + lp.moveTo(lx1, ly1); + rp.moveTo(rx1, ry1); + } else { + addJoin(lp, x1, y1, lx1, ly1, true); + addJoin(rp, x1, y1, rx1, ry1, false); + } + } else if (dasher.isContinue()) { + double px2 = scx; + double py2 = scy; + lp.lineTo(px2 + mx, py2 + my); + rp.lineTo(px2 - mx, py2 - my); + if (dasher.close) { + addCap(lp, px2, py2, rp.xLast, rp.yLast); + lp.combine(rp); + if (isFirst) { + isFirst = false; + fmx = smx; + fmy = smy; + sp = lp; + lp = new BufferedPath(); + } else { + addCap(lp, smx, smy, lp.xMove, lp.yMove); + lp.closePath(); + } + isMove = true; + } + } + + dasher.next(); + } + } + + /** + * Adds dashed quad segment to the work path. + * + * @param x1 + * the x coordinate of the first control point. + * @param y1 + * the y coordinate of the first control point. + * @param x2 + * the x coordinate of the second control point. + * @param y2 + * the y coordinate of the second control point. + * @param x3 + * the x coordinate of the third control point. + * @param y3 + * the y coordinate of the third control point. + */ + void addDashQuad(double x1, double y1, double x2, double y2, double x3, double y3) { + + double x21 = x2 - x1; + double y21 = y2 - y1; + double x23 = x2 - x3; + double y23 = y2 - y3; + + double l21 = Math.sqrt(x21 * x21 + y21 * y21); + double l23 = Math.sqrt(x23 * x23 + y23 * y23); + + if (l21 == 0.0 && l23 == 0.0) { + return; + } + + if (l21 == 0.0) { + addDashLine(x2, y2, x3, y3); + return; + } + + if (l23 == 0.0) { + addDashLine(x1, y1, x2, y2); + return; + } + + double ax = x1 + x3 - x2 - x2; + double ay = y1 + y3 - y2 - y2; + double bx = x2 - x1; + double by = y2 - y1; + double cx = x1; + double cy = y1; + + double px1, py1, dx1, dy1; + px1 = py1 = dx1 = dy1 = 0.0; + double prev = 0.0; + + dasher.init(new DashIterator.Quad(x1, y1, x2, y2, x3, y3)); + + while (!dasher.eof()) { + double t = dasher.getValue(); + double dx = t * ax + bx; + double dy = t * ay + by; + scx = t * (dx + bx) + cx; // t^2 * ax + 2.0 * t * bx + cx + scy = t * (dy + by) + cy; // t^2 * ay + 2.0 * t * by + cy + if (dasher.isOpen()) { + px1 = scx; + py1 = scy; + dx1 = dx; + dy1 = dy; + double w = w2 / Math.sqrt(dx1 * dx1 + dy1 * dy1); + double mx1 = -dy1 * w; + double my1 = dx1 * w; + double lx1 = px1 + mx1; + double ly1 = py1 + my1; + double rx1 = px1 - mx1; + double ry1 = py1 - my1; + if (isMove) { + isMove = false; + smx = px1; + smy = py1; + rp.clean(); + lp.moveTo(lx1, ly1); + rp.moveTo(rx1, ry1); + } else { + addJoin(lp, x1, y1, lx1, ly1, true); + addJoin(rp, x1, y1, rx1, ry1, false); + } + } else if (dasher.isContinue()) { + double px3 = scx; + double py3 = scy; + double sx = x2 - x23 * prev; + double sy = y2 - y23 * prev; + double t2 = (t - prev) / (1 - prev); + double px2 = px1 + (sx - px1) * t2; + double py2 = py1 + (sy - py1) * t2; + + addQuad(px1, py1, px2, py2, px3, py3); + if (dasher.isClosed()) { + addCap(lp, px3, py3, rp.xLast, rp.yLast); + lp.combine(rp); + if (isFirst) { + isFirst = false; + fmx = smx; + fmy = smy; + sp = lp; + lp = new BufferedPath(); + } else { + addCap(lp, smx, smy, lp.xMove, lp.yMove); + lp.closePath(); + } + isMove = true; + } + } + + prev = t; + dasher.next(); + } + } + + /** + * Adds dashed cubic segment to the work path. + * + * @param x1 + * the x coordinate of the first control point. + * @param y1 + * the y coordinate of the first control point. + * @param x2 + * the x coordinate of the second control point. + * @param y2 + * the y coordinate of the second control point. + * @param x3 + * the x coordinate of the third control point. + * @param y3 + * the y coordinate of the third control point. + * @param x4 + * the x coordinate of the fours control point. + * @param y4 + * the y coordinate of the fours control point. + */ + void addDashCubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4, + double y4) { + + double x12 = x1 - x2; + double y12 = y1 - y2; + double x23 = x2 - x3; + double y23 = y2 - y3; + double x34 = x3 - x4; + double y34 = y3 - y4; + + double l12 = Math.sqrt(x12 * x12 + y12 * y12); + double l23 = Math.sqrt(x23 * x23 + y23 * y23); + double l34 = Math.sqrt(x34 * x34 + y34 * y34); + + // All edges are zero + if (l12 == 0.0 && l23 == 0.0 && l34 == 0.0) { + // NOTHING + return; + } + + // One zero edge + if (l12 == 0.0 && l23 == 0.0) { + addDashLine(x3, y3, x4, y4); + return; + } + + if (l23 == 0.0 && l34 == 0.0) { + addDashLine(x1, y1, x2, y2); + return; + } + + if (l12 == 0.0 && l34 == 0.0) { + addDashLine(x2, y2, x3, y3); + return; + } + + double ax = x4 - x1 + 3.0 * (x2 - x3); + double ay = y4 - y1 + 3.0 * (y2 - y3); + double bx = 3.0 * (x1 + x3 - x2 - x2); + double by = 3.0 * (y1 + y3 - y2 - y2); + double cx = 3.0 * (x2 - x1); + double cy = 3.0 * (y2 - y1); + double dx = x1; + double dy = y1; + + double px1 = 0.0; + double py1 = 0.0; + double prev = 0.0; + + dasher.init(new DashIterator.Cubic(x1, y1, x2, y2, x3, y3, x4, y4)); + + while (!dasher.eof()) { + + double t = dasher.getValue(); + scx = t * (t * (t * ax + bx) + cx) + dx; + scy = t * (t * (t * ay + by) + cy) + dy; + if (dasher.isOpen()) { + px1 = scx; + py1 = scy; + double dx1 = t * (t * (ax + ax + ax) + bx + bx) + cx; + double dy1 = t * (t * (ay + ay + ay) + by + by) + cy; + double w = w2 / Math.sqrt(dx1 * dx1 + dy1 * dy1); + double mx1 = -dy1 * w; + double my1 = dx1 * w; + double lx1 = px1 + mx1; + double ly1 = py1 + my1; + double rx1 = px1 - mx1; + double ry1 = py1 - my1; + if (isMove) { + isMove = false; + smx = px1; + smy = py1; + rp.clean(); + lp.moveTo(lx1, ly1); + rp.moveTo(rx1, ry1); + } else { + addJoin(lp, x1, y1, lx1, ly1, true); + addJoin(rp, x1, y1, rx1, ry1, false); + } + } else if (dasher.isContinue()) { + double sx1 = x2 - x23 * prev; + double sy1 = y2 - y23 * prev; + double sx2 = x3 - x34 * prev; + double sy2 = y3 - y34 * prev; + double sx3 = sx1 + (sx2 - sx1) * prev; + double sy3 = sy1 + (sy2 - sy1) * prev; + double t2 = (t - prev) / (1 - prev); + double sx4 = sx3 + (sx2 - sx3) * t2; + double sy4 = sy3 + (sy2 - sy3) * t2; + + double px4 = scx; + double py4 = scy; + double px2 = px1 + (sx3 - px1) * t2; + double py2 = py1 + (sy3 - py1) * t2; + double px3 = px2 + (sx4 - px2) * t2; + double py3 = py2 + (sy4 - py2) * t2; + + addCubic(px1, py1, px2, py2, px3, py3, px4, py4); + if (dasher.isClosed()) { + addCap(lp, px4, py4, rp.xLast, rp.yLast); + lp.combine(rp); + if (isFirst) { + isFirst = false; + fmx = smx; + fmy = smy; + sp = lp; + lp = new BufferedPath(); + } else { + addCap(lp, smx, smy, lp.xMove, lp.yMove); + lp.closePath(); + } + isMove = true; + } + } + + prev = t; + dasher.next(); + } + } + + /** + * Dasher class provides dashing for particular dash style. + */ + class Dasher { + + /** + * The pos. + */ + double pos; + + /** + * The first. + */ + boolean close, visible, first; + + /** + * The dash. + */ + float dash[]; + + /** + * The phase. + */ + float phase; + + /** + * The index. + */ + int index; + + /** + * The iter. + */ + DashIterator iter; + + /** + * Instantiates a new dasher. + * + * @param dash + * the dash. + * @param phase + * the phase. + */ + Dasher(float dash[], float phase) { + this.dash = dash; + this.phase = phase; + index = 0; + pos = phase; + visible = true; + while (pos >= dash[index]) { + visible = !visible; + pos -= dash[index]; + index = (index + 1) % dash.length; + } + pos = -pos; + first = visible; + } + + /** + * Inits the. + * + * @param iter + * the iter. + */ + void init(DashIterator iter) { + this.iter = iter; + close = true; + } + + /** + * Checks if is open. + * + * @return true, if is open. + */ + boolean isOpen() { + return visible && pos < iter.length; + } + + /** + * Checks if is continue. + * + * @return true, if is continue. + */ + boolean isContinue() { + return !visible && pos > 0; + } + + /** + * Checks if is closed. + * + * @return true, if is closed. + */ + boolean isClosed() { + return close; + } + + /** + * Checks if is connected. + * + * @return true, if is connected. + */ + boolean isConnected() { + return first && !close; + } + + /** + * Eof. + * + * @return true, if successful. + */ + boolean eof() { + if (!close) { + pos -= iter.length; + return true; + } + if (pos >= iter.length) { + if (visible) { + pos -= iter.length; + return true; + } + close = pos == iter.length; + } + return false; + } + + /** + * Next. + */ + void next() { + if (close) { + pos += dash[index]; + index = (index + 1) % dash.length; + } else { + // Go back + index = (index + dash.length - 1) % dash.length; + pos -= dash[index]; + } + visible = !visible; + } + + /** + * Gets the value. + * + * @return the value. + */ + double getValue() { + double t = iter.getNext(pos); + return t < 0 ? 0 : (t > 1 ? 1 : t); + } + + } + + /** + * DashIterator class provides dashing for particular segment type. + */ + static abstract class DashIterator { + + /** + * The Constant FLATNESS. + */ + static final double FLATNESS = 1.0; + + /** + * The Class Line. + */ + static class Line extends DashIterator { + + /** + * Instantiates a new line. + * + * @param len + * the len. + */ + Line(double len) { + length = len; + } + + @Override + double getNext(double dashPos) { + return dashPos / length; + } + + } + + /** + * The Class Quad. + */ + static class Quad extends DashIterator { + + /** + * The val size. + */ + int valSize; + + /** + * The val pos. + */ + int valPos; + + /** + * The cur len. + */ + double curLen; + + /** + * The prev len. + */ + double prevLen; + + /** + * The last len. + */ + double lastLen; + + /** + * The values. + */ + double[] values; + + /** + * The step. + */ + double step; + + /** + * Instantiates a new quad. + * + * @param x1 + * the x1. + * @param y1 + * the y1. + * @param x2 + * the x2. + * @param y2 + * the y2. + * @param x3 + * the x3. + * @param y3 + * the y3. + */ + Quad(double x1, double y1, double x2, double y2, double x3, double y3) { + + double nx = x1 + x3 - x2 - x2; + double ny = y1 + y3 - y2 - y2; + + int n = (int)(1 + Math.sqrt(0.75 * (Math.abs(nx) + Math.abs(ny)) * FLATNESS)); + step = 1.0 / n; + + double ax = x1 + x3 - x2 - x2; + double ay = y1 + y3 - y2 - y2; + double bx = 2.0 * (x2 - x1); + double by = 2.0 * (y2 - y1); + + double dx1 = step * (step * ax + bx); + double dy1 = step * (step * ay + by); + double dx2 = step * (step * ax * 2.0); + double dy2 = step * (step * ay * 2.0); + double vx = x1; + double vy = y1; + + valSize = n; + values = new double[valSize]; + double pvx = vx; + double pvy = vy; + length = 0.0; + for (int i = 0; i < n; i++) { + vx += dx1; + vy += dy1; + dx1 += dx2; + dy1 += dy2; + double lx = vx - pvx; + double ly = vy - pvy; + values[i] = Math.sqrt(lx * lx + ly * ly); + length += values[i]; + pvx = vx; + pvy = vy; + } + + valPos = 0; + curLen = 0.0; + prevLen = 0.0; + } + + @Override + double getNext(double dashPos) { + double t = 2.0; + while (curLen <= dashPos && valPos < valSize) { + prevLen = curLen; + curLen += lastLen = values[valPos++]; + } + if (curLen > dashPos) { + t = (valPos - 1 + (dashPos - prevLen) / lastLen) * step; + } + return t; + } + + } + + /** + * The Class Cubic. + */ + static class Cubic extends DashIterator { + + /** + * The val size. + */ + int valSize; + + /** + * The val pos. + */ + int valPos; + + /** + * The cur len. + */ + double curLen; + + /** + * The prev len. + */ + double prevLen; + + /** + * The last len. + */ + double lastLen; + + /** + * The values. + */ + double[] values; + + /** + * The step. + */ + double step; + + /** + * Instantiates a new cubic. + * + * @param x1 + * the x1. + * @param y1 + * the y1. + * @param x2 + * the x2. + * @param y2 + * the y2. + * @param x3 + * the x3. + * @param y3 + * the y3. + * @param x4 + * the x4. + * @param y4 + * the y4. + */ + Cubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4, + double y4) { + + double nx1 = x1 + x3 - x2 - x2; + double ny1 = y1 + y3 - y2 - y2; + double nx2 = x2 + x4 - x3 - x3; + double ny2 = y2 + y4 - y3 - y3; + + double max = Math.max(Math.abs(nx1) + Math.abs(ny1), Math.abs(nx2) + Math.abs(ny2)); + int n = (int)(1 + Math.sqrt(0.75 * max) * FLATNESS); + step = 1.0 / n; + + double ax = x4 - x1 + 3.0 * (x2 - x3); + double ay = y4 - y1 + 3.0 * (y2 - y3); + double bx = 3.0 * (x1 + x3 - x2 - x2); + double by = 3.0 * (y1 + y3 - y2 - y2); + double cx = 3.0 * (x2 - x1); + double cy = 3.0 * (y2 - y1); + + double dx1 = step * (step * (step * ax + bx) + cx); + double dy1 = step * (step * (step * ay + by) + cy); + double dx2 = step * (step * (step * ax * 6.0 + bx * 2.0)); + double dy2 = step * (step * (step * ay * 6.0 + by * 2.0)); + double dx3 = step * (step * (step * ax * 6.0)); + double dy3 = step * (step * (step * ay * 6.0)); + double vx = x1; + double vy = y1; + + valSize = n; + values = new double[valSize]; + double pvx = vx; + double pvy = vy; + length = 0.0; + for (int i = 0; i < n; i++) { + vx += dx1; + vy += dy1; + dx1 += dx2; + dy1 += dy2; + dx2 += dx3; + dy2 += dy3; + double lx = vx - pvx; + double ly = vy - pvy; + values[i] = Math.sqrt(lx * lx + ly * ly); + length += values[i]; + pvx = vx; + pvy = vy; + } + + valPos = 0; + curLen = 0.0; + prevLen = 0.0; + } + + @Override + double getNext(double dashPos) { + double t = 2.0; + while (curLen <= dashPos && valPos < valSize) { + prevLen = curLen; + curLen += lastLen = values[valPos++]; + } + if (curLen > dashPos) { + t = (valPos - 1 + (dashPos - prevLen) / lastLen) * step; + } + return t; + } + + } + + /** + * The length. + */ + double length; + + /** + * Gets the next. + * + * @param dashPos + * the dash pos. + * @return the next. + */ + abstract double getNext(double dashPos); + + } + + /** + * BufferedPath class provides work path storing and processing. + */ + static class BufferedPath { + + /** + * The Constant bufCapacity. + */ + private static final int bufCapacity = 10; + + /** + * The point shift. + */ + static int pointShift[] = { + 2, // MOVETO + 2, // LINETO + 4, // QUADTO + 6, // CUBICTO + 0 + }; // CLOSE + + /** + * The types. + */ + byte[] types; + + /** + * The points. + */ + float[] points; + + /** + * The type size. + */ + int typeSize; + + /** + * The point size. + */ + int pointSize; + + /** + * The x last. + */ + float xLast; + + /** + * The y last. + */ + float yLast; + + /** + * The x move. + */ + float xMove; + + /** + * The y move. + */ + float yMove; + + /** + * Instantiates a new buffered path. + */ + public BufferedPath() { + types = new byte[bufCapacity]; + points = new float[bufCapacity * 2]; + } + + /** + * Check buf. + * + * @param typeCount + * the type count. + * @param pointCount + * the point count. + */ + void checkBuf(int typeCount, int pointCount) { + if (typeSize + typeCount > types.length) { + byte tmp[] = new byte[typeSize + Math.max(bufCapacity, typeCount)]; + System.arraycopy(types, 0, tmp, 0, typeSize); + types = tmp; + } + if (pointSize + pointCount > points.length) { + float tmp[] = new float[pointSize + Math.max(bufCapacity * 2, pointCount)]; + System.arraycopy(points, 0, tmp, 0, pointSize); + points = tmp; + } + } + + /** + * Checks if is empty. + * + * @return true, if is empty. + */ + boolean isEmpty() { + return typeSize == 0; + } + + /** + * Clean. + */ + void clean() { + typeSize = 0; + pointSize = 0; + } + + /** + * Move to. + * + * @param x + * the x. + * @param y + * the y. + */ + void moveTo(double x, double y) { + checkBuf(1, 2); + types[typeSize++] = PathIterator.SEG_MOVETO; + points[pointSize++] = xMove = (float)x; + points[pointSize++] = yMove = (float)y; + } + + /** + * Line to. + * + * @param x + * the x. + * @param y + * the y. + */ + void lineTo(double x, double y) { + checkBuf(1, 2); + types[typeSize++] = PathIterator.SEG_LINETO; + points[pointSize++] = xLast = (float)x; + points[pointSize++] = yLast = (float)y; + } + + /** + * Quad to. + * + * @param x1 + * the x1. + * @param y1 + * the y1. + * @param x2 + * the x2. + * @param y2 + * the y2. + */ + void quadTo(double x1, double y1, double x2, double y2) { + checkBuf(1, 4); + types[typeSize++] = PathIterator.SEG_QUADTO; + points[pointSize++] = (float)x1; + points[pointSize++] = (float)y1; + points[pointSize++] = xLast = (float)x2; + points[pointSize++] = yLast = (float)y2; + } + + /** + * Cubic to. + * + * @param x1 + * the x1. + * @param y1 + * the y1. + * @param x2 + * the x2. + * @param y2 + * the y2. + * @param x3 + * the x3. + * @param y3 + * the y3. + */ + void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { + checkBuf(1, 6); + types[typeSize++] = PathIterator.SEG_CUBICTO; + points[pointSize++] = (float)x1; + points[pointSize++] = (float)y1; + points[pointSize++] = (float)x2; + points[pointSize++] = (float)y2; + points[pointSize++] = xLast = (float)x3; + points[pointSize++] = yLast = (float)y3; + } + + /** + * Close path. + */ + void closePath() { + checkBuf(1, 0); + types[typeSize++] = PathIterator.SEG_CLOSE; + } + + /** + * Sets the last. + * + * @param x + * the x. + * @param y + * the y. + */ + void setLast(double x, double y) { + points[pointSize - 2] = xLast = (float)x; + points[pointSize - 1] = yLast = (float)y; + } + + /** + * Append. + * + * @param p + * the p. + */ + void append(BufferedPath p) { + checkBuf(p.typeSize, p.pointSize); + System.arraycopy(p.points, 0, points, pointSize, p.pointSize); + System.arraycopy(p.types, 0, types, typeSize, p.typeSize); + pointSize += p.pointSize; + typeSize += p.typeSize; + xLast = points[pointSize - 2]; + yLast = points[pointSize - 1]; + } + + /** + * Append reverse. + * + * @param p + * the p. + */ + void appendReverse(BufferedPath p) { + checkBuf(p.typeSize, p.pointSize); + // Skip last point, beacause it's the first point of the second path + for (int i = p.pointSize - 2; i >= 0; i -= 2) { + points[pointSize++] = p.points[i + 0]; + points[pointSize++] = p.points[i + 1]; + } + // Skip first type, beacuse it's always MOVETO + int closeIndex = 0; + for (int i = p.typeSize - 1; i >= 0; i--) { + byte type = p.types[i]; + if (type == PathIterator.SEG_MOVETO) { + types[closeIndex] = PathIterator.SEG_MOVETO; + types[typeSize++] = PathIterator.SEG_CLOSE; + } else { + if (type == PathIterator.SEG_CLOSE) { + closeIndex = typeSize; + } + types[typeSize++] = type; + } + } + xLast = points[pointSize - 2]; + yLast = points[pointSize - 1]; + } + + /** + * Join. + * + * @param p + * the p. + */ + void join(BufferedPath p) { + // Skip MOVETO + checkBuf(p.typeSize - 1, p.pointSize - 2); + System.arraycopy(p.points, 2, points, pointSize, p.pointSize - 2); + System.arraycopy(p.types, 1, types, typeSize, p.typeSize - 1); + pointSize += p.pointSize - 2; + typeSize += p.typeSize - 1; + xLast = points[pointSize - 2]; + yLast = points[pointSize - 1]; + } + + /** + * Combine. + * + * @param p + * the p. + */ + void combine(BufferedPath p) { + checkBuf(p.typeSize - 1, p.pointSize - 2); + // Skip last point, beacause it's the first point of the second path + for (int i = p.pointSize - 4; i >= 0; i -= 2) { + points[pointSize++] = p.points[i + 0]; + points[pointSize++] = p.points[i + 1]; + } + // Skip first type, beacuse it's always MOVETO + for (int i = p.typeSize - 1; i >= 1; i--) { + types[typeSize++] = p.types[i]; + } + xLast = points[pointSize - 2]; + yLast = points[pointSize - 1]; + } + + /** + * Creates the general path. + * + * @return the general path. + */ + GeneralPath createGeneralPath() { + GeneralPath p = new GeneralPath(); + int j = 0; + for (int i = 0; i < typeSize; i++) { + int type = types[i]; + switch (type) { + case PathIterator.SEG_MOVETO: + p.moveTo(points[j], points[j + 1]); + break; + case PathIterator.SEG_LINETO: + p.lineTo(points[j], points[j + 1]); + break; + case PathIterator.SEG_QUADTO: + p.quadTo(points[j], points[j + 1], points[j + 2], points[j + 3]); + break; + case PathIterator.SEG_CUBICTO: + p.curveTo(points[j], points[j + 1], points[j + 2], points[j + 3], + points[j + 4], points[j + 5]); + break; + case PathIterator.SEG_CLOSE: + p.closePath(); + break; + } + j += pointShift[type]; + } + return p; + } + + } + +} diff --git a/awt/java/awt/BufferCapabilities.java b/awt/java/awt/BufferCapabilities.java new file mode 100644 index 000000000..cd5fe7b1c --- /dev/null +++ b/awt/java/awt/BufferCapabilities.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +/** + * The BufferCapabilities class represents the capabilities and other properties + * of the image buffers. + * + * @since Android 1.0 + */ +public class BufferCapabilities implements Cloneable { + + /** + * The front buffer capabilities. + */ + private final ImageCapabilities frontBufferCapabilities; + + /** + * The back buffer capabilities. + */ + private final ImageCapabilities backBufferCapabilities; + + /** + * The flip contents. + */ + private final FlipContents flipContents; + + /** + * Instantiates a new BufferCapabilities object. + * + * @param frontBufferCapabilities + * the front buffer capabilities, can not be null. + * @param backBufferCapabilities + * the the back and intermediate buffers capabilities, can not be + * null. + * @param flipContents + * the back buffer contents after page flipping, null if page + * flipping is not used. + */ + public BufferCapabilities(ImageCapabilities frontBufferCapabilities, + ImageCapabilities backBufferCapabilities, FlipContents flipContents) { + if (frontBufferCapabilities == null || backBufferCapabilities == null) { + throw new IllegalArgumentException(); + } + + this.frontBufferCapabilities = frontBufferCapabilities; + this.backBufferCapabilities = backBufferCapabilities; + this.flipContents = flipContents; + } + + /** + * Returns a copy of the BufferCapabilities object. + * + * @return a copy of the BufferCapabilities object. + */ + @Override + public Object clone() { + return new BufferCapabilities(frontBufferCapabilities, backBufferCapabilities, flipContents); + } + + /** + * Gets the image capabilities of the front buffer. + * + * @return the ImageCapabilities object represented capabilities of the + * front buffer. + */ + public ImageCapabilities getFrontBufferCapabilities() { + return frontBufferCapabilities; + } + + /** + * Gets the image capabilities of the back buffer. + * + * @return the ImageCapabilities object represented capabilities of the back + * buffer. + */ + public ImageCapabilities getBackBufferCapabilities() { + return backBufferCapabilities; + } + + /** + * Gets the flip contents of the back buffer after page-flipping. + * + * @return the FlipContents of the back buffer after page-flipping. + */ + public FlipContents getFlipContents() { + return flipContents; + } + + /** + * Checks if the buffer strategy uses page flipping. + * + * @return true, if the buffer strategy uses page flipping, false otherwise. + */ + public boolean isPageFlipping() { + return flipContents != null; + } + + /** + * Checks if page flipping is only available in full-screen mode. + * + * @return true, if page flipping is only available in full-screen mode, + * false otherwise. + */ + public boolean isFullScreenRequired() { + return false; + } + + /** + * Checks if page flipping can be performed using more than two buffers. + * + * @return true, if page flipping can be performed using more than two + * buffers, false otherwise. + */ + public boolean isMultiBufferAvailable() { + return false; + } + + /** + * The FlipContents class represents a set of possible back buffer contents + * after page-flipping. + * + * @since Android 1.0 + */ + public static final class FlipContents { + + /** + * The back buffered contents are cleared with the background color + * after flipping. + */ + public static final FlipContents BACKGROUND = new FlipContents(); + + /** + * The back buffered contents are copied to the front buffer before + * flipping. + */ + public static final FlipContents COPIED = new FlipContents(); + + /** + * The back buffer contents are the prior contents of the front buffer. + */ + public static final FlipContents PRIOR = new FlipContents(); + + /** + * The back buffer contents are undefined after flipping + */ + public static final FlipContents UNDEFINED = new FlipContents(); + + /** + * Instantiates a new flip contents. + */ + private FlipContents() { + + } + + /** + * Returns the hash code of the FlipContents object. + * + * @return the hash code of the FlipContents object. + */ + @Override + public int hashCode() { + return super.hashCode(); + } + + /** + * Returns the String representation of the FlipContents object. + * + * @return the string + */ + @Override + public String toString() { + return super.toString(); + } + } +} diff --git a/awt/java/awt/Color.java b/awt/java/awt/Color.java new file mode 100644 index 000000000..93c532d78 --- /dev/null +++ b/awt/java/awt/Color.java @@ -0,0 +1,990 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package java.awt; + +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import java.awt.image.DataBufferInt; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.io.Serializable; +import java.util.Arrays; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Color class defines colors in the default sRGB color space or in the + * specified ColorSpace. Every Color contains alpha value. The alpha value + * defines the transparency of a color and can be represented by a float value + * in the range 0.0 - 1.0 or 0 - 255. + * + * @since Android 1.0 + */ +public class Color implements Paint, Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 118526816881161077L; + + /* + * The values of the following colors are based on 1.5 release behavior + * which can be revealed using the following or similar code: Color c = + * Color.white; System.out.println(c); + */ + + /** + * The color white. + */ + public static final Color white = new Color(255, 255, 255); + + /** + * The color white. + */ + public static final Color WHITE = white; + + /** + * The color light gray. + */ + public static final Color lightGray = new Color(192, 192, 192); + + /** + * The color light gray. + */ + public static final Color LIGHT_GRAY = lightGray; + + /** + * The color gray. + */ + public static final Color gray = new Color(128, 128, 128); + + /** + * The color gray. + */ + public static final Color GRAY = gray; + + /** + * The color dark gray. + */ + public static final Color darkGray = new Color(64, 64, 64); + + /** + * The color dark gray. + */ + public static final Color DARK_GRAY = darkGray; + + /** + * The color black. + */ + public static final Color black = new Color(0, 0, 0); + + /** + * The color black. + */ + public static final Color BLACK = black; + + /** + * The color red. + */ + public static final Color red = new Color(255, 0, 0); + + /** + * The color red. + */ + public static final Color RED = red; + + /** + * The color pink. + */ + public static final Color pink = new Color(255, 175, 175); + + /** + * The color pink. + */ + public static final Color PINK = pink; + + /** + * The color orange. + */ + public static final Color orange = new Color(255, 200, 0); + + /** + * The color orange. + */ + public static final Color ORANGE = orange; + + /** + * The color yellow. + */ + public static final Color yellow = new Color(255, 255, 0); + + /** + * The color yellow. + */ + public static final Color YELLOW = yellow; + + /** + * The color green. + */ + public static final Color green = new Color(0, 255, 0); + + /** + * The color green. + */ + public static final Color GREEN = green; + + /** + * The color magenta. + */ + public static final Color magenta = new Color(255, 0, 255); + + /** + * The color magenta. + */ + public static final Color MAGENTA = magenta; + + /** + * The color cyan. + */ + public static final Color cyan = new Color(0, 255, 255); + + /** + * The color cyan. + */ + public static final Color CYAN = cyan; + + /** + * The color blue. + */ + public static final Color blue = new Color(0, 0, 255); + + /** + * The color blue. + */ + public static final Color BLUE = blue; + + /** + * integer RGB value. + */ + int value; + + /** + * Float sRGB value. + */ + private float[] frgbvalue; + + /** + * Color in an arbitrary color space with float components. If + * null, other value should be used. + */ + private float fvalue[]; + + /** + * Float alpha value. If frgbvalue is null, this is not valid data. + */ + private float falpha; + + /** + * The color's color space if applicable. + */ + private ColorSpace cs; + + /* + * The value of the SCALE_FACTOR is based on 1.5 release behavior which can + * be revealed using the following code: Color c = new Color(100, 100, 100); + * Color bc = c.brighter(); System.out.println("Brighter factor: " + + * ((float)c.getRed())/((float)bc.getRed())); Color dc = c.darker(); + * System.out.println("Darker factor: " + + * ((float)dc.getRed())/((float)c.getRed())); The result is the same for + * brighter and darker methods, so we need only one scale factor for both. + */ + /** + * The Constant SCALE_FACTOR. + */ + private static final double SCALE_FACTOR = 0.7; + + /** + * The Constant MIN_SCALABLE. + */ + private static final int MIN_SCALABLE = 3; // should increase when + + // multiplied by SCALE_FACTOR + + /** + * The current paint context. + */ + transient private PaintContext currentPaintContext; + + /** + * Creates a color in the specified ColorSpace, the specified color + * components and the specified alpha. + * + * @param cspace + * the ColorSpace to be used to define the components. + * @param components + * the components. + * @param alpha + * the alpha. + */ + public Color(ColorSpace cspace, float[] components, float alpha) { + int nComps = cspace.getNumComponents(); + float comp; + fvalue = new float[nComps]; + + for (int i = 0; i < nComps; i++) { + comp = components[i]; + if (comp < 0.0f || comp > 1.0f) { + // awt.107=Color parameter outside of expected range: component + // {0}. + throw new IllegalArgumentException(Messages.getString("awt.107", i)); //$NON-NLS-1$ + } + fvalue[i] = components[i]; + } + + if (alpha < 0.0f || alpha > 1.0f) { + // awt.108=Alpha value outside of expected range. + throw new IllegalArgumentException(Messages.getString("awt.108")); //$NON-NLS-1$ + } + falpha = alpha; + + cs = cspace; + + frgbvalue = cs.toRGB(fvalue); + + value = ((int)(frgbvalue[2] * 255 + 0.5)) | (((int)(frgbvalue[1] * 255 + 0.5)) << 8) + | (((int)(frgbvalue[0] * 255 + 0.5)) << 16) | (((int)(falpha * 255 + 0.5)) << 24); + } + + /** + * Instantiates a new sRGB color with the specified combined RGBA value + * consisting of the alpha component in bits 24-31, the red component in + * bits 16-23, the green component in bits 8-15, and the blue component in + * bits 0-7. If the hasalpha argument is false, the alpha has default value + * - 255. + * + * @param rgba + * the RGBA components. + * @param hasAlpha + * the alpha parameter is true if alpha bits are valid, false + * otherwise. + */ + public Color(int rgba, boolean hasAlpha) { + if (!hasAlpha) { + value = rgba | 0xFF000000; + } else { + value = rgba; + } + } + + /** + * Instantiates a new color with the specified red, green, blue and alpha + * components. + * + * @param r + * the red component. + * @param g + * the green component. + * @param b + * the blue component. + * @param a + * the alpha component. + */ + public Color(int r, int g, int b, int a) { + if ((r & 0xFF) != r || (g & 0xFF) != g || (b & 0xFF) != b || (a & 0xFF) != a) { + // awt.109=Color parameter outside of expected range. + throw new IllegalArgumentException(Messages.getString("awt.109")); //$NON-NLS-1$ + } + value = b | (g << 8) | (r << 16) | (a << 24); + } + + /** + * Instantiates a new opaque sRGB color with the specified red, green, and + * blue values. The Alpha component is set to the default - 1.0. + * + * @param r + * the red component. + * @param g + * the green component. + * @param b + * the blue component. + */ + public Color(int r, int g, int b) { + if ((r & 0xFF) != r || (g & 0xFF) != g || (b & 0xFF) != b) { + // awt.109=Color parameter outside of expected range. + throw new IllegalArgumentException(Messages.getString("awt.109")); //$NON-NLS-1$ + } + // 0xFF for alpha channel + value = b | (g << 8) | (r << 16) | 0xFF000000; + } + + /** + * Instantiates a new sRGB color with the specified RGB value consisting of + * the red component in bits 16-23, the green component in bits 8-15, and + * the blue component in bits 0-7. Alpha has default value - 255. + * + * @param rgb + * the RGB components. + */ + public Color(int rgb) { + value = rgb | 0xFF000000; + } + + /** + * Instantiates a new color with the specified red, green, blue and alpha + * components. + * + * @param r + * the red component. + * @param g + * the green component. + * @param b + * the blue component. + * @param a + * the alpha component. + */ + public Color(float r, float g, float b, float a) { + this((int)(r * 255 + 0.5), (int)(g * 255 + 0.5), (int)(b * 255 + 0.5), (int)(a * 255 + 0.5)); + falpha = a; + fvalue = new float[3]; + fvalue[0] = r; + fvalue[1] = g; + fvalue[2] = b; + frgbvalue = fvalue; + } + + /** + * Instantiates a new color with the specified red, green, and blue + * components and default alpha value - 1.0. + * + * @param r + * the red component. + * @param g + * the green component. + * @param b + * the blue component. + */ + public Color(float r, float g, float b) { + this(r, g, b, 1.0f); + } + + public PaintContext createContext(ColorModel cm, Rectangle r, Rectangle2D r2d, + AffineTransform xform, RenderingHints rhs) { + if (currentPaintContext != null) { + return currentPaintContext; + } + currentPaintContext = new Color.ColorPaintContext(value); + return currentPaintContext; + } + + /** + * Returns a string representation of the Color object. + * + * @return the string representation of the Color object. + */ + @Override + public String toString() { + /* + * The format of the string is based on 1.5 release behavior which can + * be revealed using the following code: Color c = new Color(1, 2, 3); + * System.out.println(c); + */ + + return getClass().getName() + "[r=" + getRed() + //$NON-NLS-1$ + ",g=" + getGreen() + //$NON-NLS-1$ + ",b=" + getBlue() + //$NON-NLS-1$ + "]"; //$NON-NLS-1$ + } + + /** + * Compares the specified Object to the Color. + * + * @param obj + * the Object to be compared. + * @return true, if the specified Object is a Color whose value is equal to + * this Color, false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof Color) { + return ((Color)obj).value == this.value; + } + return false; + } + + /** + * Returns a float array containing the color and alpha components of the + * Color in the specified ColorSpace. + * + * @param colorSpace + * the specified ColorSpace. + * @param components + * the results of this method will be written to this float + * array. If null, a float array will be created. + * @return the color and alpha components in a float array. + */ + public float[] getComponents(ColorSpace colorSpace, float[] components) { + int nComps = colorSpace.getNumComponents(); + if (components == null) { + components = new float[nComps + 1]; + } + + getColorComponents(colorSpace, components); + + if (frgbvalue != null) { + components[nComps] = falpha; + } else { + components[nComps] = getAlpha() / 255f; + } + + return components; + } + + /** + * Returns a float array containing the color components of the Color in the + * specified ColorSpace. + * + * @param colorSpace + * the specified ColorSpace. + * @param components + * the results of this method will be written to this float + * array. If null, a float array will be created. + * @return the color components in a float array. + */ + public float[] getColorComponents(ColorSpace colorSpace, float[] components) { + float[] cieXYZComponents = getColorSpace().toCIEXYZ(getColorComponents(null)); + float[] csComponents = colorSpace.fromCIEXYZ(cieXYZComponents); + + if (components == null) { + return csComponents; + } + + for (int i = 0; i < csComponents.length; i++) { + components[i] = csComponents[i]; + } + + return components; + } + + /** + * Gets the ColorSpace of this Color. + * + * @return the ColorSpace object. + */ + public ColorSpace getColorSpace() { + if (cs == null) { + cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); + } + + return cs; + } + + /** + * Creates a new Color which is a darker than this Color according to a + * fixed scale factor. + * + * @return the darker Color. + */ + public Color darker() { + return new Color((int)(getRed() * SCALE_FACTOR), (int)(getGreen() * SCALE_FACTOR), + (int)(getBlue() * SCALE_FACTOR)); + } + + /** + * Creates a new Color which is a brighter than this Color. + * + * @return the brighter Color. + */ + public Color brighter() { + + int r = getRed(); + int b = getBlue(); + int g = getGreen(); + + if (r == 0 && b == 0 && g == 0) { + return new Color(MIN_SCALABLE, MIN_SCALABLE, MIN_SCALABLE); + } + + if (r < MIN_SCALABLE && r != 0) { + r = MIN_SCALABLE; + } else { + r = (int)(r / SCALE_FACTOR); + r = (r > 255) ? 255 : r; + } + + if (b < MIN_SCALABLE && b != 0) { + b = MIN_SCALABLE; + } else { + b = (int)(b / SCALE_FACTOR); + b = (b > 255) ? 255 : b; + } + + if (g < MIN_SCALABLE && g != 0) { + g = MIN_SCALABLE; + } else { + g = (int)(g / SCALE_FACTOR); + g = (g > 255) ? 255 : g; + } + + return new Color(r, g, b); + } + + /** + * Returns a float array containing the color and alpha components of the + * Color in the default sRGB color space. + * + * @param components + * the results of this method will be written to this float + * array. A new float array will be created if this argument is + * null. + * @return the RGB color and alpha components in a float array. + */ + public float[] getRGBComponents(float[] components) { + if (components == null) { + components = new float[4]; + } + + if (frgbvalue != null) { + components[3] = falpha; + } else { + components[3] = getAlpha() / 255f; + } + + getRGBColorComponents(components); + + return components; + } + + /** + * Returns a float array containing the color components of the Color in the + * default sRGB color space. + * + * @param components + * the results of this method will be written to this float + * array. A new float array will be created if this argument is + * null. + * @return the RGB color components in a float array. + */ + public float[] getRGBColorComponents(float[] components) { + if (components == null) { + components = new float[3]; + } + + if (frgbvalue != null) { + components[2] = frgbvalue[2]; + components[1] = frgbvalue[1]; + components[0] = frgbvalue[0]; + } else { + components[2] = getBlue() / 255f; + components[1] = getGreen() / 255f; + components[0] = getRed() / 255f; + } + + return components; + } + + /** + * Returns a float array which contains the color and alpha components of + * the Color in the ColorSpace of the Color. + * + * @param components + * the results of this method will be written to this float + * array. A new float array will be created if this argument is + * null. + * @return the color and alpha components in a float array. + */ + public float[] getComponents(float[] components) { + if (fvalue == null) { + return getRGBComponents(components); + } + + int nColorComps = fvalue.length; + + if (components == null) { + components = new float[nColorComps + 1]; + } + + getColorComponents(components); + + components[nColorComps] = falpha; + + return components; + } + + /** + * Returns a float array which contains the color components of the Color in + * the ColorSpace of the Color. + * + * @param components + * the results of this method will be written to this float + * array. A new float array will be created if this argument is + * null. + * @return the color components in a float array. + */ + public float[] getColorComponents(float[] components) { + if (fvalue == null) { + return getRGBColorComponents(components); + } + + if (components == null) { + components = new float[fvalue.length]; + } + + for (int i = 0; i < fvalue.length; i++) { + components[i] = fvalue[i]; + } + + return components; + } + + /** + * Returns a hash code of this Color object. + * + * @return a hash code of this Color object. + */ + @Override + public int hashCode() { + return value; + } + + public int getTransparency() { + switch (getAlpha()) { + case 0xff: + return Transparency.OPAQUE; + case 0: + return Transparency.BITMASK; + default: + return Transparency.TRANSLUCENT; + } + } + + /** + * Gets the red component of the Color in the range 0-255. + * + * @return the red component of the Color. + */ + public int getRed() { + return (value >> 16) & 0xFF; + } + + /** + * Gets the RGB value that represents the color in the default sRGB + * ColorModel. + * + * @return the RGB color value in the default sRGB ColorModel. + */ + public int getRGB() { + return value; + } + + /** + * Gets the green component of the Color in the range 0-255. + * + * @return the green component of the Color. + */ + public int getGreen() { + return (value >> 8) & 0xFF; + } + + /** + * Gets the blue component of the Color in the range 0-255. + * + * @return the blue component of the Color. + */ + public int getBlue() { + return value & 0xFF; + } + + /** + * Gets the alpha component of the Color in the range 0-255. + * + * @return the alpha component of the Color. + */ + public int getAlpha() { + return (value >> 24) & 0xFF; + } + + /** + * Gets the Color from the specified string, or returns the Color specified + * by the second parameter. + * + * @param nm + * the specified string. + * @param def + * the default Color. + * @return the color from the specified string, or the Color specified by + * the second parameter. + */ + public static Color getColor(String nm, Color def) { + Integer integer = Integer.getInteger(nm); + + if (integer == null) { + return def; + } + + return new Color(integer.intValue()); + } + + /** + * Gets the Color from the specified string, or returns the Color converted + * from the second parameter. + * + * @param nm + * the specified string. + * @param def + * the default Color. + * @return the color from the specified string, or the Color converted from + * the second parameter. + */ + public static Color getColor(String nm, int def) { + Integer integer = Integer.getInteger(nm); + + if (integer == null) { + return new Color(def); + } + + return new Color(integer.intValue()); + } + + /** + * Gets the Color from the specified String. + * + * @param nm + * the specified string. + * @return the Color object, or null. + */ + public static Color getColor(String nm) { + Integer integer = Integer.getInteger(nm); + + if (integer == null) { + return null; + } + + return new Color(integer.intValue()); + } + + /** + * Decodes a String to an integer and returns the specified opaque Color. + * + * @param nm + * the String which represents an opaque color as a 24-bit + * integer. + * @return the Color object from the given String. + * @throws NumberFormatException + * if the specified string can not be converted to an integer. + */ + public static Color decode(String nm) throws NumberFormatException { + Integer integer = Integer.decode(nm); + return new Color(integer.intValue()); + } + + /** + * Gets a Color object using the specified values of the HSB color model. + * + * @param h + * the hue component of the Color. + * @param s + * the saturation of the Color. + * @param b + * the brightness of the Color. + * @return a color object with the specified hue, saturation and brightness + * values. + */ + public static Color getHSBColor(float h, float s, float b) { + return new Color(HSBtoRGB(h, s, b)); + } + + /** + * Converts the Color specified by the RGB model to an equivalent color in + * the HSB model. + * + * @param r + * the red component. + * @param g + * the green component. + * @param b + * the blue component. + * @param hsbvals + * the array of result hue, saturation, brightness values or + * null. + * @return the float array of hue, saturation, brightness values. + */ + public static float[] RGBtoHSB(int r, int g, int b, float[] hsbvals) { + if (hsbvals == null) { + hsbvals = new float[3]; + } + + int V = Math.max(b, Math.max(r, g)); + int temp = Math.min(b, Math.min(r, g)); + + float H, S, B; + + B = V / 255.f; + + if (V == temp) { + H = S = 0; + } else { + S = (V - temp) / ((float)V); + + float Cr = (V - r) / (float)(V - temp); + float Cg = (V - g) / (float)(V - temp); + float Cb = (V - b) / (float)(V - temp); + + if (r == V) { + H = Cb - Cg; + } else if (g == V) { + H = 2 + Cr - Cb; + } else { + H = 4 + Cg - Cr; + } + + H /= 6.f; + if (H < 0) { + H++; + } + } + + hsbvals[0] = H; + hsbvals[1] = S; + hsbvals[2] = B; + + return hsbvals; + } + + /** + * Converts the Color specified by the HSB model to an equivalent color in + * the default RGB model. + * + * @param hue + * the hue component of the Color. + * @param saturation + * the saturation of the Color. + * @param brightness + * the brightness of the Color. + * @return the RGB value of the color with the specified hue, saturation and + * brightness. + */ + public static int HSBtoRGB(float hue, float saturation, float brightness) { + float fr, fg, fb; + + if (saturation == 0) { + fr = fg = fb = brightness; + } else { + float H = (hue - (float)Math.floor(hue)) * 6; + int I = (int)Math.floor(H); + float F = H - I; + float M = brightness * (1 - saturation); + float N = brightness * (1 - saturation * F); + float K = brightness * (1 - saturation * (1 - F)); + + switch (I) { + case 0: + fr = brightness; + fg = K; + fb = M; + break; + case 1: + fr = N; + fg = brightness; + fb = M; + break; + case 2: + fr = M; + fg = brightness; + fb = K; + break; + case 3: + fr = M; + fg = N; + fb = brightness; + break; + case 4: + fr = K; + fg = M; + fb = brightness; + break; + case 5: + fr = brightness; + fg = M; + fb = N; + break; + default: + fr = fb = fg = 0; // impossible, to supress compiler error + } + } + + int r = (int)(fr * 255. + 0.5); + int g = (int)(fg * 255. + 0.5); + int b = (int)(fb * 255. + 0.5); + + return (r << 16) | (g << 8) | b | 0xFF000000; + } + + /** + * The Class ColorPaintContext. + */ + class ColorPaintContext implements PaintContext { + + /** + * The RGB value. + */ + int rgbValue; + + /** + * The saved raster. + */ + WritableRaster savedRaster = null; + + /** + * Instantiates a new color paint context. + * + * @param rgb + * the RGB value. + */ + protected ColorPaintContext(int rgb) { + rgbValue = rgb; + } + + public void dispose() { + savedRaster = null; + } + + public ColorModel getColorModel() { + return ColorModel.getRGBdefault(); + } + + public Raster getRaster(int x, int y, int w, int h) { + if (savedRaster == null || w != savedRaster.getWidth() || h != savedRaster.getHeight()) { + savedRaster = getColorModel().createCompatibleWritableRaster(w, h); + + // Suppose we have here simple INT/RGB color/sample model + DataBufferInt intBuffer = (DataBufferInt)savedRaster.getDataBuffer(); + int rgbValues[] = intBuffer.getData(); + int rgbFillValue = rgbValue; + Arrays.fill(rgbValues, rgbFillValue); + } + + return savedRaster; + } + } +} diff --git a/awt/java/awt/Component.java b/awt/java/awt/Component.java new file mode 100644 index 000000000..c52a9f472 --- /dev/null +++ b/awt/java/awt/Component.java @@ -0,0 +1,6020 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.awt; + +//import java.awt.dnd.DropTarget; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; +import java.awt.event.InputMethodEvent; +import java.awt.event.InputMethodListener; +import java.awt.event.InvocationEvent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.event.PaintEvent; +import java.awt.event.WindowEvent; +import java.awt.im.InputContext; +import java.awt.im.InputMethodRequests; +import java.awt.image.BufferStrategy; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.VolatileImage; +import java.awt.image.WritableRaster; +import java.awt.peer.ComponentPeer; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EventListener; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +//???AWT +//import javax.accessibility.Accessible; +//import javax.accessibility.AccessibleComponent; +//import javax.accessibility.AccessibleContext; +//import javax.accessibility.AccessibleRole; +//import javax.accessibility.AccessibleState; +//import javax.accessibility.AccessibleStateSet; + +import org.apache.harmony.awt.ClipRegion; //import org.apache.harmony.awt.FieldsAccessor; +import org.apache.harmony.awt.gl.MultiRectArea; +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.awt.state.State; //import org.apache.harmony.awt.text.TextFieldKit; +//import org.apache.harmony.awt.text.TextKit; +import org.apache.harmony.awt.wtk.NativeWindow; +import org.apache.harmony.luni.util.NotImplementedException; + +/** + * The abstract Component class specifies an object with a graphical + * representation that can be displayed on the screen and that can interact with + * the user (for example: scrollbars, buttons, checkboxes). + * + * @since Android 1.0 + */ +public abstract class Component implements ImageObserver, MenuContainer, Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -7644114512714619750L; + + /** + * The Constant TOP_ALIGNMENT indicates the top alignment of the component. + */ + public static final float TOP_ALIGNMENT = 0.0f; + + /** + * The Constant CENTER_ALIGNMENT indicates the center alignment of the + * component. + */ + public static final float CENTER_ALIGNMENT = 0.5f; + + /** + * The Constant BOTTOM_ALIGNMENT indicates the bottom alignment of the + * component. + */ + public static final float BOTTOM_ALIGNMENT = 1.0f; + + /** + * The Constant LEFT_ALIGNMENT indicates the left alignment of the + * component. + */ + public static final float LEFT_ALIGNMENT = 0.0f; + + /** + * The Constant RIGHT_ALIGNMENT indicates the right alignment of the + * component. + */ + public static final float RIGHT_ALIGNMENT = 1.0f; + + /** + * The Constant childClassesFlags. + */ + private static final Hashtable, Boolean> childClassesFlags = new Hashtable, Boolean>(); + + /** + * The Constant peer. + */ + private static final ComponentPeer peer = new ComponentPeer() { + }; + + /** + * The Constant incrementalImageUpdate. + */ + private static final boolean incrementalImageUpdate; + + /** + * The toolkit. + */ + final transient Toolkit toolkit = Toolkit.getDefaultToolkit(); + + // ???AWT + /* + * protected abstract class AccessibleAWTComponent extends AccessibleContext + * implements Serializable, AccessibleComponent { private static final long + * serialVersionUID = 642321655757800191L; protected class + * AccessibleAWTComponentHandler implements ComponentListener { protected + * AccessibleAWTComponentHandler() { } public void + * componentHidden(ComponentEvent e) { if (behaviour.isLightweight()) { + * return; } firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, + * AccessibleState.VISIBLE, null); } public void + * componentMoved(ComponentEvent e) { } public void + * componentResized(ComponentEvent e) { } public void + * componentShown(ComponentEvent e) { if (behaviour.isLightweight()) { + * return; } firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, + * null, AccessibleState.VISIBLE); } } protected class + * AccessibleAWTFocusHandler implements FocusListener { public void + * focusGained(FocusEvent e) { if (behaviour.isLightweight()) { return; } + * firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null, + * AccessibleState.FOCUSED); } public void focusLost(FocusEvent e) { if + * (behaviour.isLightweight()) { return; } + * firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, + * AccessibleState.FOCUSED, null); } } protected ComponentListener + * accessibleAWTComponentHandler; protected FocusListener + * accessibleAWTFocusHandler; + */ + /* + * Number of registered property change listeners. + */ + /* + * int listenersCount; public void addFocusListener(FocusListener l) { + * Component.this.addFocusListener(l); } + * @Override public void addPropertyChangeListener(PropertyChangeListener + * listener) { toolkit.lockAWT(); try { + * super.addPropertyChangeListener(listener); listenersCount++; if + * (accessibleAWTComponentHandler == null) { accessibleAWTComponentHandler = + * new AccessibleAWTComponentHandler(); + * Component.this.addComponentListener(accessibleAWTComponentHandler); } if + * (accessibleAWTFocusHandler == null) { accessibleAWTFocusHandler = new + * AccessibleAWTFocusHandler(); + * Component.this.addFocusListener(accessibleAWTFocusHandler); } } finally { + * toolkit.unlockAWT(); } } public boolean contains(Point p) { + * toolkit.lockAWT(); try { return Component.this.contains(p); } finally { + * toolkit.unlockAWT(); } } public Accessible getAccessibleAt(Point arg0) { + * toolkit.lockAWT(); try { return null; } finally { toolkit.unlockAWT(); } + * } public Color getBackground() { toolkit.lockAWT(); try { return + * Component.this.getBackground(); } finally { toolkit.unlockAWT(); } } + * public Rectangle getBounds() { toolkit.lockAWT(); try { return + * Component.this.getBounds(); } finally { toolkit.unlockAWT(); } } public + * Cursor getCursor() { toolkit.lockAWT(); try { return + * Component.this.getCursor(); } finally { toolkit.unlockAWT(); } } public + * Font getFont() { toolkit.lockAWT(); try { return + * Component.this.getFont(); } finally { toolkit.unlockAWT(); } } public + * FontMetrics getFontMetrics(Font f) { toolkit.lockAWT(); try { return + * Component.this.getFontMetrics(f); } finally { toolkit.unlockAWT(); } } + * public Color getForeground() { toolkit.lockAWT(); try { return + * Component.this.getForeground(); } finally { toolkit.unlockAWT(); } } + * public Point getLocation() { toolkit.lockAWT(); try { return + * Component.this.getLocation(); } finally { toolkit.unlockAWT(); } } public + * Point getLocationOnScreen() { toolkit.lockAWT(); try { return + * Component.this.getLocationOnScreen(); } finally { toolkit.unlockAWT(); } + * } public Dimension getSize() { toolkit.lockAWT(); try { return + * Component.this.getSize(); } finally { toolkit.unlockAWT(); } } public + * boolean isEnabled() { toolkit.lockAWT(); try { return + * Component.this.isEnabled(); } finally { toolkit.unlockAWT(); } } public + * boolean isFocusTraversable() { toolkit.lockAWT(); try { return + * Component.this.isFocusTraversable(); } finally { toolkit.unlockAWT(); } } + * public boolean isShowing() { toolkit.lockAWT(); try { return + * Component.this.isShowing(); } finally { toolkit.unlockAWT(); } } public + * boolean isVisible() { toolkit.lockAWT(); try { return + * Component.this.isVisible(); } finally { toolkit.unlockAWT(); } } public + * void removeFocusListener(FocusListener l) { + * Component.this.removeFocusListener(l); } + * @Override public void removePropertyChangeListener(PropertyChangeListener + * listener) { toolkit.lockAWT(); try { + * super.removePropertyChangeListener(listener); listenersCount--; if + * (listenersCount > 0) { return; } // if there are no more listeners, + * remove handlers: + * Component.this.removeFocusListener(accessibleAWTFocusHandler); + * Component.this.removeComponentListener(accessibleAWTComponentHandler); + * accessibleAWTComponentHandler = null; accessibleAWTFocusHandler = null; } + * finally { toolkit.unlockAWT(); } } public void requestFocus() { + * toolkit.lockAWT(); try { Component.this.requestFocus(); } finally { + * toolkit.unlockAWT(); } } public void setBackground(Color color) { + * toolkit.lockAWT(); try { Component.this.setBackground(color); } finally { + * toolkit.unlockAWT(); } } public void setBounds(Rectangle r) { + * toolkit.lockAWT(); try { Component.this.setBounds(r); } finally { + * toolkit.unlockAWT(); } } public void setCursor(Cursor cursor) { + * toolkit.lockAWT(); try { Component.this.setCursor(cursor); } finally { + * toolkit.unlockAWT(); } } public void setEnabled(boolean enabled) { + * toolkit.lockAWT(); try { Component.this.setEnabled(enabled); } finally { + * toolkit.unlockAWT(); } } public void setFont(Font f) { toolkit.lockAWT(); + * try { Component.this.setFont(f); } finally { toolkit.unlockAWT(); } } + * public void setForeground(Color color) { toolkit.lockAWT(); try { + * Component.this.setForeground(color); } finally { toolkit.unlockAWT(); } } + * public void setLocation(Point p) { toolkit.lockAWT(); try { + * Component.this.setLocation(p); } finally { toolkit.unlockAWT(); } } + * public void setSize(Dimension size) { toolkit.lockAWT(); try { + * Component.this.setSize(size); } finally { toolkit.unlockAWT(); } } public + * void setVisible(boolean visible) { toolkit.lockAWT(); try { + * Component.this.setVisible(visible); } finally { toolkit.unlockAWT(); } } + * @Override public Accessible getAccessibleParent() { toolkit.lockAWT(); + * try { Accessible aParent = super.getAccessibleParent(); if (aParent != + * null) { return aParent; } Container parent = getParent(); return (parent + * instanceof Accessible ? (Accessible) parent : null); } finally { + * toolkit.unlockAWT(); } } + * @Override public Accessible getAccessibleChild(int i) { + * toolkit.lockAWT(); try { return null; } finally { toolkit.unlockAWT(); } + * } + * @Override public int getAccessibleChildrenCount() { toolkit.lockAWT(); + * try { return 0; } finally { toolkit.unlockAWT(); } } + * @Override public AccessibleComponent getAccessibleComponent() { return + * this; } + * @Override public String getAccessibleDescription() { return + * super.getAccessibleDescription(); // why override? } + * @Override public int getAccessibleIndexInParent() { toolkit.lockAWT(); + * try { if (getAccessibleParent() == null) { return -1; } int count = 0; + * Container parent = getParent(); for (int i = 0; i < + * parent.getComponentCount(); i++) { Component aComp = + * parent.getComponent(i); if (aComp instanceof Accessible) { if (aComp == + * Component.this) { return count; } ++count; } } return -1; } finally { + * toolkit.unlockAWT(); } } + * @Override public AccessibleRole getAccessibleRole() { toolkit.lockAWT(); + * try { return AccessibleRole.AWT_COMPONENT; } finally { + * toolkit.unlockAWT(); } } + * @Override public AccessibleStateSet getAccessibleStateSet() { + * toolkit.lockAWT(); try { AccessibleStateSet set = new + * AccessibleStateSet(); if (isEnabled()) { + * set.add(AccessibleState.ENABLED); } if (isFocusable()) { + * set.add(AccessibleState.FOCUSABLE); } if (hasFocus()) { + * set.add(AccessibleState.FOCUSED); } if (isOpaque()) { + * set.add(AccessibleState.OPAQUE); } if (isShowing()) { + * set.add(AccessibleState.SHOWING); } if (isVisible()) { + * set.add(AccessibleState.VISIBLE); } return set; } finally { + * toolkit.unlockAWT(); } } + * @Override public Locale getLocale() throws IllegalComponentStateException + * { toolkit.lockAWT(); try { return Component.this.getLocale(); } finally { + * toolkit.unlockAWT(); } } } + */ + /** + * The BltBufferStrategy class provides opportunity of blitting offscreen + * surfaces to a component. For more information on blitting, see Bit blit. + * + * @since Android 1.0 + */ + protected class BltBufferStrategy extends BufferStrategy { + + /** + * The back buffers. + */ + protected VolatileImage[] backBuffers; + + /** + * The caps. + */ + protected BufferCapabilities caps; + + /** + * The width. + */ + protected int width; + + /** + * The height. + */ + protected int height; + + /** + * The validated contents. + */ + protected boolean validatedContents; + + /** + * Instantiates a new BltBufferStrategy buffer strategy. + * + * @param numBuffers + * the number of buffers. + * @param caps + * the BufferCapabilities. + * @throws NotImplementedException + * the not implemented exception. + */ + protected BltBufferStrategy(int numBuffers, BufferCapabilities caps) + throws org.apache.harmony.luni.util.NotImplementedException { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + } + + /** + * Returns true if the drawing buffer has been lost since the last call + * to getDrawGraphics. + * + * @return true if the drawing buffer has been lost since the last call + * to getDrawGraphics, false otherwise. + * @see java.awt.image.BufferStrategy#contentsLost() + */ + @Override + public boolean contentsLost() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + return false; + } + + /** + * Returns true if the drawing buffer has been restored from a lost + * state and reinitialized to the default background color. + * + * @return true if the drawing buffer has been restored from a lost + * state and reinitialized to the default background color, + * false otherwise. + * @see java.awt.image.BufferStrategy#contentsRestored() + */ + @Override + public boolean contentsRestored() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + return false; + } + + /** + * Creates the back buffers. + * + * @param numBuffers + * the number of buffers. + */ + protected void createBackBuffers(int numBuffers) { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + } + + /** + * Returns the BufferCapabilities of the buffer strategy. + * + * @return the BufferCapabilities. + * @see java.awt.image.BufferStrategy#getCapabilities() + */ + @Override + public BufferCapabilities getCapabilities() { + return (BufferCapabilities)caps.clone(); + } + + /** + * Gets Graphics of current buffer strategy. + * + * @return the Graphics of current buffer strategy. + * @see java.awt.image.BufferStrategy#getDrawGraphics() + */ + @Override + public Graphics getDrawGraphics() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + return null; + } + + /** + * Revalidates the lost drawing buffer. + */ + protected void revalidate() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + } + + /** + * Shows the next available buffer. + * + * @see java.awt.image.BufferStrategy#show() + */ + @Override + public void show() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + } + } + + /** + * The FlipBufferStrategy class is for flipping buffers on a component. + * + * @since Android 1.0 + */ + protected class FlipBufferStrategy extends BufferStrategy { + + /** + * The Buffer Capabilities. + */ + protected BufferCapabilities caps; + + /** + * The drawing buffer. + */ + protected Image drawBuffer; + + /** + * The drawing VolatileImage buffer. + */ + protected VolatileImage drawVBuffer; + + /** + * The number of buffers. + */ + protected int numBuffers; + + /** + * The validated contents indicates if the drawing buffer is restored + * from lost state. + */ + protected boolean validatedContents; + + /** + * Instantiates a new flip buffer strategy. + * + * @param numBuffers + * the number of buffers. + * @param caps + * the BufferCapabilities. + * @throws AWTException + * if the capabilities supplied could not be supported or + * met. + */ + protected FlipBufferStrategy(int numBuffers, BufferCapabilities caps) throws AWTException { + // ???AWT + /* + * if (!(Component.this instanceof Window) && !(Component.this + * instanceof Canvas)) { // awt.14B=Only Canvas or Window is allowed + * throw new ClassCastException(Messages.getString("awt.14B")); + * //$NON-NLS-1$ } + */ + // TODO: throw new AWTException("Capabilities are not supported"); + this.numBuffers = numBuffers; + this.caps = (BufferCapabilities)caps.clone(); + } + + /** + * Returns true if the drawing buffer has been lost since the last call + * to getDrawGraphics. + * + * @return true if the drawing buffer has been lost since the last call + * to getDrawGraphics, false otherwise. + * @see java.awt.image.BufferStrategy#contentsLost() + */ + @Override + public boolean contentsLost() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + return false; + } + + /** + * Returns true if the drawing buffer has been restored from a lost + * state and reinitialized to the default background color. + * + * @return true if the drawing buffer has been restored from a lost + * state and reinitialized to the default background color, + * false otherwise. + * @see java.awt.image.BufferStrategy#contentsRestored() + */ + @Override + public boolean contentsRestored() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + return false; + } + + /** + * Creates flipping buffers with the specified buffer capabilities. + * + * @param numBuffers + * the number of buffers. + * @param caps + * the BufferCapabilities. + * @throws AWTException + * if the capabilities could not be supported or met. + */ + protected void createBuffers(int numBuffers, BufferCapabilities caps) throws AWTException { + if (numBuffers < 2) { + // awt.14C=Number of buffers must be greater than one + throw new IllegalArgumentException(Messages.getString("awt.14C")); //$NON-NLS-1$ + } + if (!caps.isPageFlipping()) { + // awt.14D=Buffer capabilities should support flipping + throw new IllegalArgumentException(Messages.getString("awt.14D")); //$NON-NLS-1$ + } + if (!Component.this.behaviour.isDisplayable()) { + // awt.14E=Component should be displayable + throw new IllegalStateException(Messages.getString("awt.14E")); //$NON-NLS-1$ + } + // TODO: throw new AWTException("Capabilities are not supported"); + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + } + + /** + * Destroy buffers. + */ + protected void destroyBuffers() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + } + + /** + * Flips the contents of the back buffer to the front buffer. + * + * @param flipAction + * the flip action. + */ + protected void flip(BufferCapabilities.FlipContents flipAction) { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + } + + /** + * Gets the back buffer as Image. + * + * @return the back buffer as Image. + */ + protected Image getBackBuffer() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + return null; + } + + /** + * Returns the BufferCapabilities of the buffer strategy. + * + * @return the BufferCapabilities. + * @see java.awt.image.BufferStrategy#getCapabilities() + */ + @Override + public BufferCapabilities getCapabilities() { + return (BufferCapabilities)caps.clone(); + } + + /** + * Gets Graphics of current buffer strategy. + * + * @return the Graphics of current buffer strategy. + * @see java.awt.image.BufferStrategy#getDrawGraphics() + */ + @Override + public Graphics getDrawGraphics() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + return null; + } + + /** + * Revalidates the lost drawing buffer. + */ + protected void revalidate() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + } + + /** + * Shows the next available buffer. + * + * @see java.awt.image.BufferStrategy#show() + */ + @Override + public void show() { + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + } + } + + /** + * The internal component's state utilized by the visual theme. + */ + class ComponentState implements State { + + /** + * The default minimum size. + */ + private Dimension defaultMinimumSize = new Dimension(); + + /** + * Checks if the component is enabled. + * + * @return true, if the component is enabled. + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Checks if the component is visible. + * + * @return true, if the component is visible. + */ + public boolean isVisible() { + return visible; + } + + /** + * Checks if is focused. + * + * @return true, if is focused. + */ + public boolean isFocused() { + // ???AWT: return isFocusOwner(); + return false; + } + + /** + * Gets the font. + * + * @return the font. + */ + public Font getFont() { + return Component.this.getFont(); + } + + /** + * Checks if the font has been set. + * + * @return true, if the font has been set. + */ + public boolean isFontSet() { + return font != null; + } + + /** + * Gets the background color. + * + * @return the background color. + */ + public Color getBackground() { + Color c = Component.this.getBackground(); + return (c != null) ? c : getDefaultBackground(); + } + + /** + * Checks if the background is set. + * + * @return true, if the background is set. + */ + public boolean isBackgroundSet() { + return backColor != null; + } + + /** + * Gets the text color. + * + * @return the text color. + */ + public Color getTextColor() { + Color c = getForeground(); + return (c != null) ? c : getDefaultForeground(); + } + + /** + * Checks if the text color is set. + * + * @return true, if the text color is set. + */ + public boolean isTextColorSet() { + return foreColor != null; + } + + /** + * Gets the font metrics. + * + * @return the font metrics. + */ + @SuppressWarnings("deprecation") + public FontMetrics getFontMetrics() { + return toolkit.getFontMetrics(Component.this.getFont()); + } + + /** + * Gets the bounding rectangle. + * + * @return the bounding rectangle. + */ + public Rectangle getBounds() { + return new Rectangle(x, y, w, h); + } + + /** + * Gets the size of the bounding rectangle. + * + * @return the size of the bounding rectangle. + */ + public Dimension getSize() { + return new Dimension(w, h); + } + + /** + * Gets the window id. + * + * @return the window id. + */ + public long getWindowId() { + NativeWindow win = getNativeWindow(); + return (win != null) ? win.getId() : 0; + } + + /** + * Gets the default minimum size. + * + * @return the default minimum size. + */ + public Dimension getDefaultMinimumSize() { + if (defaultMinimumSize == null) { + calculate(); + } + return defaultMinimumSize; + } + + /** + * Sets the default minimum size. + * + * @param size + * the new default minimum size. + */ + public void setDefaultMinimumSize(Dimension size) { + defaultMinimumSize = size; + } + + /** + * Reset the default minimum size to null. + */ + public void reset() { + defaultMinimumSize = null; + } + + /** + * Calculate the default minimum size: to be overridden. + */ + public void calculate() { + // to be overridden + } + } + + // ???AWT: private transient AccessibleContext accessibleContext; + + /** + * The behaviour. + */ + final transient ComponentBehavior behaviour; + + // ???AWT: Container parent; + + /** + * The name. + */ + private String name; + + /** + * The auto name. + */ + private boolean autoName = true; + + /** + * The font. + */ + private Font font; + + /** + * The back color. + */ + private Color backColor; + + /** + * The fore color. + */ + private Color foreColor; + + /** + * The deprecated event handler. + */ + boolean deprecatedEventHandler = true; + + /** + * The enabled events. + */ + private long enabledEvents; + + /** + * The enabled AWT events. + */ + private long enabledAWTEvents; + + /** + * The component listeners. + */ + private final AWTListenerList componentListeners = new AWTListenerList( + this); + + /** + * The focus listeners. + */ + private final AWTListenerList focusListeners = new AWTListenerList( + this); + + /** + * The hierarchy listeners. + */ + private final AWTListenerList hierarchyListeners = new AWTListenerList( + this); + + /** + * The hierarchy bounds listeners. + */ + private final AWTListenerList hierarchyBoundsListeners = new AWTListenerList( + this); + + /** + * The key listeners. + */ + private final AWTListenerList keyListeners = new AWTListenerList(this); + + /** + * The mouse listeners. + */ + private final AWTListenerList mouseListeners = new AWTListenerList( + this); + + /** + * The mouse motion listeners. + */ + private final AWTListenerList mouseMotionListeners = new AWTListenerList( + this); + + /** + * The mouse wheel listeners. + */ + private final AWTListenerList mouseWheelListeners = new AWTListenerList( + this); + + /** + * The input method listeners. + */ + private final AWTListenerList inputMethodListeners = new AWTListenerList( + this); + + /** + * The x. + */ + int x; + + /** + * The y. + */ + int y; + + /** + * The w. + */ + int w; + + /** + * The h. + */ + int h; + + /** + * The maximum size. + */ + private Dimension maximumSize; + + /** + * The minimum size. + */ + private Dimension minimumSize; + + /** + * The preferred size. + */ + private Dimension preferredSize; + + /** + * The bounds mask param. + */ + private int boundsMaskParam; + + /** + * The ignore repaint. + */ + private boolean ignoreRepaint; + + /** + * The enabled. + */ + private boolean enabled = true; + + /** + * The input methods enabled. + */ + private boolean inputMethodsEnabled = true; + + /** + * The dispatch to im. + */ + transient boolean dispatchToIM = true; + + /** + * The focusable. + */ + private boolean focusable = true; // By default, all Components return + + // true from isFocusable() method + /** + * The visible. + */ + boolean visible = true; + + /** + * The called set focusable. + */ + private boolean calledSetFocusable; + + /** + * The overridden is focusable. + */ + private boolean overridenIsFocusable = true; + + /** + * The focus traversal keys enabled. + */ + private boolean focusTraversalKeysEnabled = true; + + /** + * Possible keys are: FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, + * UP_CYCLE_TRAVERSAL_KEYS. + */ + private final Map> traversalKeys = new HashMap>(); + + /** + * The traversal i ds. + */ + int[] traversalIDs; + + /** + * The locale. + */ + private Locale locale; + + /** + * The orientation. + */ + private ComponentOrientation orientation; + + /** + * The property change support. + */ + private PropertyChangeSupport propertyChangeSupport; + + // ???AWT: private ArrayList popups; + + /** + * The coalescer. + */ + private boolean coalescer; + + /** + * The events table. + */ + private Hashtable> eventsTable; + + /** + * Cashed reference used during EventQueue.postEvent() + */ + private LinkedList eventsList; + + /** + * The hierarchy changing counter. + */ + private int hierarchyChangingCounter; + + /** + * The was showing. + */ + private boolean wasShowing; + + /** + * The was displayable. + */ + private boolean wasDisplayable; + + /** + * The cursor. + */ + Cursor cursor; + + // ???AWT: DropTarget dropTarget; + + /** + * The mouse exited expected. + */ + private boolean mouseExitedExpected; + + /** + * The repaint region. + */ + transient MultiRectArea repaintRegion; + + // ???AWT: transient RedrawManager redrawManager; + /** + * The redraw manager. + */ + transient Object redrawManager; + + /** + * The valid. + */ + private boolean valid; + + /** + * The updated images. + */ + private HashMap updatedImages; + + /** + * The lock object for private component's data which don't affect the + * component hierarchy. + */ + private class ComponentLock { + } + + /** + * The component lock. + */ + private final transient Object componentLock = new ComponentLock(); + static { + PrivilegedAction action = new PrivilegedAction() { + public String[] run() { + String properties[] = new String[2]; + properties[0] = System.getProperty("awt.image.redrawrate", "100"); //$NON-NLS-1$ //$NON-NLS-2$ + properties[1] = System.getProperty("awt.image.incrementaldraw", "true"); //$NON-NLS-1$ //$NON-NLS-2$ + return properties; + } + }; + String properties[] = AccessController.doPrivileged(action); + // FIXME: rate is never used, can this code and the get property above + // be removed? + // int rate; + // + // try { + // rate = Integer.decode(properties[0]).intValue(); + // } catch (NumberFormatException e) { + // rate = 100; + // } + incrementalImageUpdate = properties[1].equals("true"); //$NON-NLS-1$ + } + + /** + * Instantiates a new component. + */ + protected Component() { + toolkit.lockAWT(); + try { + orientation = ComponentOrientation.UNKNOWN; + redrawManager = null; + // ???AWT + /* + * traversalIDs = this instanceof Container ? + * KeyboardFocusManager.contTraversalIDs : + * KeyboardFocusManager.compTraversalIDs; for (int element : + * traversalIDs) { traversalKeys.put(new Integer(element), null); } + * behaviour = createBehavior(); + */ + behaviour = null; + + deriveCoalescerFlag(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Determine that the class inherited from Component declares the method + * coalesceEvents(), and put the results to the childClassesFlags map. + */ + private void deriveCoalescerFlag() { + Class thisClass = getClass(); + boolean flag = true; + synchronized (childClassesFlags) { + Boolean flagWrapper = childClassesFlags.get(thisClass); + if (flagWrapper == null) { + Method coalesceMethod = null; + for (Class c = thisClass; c != Component.class; c = c.getSuperclass()) { + try { + coalesceMethod = c.getDeclaredMethod("coalesceEvents", new Class[] { //$NON-NLS-1$ + Class.forName("java.awt.AWTEvent"), //$NON-NLS-1$ + Class.forName("java.awt.AWTEvent")}); //$NON-NLS-1$ + } catch (Exception e) { + } + if (coalesceMethod != null) { + break; + } + } + flag = (coalesceMethod != null); + childClassesFlags.put(thisClass, Boolean.valueOf(flag)); + } else { + flag = flagWrapper.booleanValue(); + } + } + coalescer = flag; + if (flag) { + eventsTable = new Hashtable>(); + } else { + eventsTable = null; + } + } + + /** + * Sets the name of the Component. + * + * @param name + * the new name of the Component. + */ + public void setName(String name) { + String oldName; + toolkit.lockAWT(); + try { + autoName = false; + oldName = this.name; + this.name = name; + } finally { + toolkit.unlockAWT(); + } + firePropertyChange("name", oldName, name); //$NON-NLS-1$ + } + + /** + * Gets the name of this Component. + * + * @return the name of this Component. + */ + public String getName() { + toolkit.lockAWT(); + try { + if ((name == null) && autoName) { + name = autoName(); + } + return name; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Auto name. + * + * @return the string. + */ + String autoName() { + String name = getClass().getName(); + if (name.indexOf("$") != -1) { //$NON-NLS-1$ + return null; + } + // ???AWT + // int number = toolkit.autoNumber.nextComponent++; + int number = 0; + name = name.substring(name.lastIndexOf(".") + 1) + Integer.toString(number); //$NON-NLS-1$ + return name; + } + + /** + * Returns the string representation of the Component. + * + * @return the string representation of the Component. + */ + @Override + public String toString() { + /* + * The format is based on 1.5 release behavior which can be revealed by + * the following code: Component c = new Component(){}; + * c.setVisible(false); System.out.println(c); + */ + toolkit.lockAWT(); + try { + return getClass().getName() + "[" + paramString() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } finally { + toolkit.unlockAWT(); + } + } + + // ???AWT + /* + * public void add(PopupMenu popup) { toolkit.lockAWT(); try { if + * (popup.getParent() == this) { return; } if (popups == null) { popups = + * new ArrayList(); } popup.setParent(this); popups.add(popup); } + * finally { toolkit.unlockAWT(); } } + */ + + /** + * Returns true, if the component contains the specified Point. + * + * @param p + * the Point. + * @return true, if the component contains the specified Point, false + * otherwise. + */ + public boolean contains(Point p) { + toolkit.lockAWT(); + try { + return contains(p.x, p.y); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Returns true, if the component contains the point with the specified + * coordinates. + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @return true, if the component contains the point with the specified + * coordinates, false otherwise. + */ + public boolean contains(int x, int y) { + toolkit.lockAWT(); + try { + return inside(x, y); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Deprecated: replaced by replaced by getSize() method. + * + * @return the dimension. + * @deprecated Replaced by getSize() method. + */ + @Deprecated + public Dimension size() { + toolkit.lockAWT(); + try { + return new Dimension(w, h); + } finally { + toolkit.unlockAWT(); + } + } + + // ???AWT + /* + * public Container getParent() { toolkit.lockAWT(); try { return parent; } + * finally { toolkit.unlockAWT(); } } + */ + + /** + * List. + * + * @param out + * the out. + * @param indent + * the indent + * @return the nearest heavyweight ancestor in hierarchy or + * null if not found. + */ + // ???AWT + /* + * Component getHWAncestor() { return (parent != null ? + * parent.getHWSurface() : null); } + */ + + /** + * @return heavyweight component that is equal to or is a nearest + * heavyweight container of the current component, or + * null if not found. + */ + // ???AWT + /* + * Component getHWSurface() { Component parent; for (parent = this; (parent + * != null) && (parent.isLightweight()); parent = parent .getParent()) { ; } + * return parent; } Window getWindowAncestor() { Component par; for (par = + * this; par != null && !(par instanceof Window); par = par.getParent()) { ; + * } return (Window) par; } + */ + + /** + * To be called by container + */ + // ???AWT + /* + * void setParent(Container parent) { this.parent = parent; + * setRedrawManager(); } void setRedrawManager() { redrawManager = + * getRedrawManager(); } public void remove(MenuComponent menu) { + * toolkit.lockAWT(); try { if (menu.getParent() == this) { + * menu.setParent(null); popups.remove(menu); } } finally { + * toolkit.unlockAWT(); } } + */ + /** + * Prints a list of this component with the specified number of leading + * whitespace characters to the specified PrintStream. + * + * @param out + * the output PrintStream object. + * @param indent + * how many leading whitespace characters to prepend. + */ + public void list(PrintStream out, int indent) { + toolkit.lockAWT(); + try { + out.println(getIndentStr(indent) + this); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Prints a list of this component to the specified PrintWriter. + * + * @param out + * the output PrintWriter object. + */ + public void list(PrintWriter out) { + toolkit.lockAWT(); + try { + list(out, 1); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Prints a list of this component with the specified number of leading + * whitespace characters to the specified PrintWriter. + * + * @param out + * the output PrintWriter object. + * @param indent + * how many leading whitespace characters to prepend. + */ + public void list(PrintWriter out, int indent) { + toolkit.lockAWT(); + try { + out.println(getIndentStr(indent) + this); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets a string composed of the desired number of whitespace characters. + * + * @param indent + * the length of the String to return. + * @return the string composed of the desired number of whitespace + * characters. + */ + String getIndentStr(int indent) { + char[] ind = new char[indent]; + for (int i = 0; i < indent; ind[i++] = ' ') { + ; + } + return new String(ind); + } + + /** + * Prints a list of this component to the specified PrintStream. + * + * @param out + * the output PrintStream object. + */ + public void list(PrintStream out) { + toolkit.lockAWT(); + try { + // default indent = 1 + list(out, 1); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Prints a list of this component to the standard system output stream. + */ + public void list() { + toolkit.lockAWT(); + try { + list(System.out); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Prints this component. + * + * @param g + * the Graphics to be used for painting. + */ + public void print(Graphics g) { + toolkit.lockAWT(); + try { + paint(g); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Prints the component and all of its subcomponents. + * + * @param g + * the Graphics to be used for painting. + */ + public void printAll(Graphics g) { + toolkit.lockAWT(); + try { + paintAll(g); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the size of the Component specified by width and height parameters. + * + * @param width + * the width of the Component. + * @param height + * the height of the Component. + */ + public void setSize(int width, int height) { + toolkit.lockAWT(); + try { + resize(width, height); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the size of the Component specified by Dimension object. + * + * @param d + * the new size of the Component. + */ + public void setSize(Dimension d) { + toolkit.lockAWT(); + try { + resize(d); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Deprecated: replaced by setSize(int, int) method. + * + * @param width + * the width. + * @param height + * the height. + * @deprecated Replaced by setSize(int, int) method. + */ + @Deprecated + public void resize(int width, int height) { + toolkit.lockAWT(); + try { + boundsMaskParam = NativeWindow.BOUNDS_NOMOVE; + setBounds(x, y, width, height); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Deprecated: replaced by setSize(int, int) method. + * + * @param size + * the size. + * @deprecated Replaced by setSize(int, int) method. + */ + @Deprecated + public void resize(Dimension size) { + toolkit.lockAWT(); + try { + setSize(size.width, size.height); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not this component is completely opaque. + * + * @return true, if this component is completely opaque, false by default. + */ + public boolean isOpaque() { + toolkit.lockAWT(); + try { + return behaviour.isOpaque(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Disables. + * + * @deprecated Replaced by setEnabled(boolean) method. + */ + @Deprecated + public void disable() { + toolkit.lockAWT(); + try { + setEnabledImpl(false); + } finally { + toolkit.unlockAWT(); + } + // ???AWT: fireAccessibleStateChange(AccessibleState.ENABLED, false); + } + + /** + * Enables this component. + * + * @deprecated Replaced by setEnabled(boolean) method. + */ + @Deprecated + public void enable() { + toolkit.lockAWT(); + try { + setEnabledImpl(true); + } finally { + toolkit.unlockAWT(); + } + // ???AWT: fireAccessibleStateChange(AccessibleState.ENABLED, true); + } + + /** + * Enables or disable this component. + * + * @param b + * the boolean parameter. + * @deprecated Replaced by setEnabled(boolean) method. + */ + @Deprecated + public void enable(boolean b) { + toolkit.lockAWT(); + try { + if (b) { + enable(); + } else { + disable(); + } + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Stores the location of this component to the specified Point object; + * returns the point of the component's top-left corner. + * + * @param rv + * the Point object where the component's top-left corner + * position will be stored. + * @return the Point which specifies the component's top-left corner. + */ + public Point getLocation(Point rv) { + toolkit.lockAWT(); + try { + if (rv == null) { + rv = new Point(); + } + rv.setLocation(getX(), getY()); + return rv; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the location of this component on the form; returns the point of the + * component's top-left corner. + * + * @return the Point which specifies the component's top-left corner. + */ + public Point getLocation() { + toolkit.lockAWT(); + try { + return location(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the size of this Component. + * + * @return the size of this Component. + */ + public Dimension getSize() { + toolkit.lockAWT(); + try { + return size(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Stores the size of this Component to the specified Dimension object. + * + * @param rv + * the Dimension object where the size of the Component will be + * stored. + * @return the Dimension of this Component. + */ + public Dimension getSize(Dimension rv) { + toolkit.lockAWT(); + try { + if (rv == null) { + rv = new Dimension(); + } + rv.setSize(getWidth(), getHeight()); + return rv; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not this Component is valid. A component is valid if it + * is correctly sized and positioned within its parent container and all its + * children are also valid. + * + * @return true, if the Component is valid, false otherwise. + */ + public boolean isValid() { + toolkit.lockAWT(); + try { + return valid && behaviour.isDisplayable(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Deprecated: replaced by getComponentAt(int, int) method. + * + * @return the Point. + * @deprecated Replaced by getComponentAt(int, int) method. + */ + @Deprecated + public Point location() { + toolkit.lockAWT(); + try { + return new Point(x, y); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Connects this Component to a native screen resource and makes it + * displayable. This method not be called directly by user applications. + */ + public void addNotify() { + toolkit.lockAWT(); + try { + prepare4HierarchyChange(); + behaviour.addNotify(); + // ???AWT + // finishHierarchyChange(this, parent, 0); + // if (dropTarget != null) { + // dropTarget.addNotify(peer); + // } + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Map to display. + * + * @param b + * the b. + */ + void mapToDisplay(boolean b) { + // ???AWT + /* + * if (b && !isDisplayable()) { if ((this instanceof Window) || ((parent + * != null) && parent.isDisplayable())) { addNotify(); } } else if (!b + * && isDisplayable()) { removeNotify(); } + */ + } + + /** + * Gets the toolkit. + * + * @return accessible context specific for particular component. + */ + // ???AWT + /* + * AccessibleContext createAccessibleContext() { return null; } public + * AccessibleContext getAccessibleContext() { toolkit.lockAWT(); try { if + * (accessibleContext == null) { accessibleContext = + * createAccessibleContext(); } return accessibleContext; } finally { + * toolkit.unlockAWT(); } } + */ + + /** + * Gets Toolkit for the current Component. + * + * @return the Toolkit of this Component. + */ + public Toolkit getToolkit() { + return toolkit; + } + + /** + * Gets this component's locking object for AWT component tree and layout + * operations. + * + * @return the tree locking object. + */ + public final Object getTreeLock() { + return toolkit.awtTreeLock; + } + + /** + * Handles the event. Use ActionListener instead of this. + * + * @param evt + * the Event. + * @param what + * the event's key. + * @return true, if successful. + * @deprecated Use ActionListener class for registering event listener. + */ + @Deprecated + public boolean action(Event evt, Object what) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Gets the property change support. + * + * @return the property change support. + */ + private PropertyChangeSupport getPropertyChangeSupport() { + synchronized (componentLock) { + if (propertyChangeSupport == null) { + propertyChangeSupport = new PropertyChangeSupport(this); + } + return propertyChangeSupport; + } + } + + // ???AWT + /* + * public void addPropertyChangeListener(PropertyChangeListener listener) { + * getPropertyChangeSupport().addPropertyChangeListener(listener); } public + * void addPropertyChangeListener(String propertyName, + * PropertyChangeListener listener) { + * getPropertyChangeSupport().addPropertyChangeListener(propertyName, + * listener); } public void applyComponentOrientation(ComponentOrientation + * orientation) { toolkit.lockAWT(); try { + * setComponentOrientation(orientation); } finally { toolkit.unlockAWT(); } + * } + */ + + /** + * Returns true if the set of focus traversal keys for the given focus + * traversal operation has been explicitly defined for this Component. + * + * @param id + * the ID of traversal key. + * @return true, if the set of focus traversal keys for the given focus. + * traversal operation has been explicitly defined for this + * Component, false otherwise. + */ + public boolean areFocusTraversalKeysSet(int id) { + toolkit.lockAWT(); + try { + Integer Id = new Integer(id); + if (traversalKeys.containsKey(Id)) { + return traversalKeys.get(Id) != null; + } + // awt.14F=invalid focus traversal key identifier + throw new IllegalArgumentException(Messages.getString("awt.14F")); //$NON-NLS-1$ + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the bounds of the Component. + * + * @return the rectangle bounds of the Component. + * @deprecated Use getBounds() methood. + */ + @Deprecated + public Rectangle bounds() { + toolkit.lockAWT(); + try { + return new Rectangle(x, y, w, h); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Returns the construction status of a specified image with the specified + * width and height that is being created. + * + * @param image + * the image to be checked. + * @param width + * the width of scaled image which status is being checked, or + * -1. + * @param height + * the height of scaled image which status is being checked, or + * -1. + * @param observer + * the ImageObserver object to be notified while the image is + * being prepared. + * @return the ImageObserver flags of the current state of the image data. + */ + public int checkImage(Image image, int width, int height, ImageObserver observer) { + toolkit.lockAWT(); + try { + return toolkit.checkImage(image, width, height, observer); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Returns the construction status of a specified image that is being + * created. + * + * @param image + * the image to be checked. + * @param observer + * the ImageObserver object to be notified while the image is + * being prepared. + * @return the ImageObserver flags of the current state of the image data. + */ + public int checkImage(Image image, ImageObserver observer) { + toolkit.lockAWT(); + try { + return toolkit.checkImage(image, -1, -1, observer); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Coalesces the existed event with new event. + * + * @param existingEvent + * the existing event in the EventQueue. + * @param newEvent + * the new event to be posted to the EventQueue. + * @return the coalesced AWTEvent, or null if there is no coalescing done. + */ + protected AWTEvent coalesceEvents(AWTEvent existingEvent, AWTEvent newEvent) { + toolkit.lockAWT(); + try { + // Nothing to do: + // 1. Mouse events coalesced at WTK level + // 2. Paint events handled by RedrawManager + // This method is for overriding only + return null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks if this Component is a coalescer. + * + * @return true, if is coalescer. + */ + boolean isCoalescer() { + return coalescer; + } + + /** + * Gets the relative event. + * + * @param id + * the id. + * @return the relative event. + */ + AWTEvent getRelativeEvent(int id) { + Integer idWrapper = new Integer(id); + eventsList = eventsTable.get(idWrapper); + if (eventsList == null) { + eventsList = new LinkedList(); + eventsTable.put(idWrapper, eventsList); + return null; + } + if (eventsList.isEmpty()) { + return null; + } + return eventsList.getLast(); + } + + /** + * Adds the new event. + * + * @param event + * the event. + */ + void addNewEvent(AWTEvent event) { + eventsList.addLast(event); + } + + /** + * Removes the relative event. + */ + void removeRelativeEvent() { + eventsList.removeLast(); + } + + /** + * Removes the next event. + * + * @param id + * the id. + */ + void removeNextEvent(int id) { + eventsTable.get(new Integer(id)).removeFirst(); + } + + /** + * Creates the image with the specified ImageProducer. + * + * @param producer + * the ImageProducer to be used for image creation. + * @return the image with the specified ImageProducer. + */ + public Image createImage(ImageProducer producer) { + toolkit.lockAWT(); + try { + return toolkit.createImage(producer); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Creates an off-screen drawable image to be used for double buffering. + * + * @param width + * the width of the image. + * @param height + * the height of the image. + * @return the off-screen drawable image or null if the component is not + * displayable or GraphicsEnvironment.isHeadless() method returns + * true. + */ + public Image createImage(int width, int height) { + toolkit.lockAWT(); + try { + if (!isDisplayable()) { + return null; + } + GraphicsConfiguration gc = getGraphicsConfiguration(); + if (gc == null) { + return null; + } + ColorModel cm = gc.getColorModel(Transparency.OPAQUE); + WritableRaster wr = cm.createCompatibleWritableRaster(width, height); + Image image = new BufferedImage(cm, wr, cm.isAlphaPremultiplied(), null); + fillImageBackground(image, width, height); + return image; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Creates an off-screen drawable image with the specified width, height and + * ImageCapabilities. + * + * @param width + * the width. + * @param height + * the height. + * @param caps + * the ImageCapabilities. + * @return the volatile image. + * @throws AWTException + * if an image with the specified capabilities cannot be + * created. + */ + public VolatileImage createVolatileImage(int width, int height, ImageCapabilities caps) + throws AWTException { + toolkit.lockAWT(); + try { + if (!isDisplayable()) { + return null; + } + GraphicsConfiguration gc = getGraphicsConfiguration(); + if (gc == null) { + return null; + } + VolatileImage image = gc.createCompatibleVolatileImage(width, height, caps); + fillImageBackground(image, width, height); + return image; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Creates a volatile off-screen drawable image which is used for double + * buffering. + * + * @param width + * the width of image. + * @param height + * the height of image. + * @return the volatile image a volatile off-screen drawable image which is + * used for double buffering or null if the component is not + * displayable, or GraphicsEnvironment.isHeadless() method returns + * true. + */ + public VolatileImage createVolatileImage(int width, int height) { + toolkit.lockAWT(); + try { + if (!isDisplayable()) { + return null; + } + GraphicsConfiguration gc = getGraphicsConfiguration(); + if (gc == null) { + return null; + } + VolatileImage image = gc.createCompatibleVolatileImage(width, height); + fillImageBackground(image, width, height); + return image; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Fill the image being created by createImage() or createVolatileImage() + * with the component's background color to prepare it for double-buffered + * painting. + * + * @param image + * the image. + * @param width + * the width. + * @param height + * the height. + */ + private void fillImageBackground(Image image, int width, int height) { + Graphics gr = image.getGraphics(); + gr.setColor(getBackground()); + gr.fillRect(0, 0, width, height); + gr.dispose(); + } + + /** + * Delivers event. + * + * @param evt + * the event. + * @deprecated Replaced by dispatchEvent(AWTEvent e) method. + */ + @Deprecated + public void deliverEvent(Event evt) { + postEvent(evt); + } + + /** + * Prompts the layout manager to lay out this component. + */ + public void doLayout() { + toolkit.lockAWT(); + try { + layout(); + } finally { + toolkit.unlockAWT(); + } + // Implemented in Container + } + + /** + * Fire property change impl. + * + * @param propertyName + * the property name. + * @param oldValue + * the old value. + * @param newValue + * the new value. + */ + private void firePropertyChangeImpl(String propertyName, Object oldValue, Object newValue) { + PropertyChangeSupport pcs; + synchronized (componentLock) { + if (propertyChangeSupport == null) { + return; + } + pcs = propertyChangeSupport; + } + pcs.firePropertyChange(propertyName, oldValue, newValue); + } + + /** + * Reports a bound property changes for int properties. + * + * @param propertyName + * the property name. + * @param oldValue + * the old property's value. + * @param newValue + * the new property's value. + */ + protected void firePropertyChange(String propertyName, int oldValue, int newValue) { + firePropertyChangeImpl(propertyName, new Integer(oldValue), new Integer(newValue)); + } + + /** + * Report a bound property change for a boolean-valued property. + * + * @param propertyName + * the property name. + * @param oldValue + * the property's old value. + * @param newValue + * the property's new value. + */ + protected void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { + firePropertyChangeImpl(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); + } + + /** + * Reports a bound property change for an Object-valued property. + * + * @param propertyName + * the property name. + * @param oldValue + * the property's old value. + * @param newValue + * the property's new value. + */ + protected void firePropertyChange(final String propertyName, final Object oldValue, + final Object newValue) { + firePropertyChangeImpl(propertyName, oldValue, newValue); + } + + /** + * Report a bound property change for a byte-valued property. + * + * @param propertyName + * the property name. + * @param oldValue + * the property's old value. + * @param newValue + * the property's new value. + */ + public void firePropertyChange(String propertyName, byte oldValue, byte newValue) { + firePropertyChangeImpl(propertyName, new Byte(oldValue), new Byte(newValue)); + } + + /** + * Report a bound property change for a char-valued property. + * + * @param propertyName + * the property name. + * @param oldValue + * the old property's value. + * @param newValue + * the new property's value. + */ + public void firePropertyChange(String propertyName, char oldValue, char newValue) { + firePropertyChangeImpl(propertyName, new Character(oldValue), new Character(newValue)); + } + + /** + * Report a bound property change for a short-valued property. + * + * @param propertyName + * the property name. + * @param oldValue + * the old property's value. + * @param newValue + * the new property's value. + */ + public void firePropertyChange(String propertyName, short oldValue, short newValue) { + firePropertyChangeImpl(propertyName, new Short(oldValue), new Short(newValue)); + } + + /** + * Report a bound property change for a long-valued property. + * + * @param propertyName + * the property name. + * @param oldValue + * the old property's value. + * @param newValue + * the new property's value. + */ + public void firePropertyChange(String propertyName, long oldValue, long newValue) { + firePropertyChangeImpl(propertyName, new Long(oldValue), new Long(newValue)); + } + + /** + * Report a bound property change for a float-valued property. + * + * @param propertyName + * the property name. + * @param oldValue + * the old property's value. + * @param newValue + * the new property's value. + */ + public void firePropertyChange(String propertyName, float oldValue, float newValue) { + firePropertyChangeImpl(propertyName, new Float(oldValue), new Float(newValue)); + } + + /** + * Report a bound property change for a double-valued property. + * + * @param propertyName + * the property name. + * @param oldValue + * the old property's value. + * @param newValue + * the new property's value. + */ + public void firePropertyChange(String propertyName, double oldValue, double newValue) { + firePropertyChangeImpl(propertyName, new Double(oldValue), new Double(newValue)); + } + + /** + * Gets the alignment along the x axis. + * + * @return the alignment along the x axis. + */ + public float getAlignmentX() { + toolkit.lockAWT(); + try { + return CENTER_ALIGNMENT; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the alignment along the y axis. + * + * @return the alignment along y axis. + */ + public float getAlignmentY() { + toolkit.lockAWT(); + try { + return CENTER_ALIGNMENT; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the background color for this component. + * + * @return the background color for this component. + */ + public Color getBackground() { + toolkit.lockAWT(); + try { + // ???AWT + /* + * if ((backColor == null) && (parent != null)) { return + * parent.getBackground(); } + */ + return backColor; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the bounding rectangle of this component. + * + * @return the bounding rectangle of this component. + */ + public Rectangle getBounds() { + toolkit.lockAWT(); + try { + return bounds(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Writes the data of the bounding rectangle to the specified Rectangle + * object. + * + * @param rv + * the Rectangle object where the bounding rectangle's data is + * stored. + * @return the bounding rectangle. + */ + public Rectangle getBounds(Rectangle rv) { + toolkit.lockAWT(); + try { + if (rv == null) { + rv = new Rectangle(); + } + rv.setBounds(x, y, w, h); + return rv; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the color model of the Component. + * + * @return the color model of the Component. + */ + public ColorModel getColorModel() { + toolkit.lockAWT(); + try { + return getToolkit().getColorModel(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the Component which contains the specified Point. + * + * @param p + * the Point. + * @return the Component which contains the specified Point. + */ + public Component getComponentAt(Point p) { + toolkit.lockAWT(); + try { + return getComponentAt(p.x, p.y); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the Component which contains the point with the specified + * coordinates. + * + * @param x + * the x coordinate of the point. + * @param y + * the y coordinate of the point. + * @return the Component which contains the point with the specified + * coordinates. + */ + public Component getComponentAt(int x, int y) { + toolkit.lockAWT(); + try { + return locate(x, y); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the component's orientation. + * + * @return the component's orientation. + */ + public ComponentOrientation getComponentOrientation() { + toolkit.lockAWT(); + try { + return orientation; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the cursor of the Component. + * + * @return the Cursor. + */ + public Cursor getCursor() { + toolkit.lockAWT(); + try { + if (cursor != null) { + return cursor; + // ???AWT + /* + * } else if (parent != null) { return parent.getCursor(); + */ + } + return Cursor.getDefaultCursor(); + } finally { + toolkit.unlockAWT(); + } + } + + // ???AWT + /* + * public DropTarget getDropTarget() { toolkit.lockAWT(); try { return + * dropTarget; } finally { toolkit.unlockAWT(); } } public Container + * getFocusCycleRootAncestor() { toolkit.lockAWT(); try { for (Container c = + * parent; c != null; c = c.getParent()) { if (c.isFocusCycleRoot()) { + * return c; } } return null; } finally { toolkit.unlockAWT(); } } + * @SuppressWarnings("unchecked") public Set + * getFocusTraversalKeys(int id) { toolkit.lockAWT(); try { Integer kId = + * new Integer(id); KeyboardFocusManager.checkTraversalKeysID(traversalKeys, + * kId); Set keys = traversalKeys.get(kId); if (keys + * == null && parent != null) { keys = parent.getFocusTraversalKeys(id); } + * if (keys == null) { keys = + * KeyboardFocusManager.getCurrentKeyboardFocusManager() + * .getDefaultFocusTraversalKeys(id); } return (Set) keys; } + * finally { toolkit.unlockAWT(); } } + */ + + /** + * Checks if the the focus traversal keys are enabled for this component. + * + * @return true, if the the focus traversal keys are enabled for this + * component, false otherwise. + */ + public boolean getFocusTraversalKeysEnabled() { + toolkit.lockAWT(); + try { + return focusTraversalKeysEnabled; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the font metrics of the specified Font. + * + * @param f + * the Font. + * @return the FontMetrics of the specified Font. + */ + @SuppressWarnings("deprecation") + public FontMetrics getFontMetrics(Font f) { + return toolkit.getFontMetrics(f); + } + + /** + * Gets the foreground color of the Component. + * + * @return the foreground color of the Component. + */ + public Color getForeground() { + toolkit.lockAWT(); + try { + // ???AWT + /* + * if (foreColor == null && parent != null) { return + * parent.getForeground(); } + */ + return foreColor; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the Graphics of the Component or null if this Component is not + * displayable. + * + * @return the Graphics of the Component or null if this Component is not + * displayable. + */ + public Graphics getGraphics() { + toolkit.lockAWT(); + try { + if (!isDisplayable()) { + return null; + } + Graphics g = behaviour.getGraphics(0, 0, w, h); + g.setColor(foreColor); + g.setFont(font); + return g; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the GraphicsConfiguration associated with this Component. + * + * @return the GraphicsConfiguration associated with this Component. + */ + public GraphicsConfiguration getGraphicsConfiguration() { + // ???AWT + /* + * toolkit.lockAWT(); try { Window win = getWindowAncestor(); if (win == + * null) { return null; } return win.getGraphicsConfiguration(); } + * finally { toolkit.unlockAWT(); } + */ + return null; + } + + /** + * Gets the height of the Component. + * + * @return the height of the Component. + */ + public int getHeight() { + toolkit.lockAWT(); + try { + return h; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Returns true if paint messages received from the operating system should + * be ignored. + * + * @return true if paint messages received from the operating system should + * be ignored, false otherwise. + */ + public boolean getIgnoreRepaint() { + toolkit.lockAWT(); + try { + return ignoreRepaint; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the input context of this component for handling the communication + * with input methods when text is entered in this component. + * + * @return the InputContext used by this Component or null if no context is + * specifined. + */ + public InputContext getInputContext() { + toolkit.lockAWT(); + try { + // ???AWT + /* + * Container parent = getParent(); if (parent != null) { return + * parent.getInputContext(); } + */ + return null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the input method request handler which supports requests from input + * methods for this component, or null for default. + * + * @return the input method request handler which supports requests from + * input methods for this component, or null for default. + */ + public InputMethodRequests getInputMethodRequests() { + return null; + } + + /** + * Gets the locale of this Component. + * + * @return the locale of this Component. + */ + public Locale getLocale() { + toolkit.lockAWT(); + try { + // ???AWT + /* + * if (locale == null) { if (parent == null) { if (this instanceof + * Window) { return Locale.getDefault(); } // awt.150=no parent + * throw new + * IllegalComponentStateException(Messages.getString("awt.150")); + * //$NON-NLS-1$ } return getParent().getLocale(); } + */ + return locale; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the location of this component in the form of a point specifying the + * component's top-left corner in the screen's coordinate space. + * + * @return the Point giving the component's location in the screen's + * coordinate space. + * @throws IllegalComponentStateException + * if the component is not shown on the screen. + */ + public Point getLocationOnScreen() throws IllegalComponentStateException { + toolkit.lockAWT(); + try { + Point p = new Point(); + if (isShowing()) { + // ???AWT + /* + * Component comp; for (comp = this; comp != null && !(comp + * instanceof Window); comp = comp .getParent()) { + * p.translate(comp.getX(), comp.getY()); } if (comp instanceof + * Window) { p.translate(comp.getX(), comp.getY()); } + */ + return p; + } + // awt.151=component must be showing on the screen to determine its + // location + throw new IllegalComponentStateException(Messages.getString("awt.151")); //$NON-NLS-1$ + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the peer. This method should not be called directly by user + * applications. + * + * @return the ComponentPeer. + * @deprecated Replaced by isDisplayable(). + */ + @Deprecated + public ComponentPeer getPeer() { + toolkit.lockAWT(); + try { + if (behaviour.isDisplayable()) { + return peer; + } + return null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets an array of the property change listeners registered to this + * Component. + * + * @return an array of the PropertyChangeListeners registered to this + * Component. + */ + public PropertyChangeListener[] getPropertyChangeListeners() { + return getPropertyChangeSupport().getPropertyChangeListeners(); + } + + /** + * Gets an array of PropertyChangeListener objects registered to this + * Component for the specified property. + * + * @param propertyName + * the property name. + * @return an array of PropertyChangeListener objects registered to this + * Component for the specified property. + */ + public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { + return getPropertyChangeSupport().getPropertyChangeListeners(propertyName); + } + + /** + * Gets the width of the Component. + * + * @return the width of the Component. + */ + public int getWidth() { + toolkit.lockAWT(); + try { + return w; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the x coordinate of the component's top-left corner. + * + * @return the x coordinate of the component's top-left corner. + */ + public int getX() { + toolkit.lockAWT(); + try { + return x; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the y coordinate of the component's top-left corner. + * + * @return the y coordinate of the component's top-left corner. + */ + public int getY() { + toolkit.lockAWT(); + try { + return y; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Got the focus. + * + * @param evt + * the Event. + * @param what + * the Object. + * @return true, if successful. + * @deprecated Replaced by processFocusEvent(FocusEvent) method. + */ + @Deprecated + public boolean gotFocus(Event evt, Object what) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Handles event. + * + * @param evt + * the Event. + * @return true, if successful. + * @deprecated Replaced by processEvent(AWTEvent) method. + */ + @Deprecated + public boolean handleEvent(Event evt) { + switch (evt.id) { + case Event.ACTION_EVENT: + return action(evt, evt.arg); + case Event.GOT_FOCUS: + return gotFocus(evt, null); + case Event.LOST_FOCUS: + return lostFocus(evt, null); + case Event.MOUSE_DOWN: + return mouseDown(evt, evt.x, evt.y); + case Event.MOUSE_DRAG: + return mouseDrag(evt, evt.x, evt.y); + case Event.MOUSE_ENTER: + return mouseEnter(evt, evt.x, evt.y); + case Event.MOUSE_EXIT: + return mouseExit(evt, evt.x, evt.y); + case Event.MOUSE_MOVE: + return mouseMove(evt, evt.x, evt.y); + case Event.MOUSE_UP: + return mouseUp(evt, evt.x, evt.y); + case Event.KEY_ACTION: + case Event.KEY_PRESS: + return keyDown(evt, evt.key); + case Event.KEY_ACTION_RELEASE: + case Event.KEY_RELEASE: + return keyUp(evt, evt.key); + } + return false;// event not handled + } + + /** + * Checks whether the Component is the focus owner or not. + * + * @return true, if the Component is the focus owner, false otherwise. + */ + public boolean hasFocus() { + toolkit.lockAWT(); + try { + // ???AWT: return isFocusOwner(); + return false; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Hides the Component. + * + * @deprecated Replaced by setVisible(boolean) method. + */ + @Deprecated + public void hide() { + toolkit.lockAWT(); + try { + if (!visible) { + return; + } + prepare4HierarchyChange(); + visible = false; + moveFocusOnHide(); + behaviour.setVisible(false); + postEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_HIDDEN)); + // ???AWT: finishHierarchyChange(this, parent, 0); + notifyInputMethod(null); + // ???AWT: invalidateRealParent(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not the point with the specified coordinates belongs to + * the Commponent. + * + * @param x + * the x coordinate of the Point. + * @param y + * the y coordinate of the Point. + * @return true, if the point with the specified coordinates belongs to the + * Commponent, false otherwise. + * @deprecated Replaced by contains(int, int) method. + */ + @Deprecated + public boolean inside(int x, int y) { + toolkit.lockAWT(); + try { + return x >= 0 && x < getWidth() && y >= 0 && y < getHeight(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Invalidates the component, this component and all parents above it are + * marked as needing to be laid out. + */ + public void invalidate() { + toolkit.lockAWT(); + try { + valid = false; + resetDefaultSize(); + // ???AWT: invalidateRealParent(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not the background color is set to this Component. + * + * @return true, if the background color is set to this Component, false + * otherwise. + */ + public boolean isBackgroundSet() { + toolkit.lockAWT(); + try { + return backColor != null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not a cursor is set for the Component. + * + * @return true, if a cursor is set for the Component, false otherwise. + */ + public boolean isCursorSet() { + toolkit.lockAWT(); + try { + return cursor != null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not this Component is displayable. + * + * @return true, if this Component is displayable, false otherwise. + */ + public boolean isDisplayable() { + toolkit.lockAWT(); + try { + return behaviour.isDisplayable(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not this component is painted to an buffer which is + * copied to the screen later. + * + * @return true, if this component is painted to an buffer which is copied + * to the screen later, false otherwise. + */ + public boolean isDoubleBuffered() { + toolkit.lockAWT(); + try { + // false by default + return false; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not this Component is enabled. + * + * @return true, if this Component is enabled, false otherwise. + */ + public boolean isEnabled() { + toolkit.lockAWT(); + try { + return enabled; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * "Recursive" isEnabled(). + * + * @return true if not only component itself is enabled but its heavyweight + * parent is also "indirectly" enabled. + */ + boolean isIndirectlyEnabled() { + Component comp = this; + while (comp != null) { + if (!comp.isLightweight() && !comp.isEnabled()) { + return false; + } + // ???AWT: comp = comp.getRealParent(); + } + return true; + } + + /** + * Checks if the component is key enabled. + * + * @return true, if the component is enabled and indirectly enabled. + */ + boolean isKeyEnabled() { + if (!isEnabled()) { + return false; + } + return isIndirectlyEnabled(); + } + + /** + * Gets only parent of a child component, but not owner of a window. + * + * @return parent of child component, null if component is a top-level + * (Window instance). + */ + // ???AWT + /* + * Container getRealParent() { return (!(this instanceof Window) ? + * getParent() : null); } public boolean isFocusCycleRoot(Container + * container) { toolkit.lockAWT(); try { return getFocusCycleRootAncestor() + * == container; } finally { toolkit.unlockAWT(); } } public boolean + * isFocusOwner() { toolkit.lockAWT(); try { return + * KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() == + * this; } finally { toolkit.unlockAWT(); } } + */ + + /** + * Checks whether or not this Component can be focusable. + * + * @return true, if this Component can be focusable, false otherwise. + * @deprecated Replaced by isFocusable(). + */ + @Deprecated + public boolean isFocusTraversable() { + toolkit.lockAWT(); + try { + overridenIsFocusable = false; + return focusable; // a Component must either be both focusable and + // focus traversable, or neither + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks if this Component can be focusable or not. + * + * @return true, if this Component can be focusable, false otherwise. + */ + public boolean isFocusable() { + toolkit.lockAWT(); + try { + return isFocusTraversable(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks if the Font is set for this Component or not. + * + * @return true, if the Font is set, false otherwise. + */ + public boolean isFontSet() { + toolkit.lockAWT(); + try { + return font != null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks if foreground color is set for the Component or not. + * + * @return true, if is foreground color is set for the Component, false + * otherwise. + */ + public boolean isForegroundSet() { + toolkit.lockAWT(); + try { + return foreColor != null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Returns true if this component has a lightweight peer. + * + * @return true, if this component has a lightweight peer, false if it has a + * native peer or no peer. + */ + public boolean isLightweight() { + toolkit.lockAWT(); + try { + return behaviour.isLightweight(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not this Component is shown. + * + * @return true, if this Component is shown, false otherwise. + */ + public boolean isShowing() { + // ???AWT + /* + * toolkit.lockAWT(); try { return (isVisible() && isDisplayable() && + * (parent != null) && parent.isShowing()); } finally { + * toolkit.unlockAWT(); } + */ + return false; + } + + /** + * Checks whether or not this Component is visible. + * + * @return true, if the Component is visible, false otherwise. + */ + public boolean isVisible() { + toolkit.lockAWT(); + try { + return visible; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Deprecated: replaced by processKeyEvent(KeyEvent) method. + * + * @param evt + * the Event. + * @param key + * the key code. + * @return true, if successful. + * @deprecated Replaced by replaced by processKeyEvent(KeyEvent) method. + */ + @Deprecated + public boolean keyDown(Event evt, int key) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Deprecated: replaced by processKeyEvent(KeyEvent) method. + * + * @param evt + * the Event. + * @param key + * the key code. + * @return true, if successful. + * @deprecated Replaced by processKeyEvent(KeyEvent) method. + */ + @Deprecated + public boolean keyUp(Event evt, int key) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Deprecated: Replaced by doLayout() method. + * + * @deprecated Replaced by doLayout() method. + */ + @Deprecated + public void layout() { + toolkit.lockAWT(); + try { + // Implemented in Container + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Deprecated: replaced by getComponentAt(int, int) method. + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @return The component. + * @deprecated Replaced by getComponentAt(int, int) method. + */ + @Deprecated + public Component locate(int x, int y) { + toolkit.lockAWT(); + try { + if (contains(x, y)) { + return this; + } + return null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Deprecated: replaced by processFocusEvent(FocusEvent). + * + * @param evt + * the Event. + * @param what + * the Object. + * @return true, if successful. + * @deprecated Replaced by processFocusEvent(FocusEvent). + */ + @Deprecated + public boolean lostFocus(Event evt, Object what) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Deprecated: replaced by processMouseEvent(MouseEvent) method. + * + * @param evt + * the MouseEvent. + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @return true, if successful. + * @deprecated Replaced by processMouseEvent(MouseEvent) method. + */ + @Deprecated + public boolean mouseDown(Event evt, int x, int y) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Deprecated: replaced by getMinimumSize() method. + * + * @param evt + * the Event. + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @return true, if successful. + * @deprecated Replaced by getMinimumSize() method. + */ + @Deprecated + public boolean mouseDrag(Event evt, int x, int y) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Replaced by processMouseEvent(MouseEvent) method. + * + * @param evt + * the Event. + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @return true, if successful. + * @deprecated replaced by processMouseEvent(MouseEvent) method. + */ + @Deprecated + public boolean mouseEnter(Event evt, int x, int y) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Replaced by processMouseEvent(MouseEvent) method. + * + * @param evt + * the Event. + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @return true, if successful. + * @deprecated Replaced by processMouseEvent(MouseEvent) method. + */ + @Deprecated + public boolean mouseExit(Event evt, int x, int y) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Replaced by processMouseEvent(MouseEvent) method. + * + * @param evt + * the Event. + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @deprecated Replaced by processMouseEvent(MouseEvent) method. + * @return true, if successful. + */ + @Deprecated + public boolean mouseMove(Event evt, int x, int y) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Replaced by processMouseEvent(MouseEvent) method. + * + * @param evt + * the Event. + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @return true, if successful. + * @deprecated Replaced by processMouseEvent(MouseEvent) method. + */ + @Deprecated + public boolean mouseUp(Event evt, int x, int y) { + // to be overridden: do nothing, + // just return false to propagate event up to the parent container + return false; + } + + /** + * Deprecated: replaced by setLocation(int, int) method. + * + * @param x + * the x coordinates. + * @param y + * the y coordinates. + * @deprecated Replaced by setLocation(int, int) method. + */ + @Deprecated + public void move(int x, int y) { + toolkit.lockAWT(); + try { + boundsMaskParam = NativeWindow.BOUNDS_NOSIZE; + setBounds(x, y, w, h); + } finally { + toolkit.unlockAWT(); + } + } + + // ???AWT + /* + * @Deprecated public void nextFocus() { toolkit.lockAWT(); try { + * transferFocus(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); } finally { + * toolkit.unlockAWT(); } } + */ + + /** + * Returns a string representation of the component's state. + * + * @return the string representation of the component's state. + */ + protected String paramString() { + /* + * The format is based on 1.5 release behavior which can be revealed by + * the following code: Component c = new Component(){}; + * c.setVisible(false); System.out.println(c); + */ + toolkit.lockAWT(); + try { + return getName() + "," + getX() + "," + getY() + "," + getWidth() + "x" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + + getHeight() + (!isVisible() ? ",hidden" : ""); //$NON-NLS-1$ //$NON-NLS-2$ + } finally { + toolkit.unlockAWT(); + } + } + + @Deprecated + @SuppressWarnings("deprecation") + public boolean postEvent(Event evt) { + boolean handled = handleEvent(evt); + if (handled) { + return true; + } + // ???AWT + /* + * // propagate non-handled events up to parent Component par = parent; + * // try to call postEvent only on components which // override any of + * deprecated method handlers // while (par != null && + * !par.deprecatedEventHandler) { // par = par.parent; // } // translate + * event coordinates before posting it to parent if (par != null) { + * evt.translate(x, y); par.postEvent(evt); } + */ + return false; + } + + /** + * Prepares an image for rendering on the Component. + * + * @param image + * the Image to be prepared. + * @param observer + * the ImageObserver object to be notified as soon as the image + * is prepared. + * @return true if the image has been fully prepared, false otherwise. + */ + public boolean prepareImage(Image image, ImageObserver observer) { + toolkit.lockAWT(); + try { + return toolkit.prepareImage(image, -1, -1, observer); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Prepares an image for rendering on the Component with the specified + * width, height, and ImageObserver. + * + * @param image + * the Image to be prepared. + * @param width + * the width of scaled image. + * @param height + * the height of scaled height. + * @param observer + * the ImageObserver object to be notified as soon as the image + * is prepared. + * @return true if the image is been fully prepared, false otherwise. + */ + public boolean prepareImage(Image image, int width, int height, ImageObserver observer) { + toolkit.lockAWT(); + try { + return toolkit.prepareImage(image, width, height, observer); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Makes this Component undisplayable. + */ + public void removeNotify() { + toolkit.lockAWT(); + try { + // ???AWT + /* + * if (dropTarget != null) { dropTarget.removeNotify(peer); } + */ + prepare4HierarchyChange(); + // /???AWT: moveFocus(); + behaviour.removeNotify(); + // ???AWT: finishHierarchyChange(this, parent, 0); + removeNotifyInputContext(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Calls InputContext.removeNotify. + */ + private void removeNotifyInputContext() { + if (!inputMethodsEnabled) { + return; + } + InputContext ic = getInputContext(); + if (ic != null) { + // ???AWT: ic.removeNotify(this); + } + } + + /** + * This method is called when some property of a component changes, making + * it unfocusable, e. g. hide(), removeNotify(), setEnabled(false), + * setFocusable(false) is called, and therefore automatic forward focus + * traversal is necessary + */ + // ???AWT + /* + * void moveFocus() { // don't use transferFocus(), but query focus + * traversal policy directly // and if it returns null, transfer focus up + * cycle // and find next focusable component there KeyboardFocusManager kfm + * = KeyboardFocusManager.getCurrentKeyboardFocusManager(); Container root = + * kfm.getCurrentFocusCycleRoot(); Component nextComp = this; boolean + * success = !isFocusOwner(); while (!success) { if (root != + * nextComp.getFocusCycleRootAncestor()) { // component was probably removed + * from container // so focus will be lost in some time return; } nextComp = + * root.getFocusTraversalPolicy().getComponentAfter(root, nextComp); if + * (nextComp == this) { nextComp = null; // avoid looping } if (nextComp != + * null) { success = nextComp.requestFocusInWindow(); } else { nextComp = + * root; root = root.getFocusCycleRootAncestor(); // if no acceptable + * component is found at all - clear global // focus owner if (root == null) + * { if (nextComp instanceof Window) { Window wnd = (Window) nextComp; + * wnd.setFocusOwner(null); wnd.setRequestedFocus(null); } + * kfm.clearGlobalFocusOwner(); return; } } } } + */ + + /** + * For Container there's a difference between moving focus when being made + * invisible or made unfocusable in some other way, because when container + * is made invisible, component still remains visible, i. e. its hide() or + * setVisible() is not called. + */ + void moveFocusOnHide() { + // ???AWT: moveFocus(); + } + + /** + * Removes the property change listener registered for this component. + * + * @param listener + * the PropertyChangeListener. + */ + public void removePropertyChangeListener(PropertyChangeListener listener) { + getPropertyChangeSupport().removePropertyChangeListener(listener); + } + + /** + * Removes the property change listener registered fot this component for + * the specified propertyy. + * + * @param propertyName + * the property name. + * @param listener + * the PropertyChangeListener. + */ + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + getPropertyChangeSupport().removePropertyChangeListener(propertyName, listener); + } + + /** + * Repaints the specified rectangle of this component within tm + * milliseconds. + * + * @param tm + * the time in milliseconds before updating. + * @param x + * the x coordinate of Rectangle. + * @param y + * the y coordinate of Rectangle. + * @param width + * the width of Rectangle. + * @param height + * the height of Rectangle. + */ + public void repaint(long tm, int x, int y, int width, int height) { + // ???AWT + /* + * toolkit.lockAWT(); try { if (width <= 0 || height <= 0 || + * (redrawManager == null) || !isShowing()) { return; } if (behaviour + * instanceof LWBehavior) { if (parent == null || !parent.visible || + * !parent.behaviour.isDisplayable()) { return; } if (repaintRegion == + * null) { repaintRegion = new MultiRectArea(new Rectangle(x, y, width, + * height)); } repaintRegion.intersect(new Rectangle(0, 0, this.w, + * this.h)); repaintRegion.translate(this.x, this.y); + * parent.repaintRegion = repaintRegion; repaintRegion = null; + * parent.repaint(tm, x + this.x, y + this.y, width, height); } else { + * if (repaintRegion != null) { redrawManager.addUpdateRegion(this, + * repaintRegion); repaintRegion = null; } else { + * redrawManager.addUpdateRegion(this, new Rectangle(x, y, width, + * height)); } + * toolkit.getSystemEventQueueCore().notifyEventMonitor(toolkit); } } + * finally { toolkit.unlockAWT(); } + */ + } + + /** + * Post event. + * + * @param e + * the e. + */ + void postEvent(AWTEvent e) { + getToolkit().getSystemEventQueueImpl().postEvent(e); + } + + /** + * Repaints the specified Rectangle of this Component. + * + * @param x + * the x coordinate of Rectangle. + * @param y + * the y coordinate of Rectangle. + * @param width + * the width of Rectangle. + * @param height + * the height of Rectangle. + */ + public void repaint(int x, int y, int width, int height) { + toolkit.lockAWT(); + try { + repaint(0, x, y, width, height); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Repaints this component. + */ + public void repaint() { + toolkit.lockAWT(); + try { + if (w > 0 && h > 0) { + repaint(0, 0, 0, w, h); + } + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Repaints the component within tm milliseconds. + * + * @param tm + * the time in milliseconds before updating. + */ + public void repaint(long tm) { + toolkit.lockAWT(); + try { + repaint(tm, 0, 0, w, h); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Requests that this Component get the input focus temporarily. This + * component must be displayable, visible, and focusable. + * + * @param temporary + * this parameter is true if the focus change is temporary, when + * the window loses the focus. + * @return true if the focus change request is succeeded, false otherwise. + */ + protected boolean requestFocus(boolean temporary) { + toolkit.lockAWT(); + try { + // ???AWT: return requestFocusImpl(temporary, true, false); + } finally { + toolkit.unlockAWT(); + } + // ???AWT + return false; + } + + /** + * Requests that this Component get the input focus. This component must be + * displayable, visible, and focusable. + */ + public void requestFocus() { + toolkit.lockAWT(); + try { + requestFocus(false); + } finally { + toolkit.unlockAWT(); + } + } + + // ???AWT + /* + * protected boolean requestFocusInWindow(boolean temporary) { + * toolkit.lockAWT(); try { Window wnd = getWindowAncestor(); if ((wnd == + * null) || !wnd.isFocused()) { return false; } return + * requestFocusImpl(temporary, false, false); } finally { + * toolkit.unlockAWT(); } } boolean requestFocusImpl(boolean temporary, + * boolean crossWindow, boolean rejectionRecovery) { if (!rejectionRecovery + * && isFocusOwner()) { return true; } Window wnd = getWindowAncestor(); + * Container par = getRealParent(); if ((par != null) && par.isRemoved) { + * return false; } if (!isShowing() || !isFocusable() || + * !wnd.isFocusableWindow()) { return false; } return + * KeyboardFocusManager.getCurrentKeyboardFocusManager().requestFocus(this, + * temporary, crossWindow, true); } public boolean requestFocusInWindow() { + * toolkit.lockAWT(); try { return requestFocusInWindow(false); } finally { + * toolkit.unlockAWT(); } } + */ + + /** + * Deprecated: replaced by setBounds(int, int, int, int) method. + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @param w + * the width. + * @param h + * the height. + * @deprecated Replaced by setBounds(int, int, int, int) method. + */ + @Deprecated + public void reshape(int x, int y, int w, int h) { + toolkit.lockAWT(); + try { + setBounds(x, y, w, h, boundsMaskParam, true); + boundsMaskParam = 0; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets rectangle for this Component to be the rectangle with the specified + * x,y coordinates of the top-left corner and the width and height. + * + * @param x + * the x coordinate of the rectangle's top-left corner. + * @param y + * the y coordinate of the rectangle's top-left corner. + * @param w + * the width of rectangle. + * @param h + * the height of rectangle. + */ + public void setBounds(int x, int y, int w, int h) { + toolkit.lockAWT(); + try { + reshape(x, y, w, h); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets rectangle for this Component to be the rectangle with the specified + * x,y coordinates of the top-left corner and the width and height and posts + * the appropriate events. + * + * @param x + * the x coordinate of the rectangle's top-left corner. + * @param y + * the y coordinate of the rectangle's top-left corner. + * @param w + * the width of rectangle. + * @param h + * the height of rectangle. + * @param bMask + * the bitmask of bounds options. + * @param updateBehavior + * the whether to update the behavoir's bounds as well. + */ + void setBounds(int x, int y, int w, int h, int bMask, boolean updateBehavior) { + int oldX = this.x; + int oldY = this.y; + int oldW = this.w; + int oldH = this.h; + setBoundsFields(x, y, w, h, bMask); + // Moved + if ((oldX != this.x) || (oldY != this.y)) { + // ???AWT: invalidateRealParent(); + postEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_MOVED)); + spreadHierarchyBoundsEvents(this, HierarchyEvent.ANCESTOR_MOVED); + } + // Resized + if ((oldW != this.w) || (oldH != this.h)) { + invalidate(); + postEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_RESIZED)); + spreadHierarchyBoundsEvents(this, HierarchyEvent.ANCESTOR_RESIZED); + } + if (updateBehavior) { + behaviour.setBounds(this.x, this.y, this.w, this.h, bMask); + } + notifyInputMethod(new Rectangle(x, y, w, h)); + } + + /** + * Calls InputContextImpl.notifyClientWindowChanged. + * + * @param bounds + * the bounds. + */ + void notifyInputMethod(Rectangle bounds) { + // only Window actually notifies IM of bounds change + } + + /** + * Sets the bounds fields. + * + * @param x + * the x. + * @param y + * the y. + * @param w + * the w. + * @param h + * the h. + * @param bMask + * the b mask. + */ + private void setBoundsFields(int x, int y, int w, int h, int bMask) { + if ((bMask & NativeWindow.BOUNDS_NOSIZE) == 0) { + this.w = w; + this.h = h; + } + if ((bMask & NativeWindow.BOUNDS_NOMOVE) == 0) { + this.x = x; + this.y = y; + } + } + + /** + * Gets the native insets. + * + * @return the native insets. + */ + Insets getNativeInsets() { + return new Insets(0, 0, 0, 0); + } + + /** + * Gets the insets. + * + * @return the insets. + */ + Insets getInsets() { + return new Insets(0, 0, 0, 0); + } + + /** + * Checks if is mouse exited expected. + * + * @return true, if is mouse exited expected. + */ + boolean isMouseExitedExpected() { + return mouseExitedExpected; + } + + /** + * Sets the mouse exited expected. + * + * @param expected + * the new mouse exited expected. + */ + void setMouseExitedExpected(boolean expected) { + mouseExitedExpected = expected; + } + + /** + * Sets the new bounding rectangle for this Component. + * + * @param r + * the new bounding rectangle. + */ + public void setBounds(Rectangle r) { + toolkit.lockAWT(); + try { + setBounds(r.x, r.y, r.width, r.height); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the component orientation which affects the component's elements and + * text within this component. + * + * @param o + * the ComponentOrientation object. + */ + public void setComponentOrientation(ComponentOrientation o) { + ComponentOrientation oldOrientation; + toolkit.lockAWT(); + try { + oldOrientation = orientation; + orientation = o; + } finally { + toolkit.unlockAWT(); + } + firePropertyChange("componentOrientation", oldOrientation, orientation); //$NON-NLS-1$ + invalidate(); + } + + /** + * Sets the specified cursor for this Component. + * + * @param cursor + * the new Cursor. + */ + public void setCursor(Cursor cursor) { + toolkit.lockAWT(); + try { + this.cursor = cursor; + setCursor(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Set current cursor shape to Component's Cursor. + */ + void setCursor() { + if (isDisplayable() && isShowing()) { + Rectangle absRect = new Rectangle(getLocationOnScreen(), getSize()); + Point absPointerPos = toolkit.dispatcher.mouseDispatcher.getPointerPos(); + // ???AWT + /* + * if (absRect.contains(absPointerPos)) { // set Cursor only on + * top-level Windows(on X11) Window topLevelWnd = + * getWindowAncestor(); if (topLevelWnd != null) { Point pointerPos + * = MouseDispatcher.convertPoint(null, absPointerPos, topLevelWnd); + * Component compUnderCursor = + * topLevelWnd.findComponentAt(pointerPos); // if (compUnderCursor + * == this || // compUnderCursor.getCursorAncestor() == this) { + * NativeWindow wnd = topLevelWnd.getNativeWindow(); if + * (compUnderCursor != null && wnd != null) { + * compUnderCursor.getRealCursor().getNativeCursor() + * .setCursor(wnd.getId()); } // } } } + */ + } + } + + /** + * Gets the ancestor Cursor if Component is disabled (directly or via an + * ancestor) even if Cursor is explicitly set. + * + * @param value + * the value. + * @return the actual Cursor to be displayed. + */ + // ???AWT + /* + * Cursor getRealCursor() { Component cursorAncestor = getCursorAncestor(); + * return cursorAncestor != null ? cursorAncestor.getCursor() : + * Cursor.getDefaultCursor(); } + */ + + /** + * Gets the ancestor(or component itself) whose cursor is set when pointer + * is inside component + * + * @return the actual Cursor to be displayed. + */ + // ???AWT + /* + * Component getCursorAncestor() { Component comp; for (comp = this; comp != + * null; comp = comp.getParent()) { if (comp instanceof Window || + * comp.isCursorSet() && comp.isKeyEnabled()) { return comp; } } return + * null; } public void setDropTarget(DropTarget dt) { toolkit.lockAWT(); try + * { if (dropTarget == dt) { return; } DropTarget oldDropTarget = + * dropTarget; dropTarget = dt; if (oldDropTarget != null) { if + * (behaviour.isDisplayable()) { oldDropTarget.removeNotify(peer); } + * oldDropTarget.setComponent(null); } if (dt != null) { + * dt.setComponent(this); if (behaviour.isDisplayable()) { + * dt.addNotify(peer); } } } finally { toolkit.unlockAWT(); } } + */ + + /** + * Sets this component to the "enabled" or "disabled" state depending on the + * specified boolean parameter. + * + * @param value + * true if this component should be enabled; false if this + * component should be disabled. + */ + public void setEnabled(boolean value) { + toolkit.lockAWT(); + try { + enable(value); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the enabled impl. + * + * @param value + * the new enabled impl. + */ + void setEnabledImpl(boolean value) { + if (enabled != value) { + enabled = value; + setCursor(); + if (!enabled) { + moveFocusOnHide(); + } + behaviour.setEnabled(value); + } + } + + // ???AWT + /* + * private void fireAccessibleStateChange(AccessibleState state, boolean + * value) { if (behaviour.isLightweight()) { return; } AccessibleContext ac + * = getAccessibleContext(); if (ac != null) { AccessibleState oldValue = + * null; AccessibleState newValue = null; if (value) { newValue = state; } + * else { oldValue = state; } + * ac.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, + * oldValue, newValue); } } + */ + + // ???AWT + /* + * public void setFocusTraversalKeys(int id, Set + * keystrokes) { Set oldTraversalKeys; String + * propName = "FocusTraversalKeys"; //$NON-NLS-1$ toolkit.lockAWT(); try { + * Integer kId = new Integer(id); + * KeyboardFocusManager.checkTraversalKeysID(traversalKeys, kId); + * Map> keys = new HashMap>(); for (int kid : traversalIDs) { Integer + * key = new Integer(kid); keys.put(key, getFocusTraversalKeys(kid)); } + * KeyboardFocusManager.checkKeyStrokes(traversalIDs, keys, kId, + * keystrokes); oldTraversalKeys = traversalKeys.get(new Integer(id)); // + * put a copy of keystrokes object into map: Set + * newKeys = keystrokes; if (keystrokes != null) { newKeys = new + * HashSet(keystrokes); } traversalKeys.put(kId, newKeys); + * String direction = ""; //$NON-NLS-1$ switch (id) { case + * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS: direction = "forward"; + * //$NON-NLS-1$ break; case KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS: + * direction = "backward"; //$NON-NLS-1$ break; case + * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS: direction = "upCycle"; + * //$NON-NLS-1$ break; case KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS: + * direction = "downCycle"; //$NON-NLS-1$ break; } propName = direction + + * propName; } finally { toolkit.unlockAWT(); } firePropertyChange(propName, + * oldTraversalKeys, keystrokes); } + */ + + /** + * Sets the focus traversal keys state for this component. + * + * @param value + * true if the focus traversal keys state is enabled, false if + * the focus traversal keys state is disabled. + */ + public void setFocusTraversalKeysEnabled(boolean value) { + boolean oldFocusTraversalKeysEnabled; + toolkit.lockAWT(); + try { + oldFocusTraversalKeysEnabled = focusTraversalKeysEnabled; + focusTraversalKeysEnabled = value; + } finally { + toolkit.unlockAWT(); + } + firePropertyChange("focusTraversalKeysEnabled", oldFocusTraversalKeysEnabled, //$NON-NLS-1$ + focusTraversalKeysEnabled); + } + + // ???AWT + /* + * public void setFocusable(boolean focusable) { boolean oldFocusable; + * toolkit.lockAWT(); try { calledSetFocusable = true; oldFocusable = + * this.focusable; this.focusable = focusable; if (!focusable) { + * moveFocus(); } } finally { toolkit.unlockAWT(); } + * firePropertyChange("focusable", oldFocusable, focusable); //$NON-NLS-1$ } + * public Font getFont() { toolkit.lockAWT(); try { return (font == null) && + * (parent != null) ? parent.getFont() : font; } finally { + * toolkit.unlockAWT(); } } + */ + + /** + * Sets the font for this Component. + * + * @param f + * the new font of the Component. + */ + public void setFont(Font f) { + Font oldFont; + toolkit.lockAWT(); + try { + oldFont = font; + setFontImpl(f); + } finally { + toolkit.unlockAWT(); + } + firePropertyChange("font", oldFont, font); //$NON-NLS-1$ + } + + /** + * Sets the font impl. + * + * @param f + * the new font impl. + */ + void setFontImpl(Font f) { + font = f; + invalidate(); + if (isShowing()) { + repaint(); + } + } + + /** + * Invalidate the component if it inherits the font from the parent. This + * method is overridden in Container. + * + * @return true if the component was invalidated, false otherwise. + */ + boolean propagateFont() { + if (font == null) { + invalidate(); + return true; + } + return false; + } + + /** + * Sets the foreground color for this Component. + * + * @param c + * the new foreground color. + */ + public void setForeground(Color c) { + Color oldFgColor; + toolkit.lockAWT(); + try { + oldFgColor = foreColor; + foreColor = c; + } finally { + toolkit.unlockAWT(); + } + firePropertyChange("foreground", oldFgColor, foreColor); //$NON-NLS-1$ + repaint(); + } + + /** + * Sets the background color for the Component. + * + * @param c + * the new background color for this component. + */ + public void setBackground(Color c) { + Color oldBkColor; + toolkit.lockAWT(); + try { + oldBkColor = backColor; + backColor = c; + } finally { + toolkit.unlockAWT(); + } + firePropertyChange("background", oldBkColor, backColor); //$NON-NLS-1$ + repaint(); + } + + /** + * Sets the flag for whether paint messages received from the operating + * system should be ignored or not. + * + * @param value + * true if paint messages received from the operating system + * should be ignored, false otherwise. + */ + public void setIgnoreRepaint(boolean value) { + toolkit.lockAWT(); + try { + ignoreRepaint = value; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the locale of the component. + * + * @param locale + * the new Locale. + */ + public void setLocale(Locale locale) { + Locale oldLocale; + toolkit.lockAWT(); + try { + oldLocale = this.locale; + this.locale = locale; + } finally { + toolkit.unlockAWT(); + } + firePropertyChange("locale", oldLocale, locale); //$NON-NLS-1$ + } + + /** + * Sets the location of the Component to the specified point. + * + * @param p + * the new location of the Component. + */ + public void setLocation(Point p) { + toolkit.lockAWT(); + try { + setLocation(p.x, p.y); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the location of the Component to the specified x, y coordinates. + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + */ + public void setLocation(int x, int y) { + toolkit.lockAWT(); + try { + move(x, y); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the visibility state of the component. + * + * @param b + * true if the component is visible, false if the component is + * not shown. + */ + public void setVisible(boolean b) { + // show() & hide() are not deprecated for Window, + // so have to call them from setVisible() + show(b); + } + + /** + * Deprecated: replaced by setVisible(boolean) method. + * + * @deprecated Replaced by setVisible(boolean) method. + */ + @Deprecated + public void show() { + toolkit.lockAWT(); + try { + if (visible) { + return; + } + prepare4HierarchyChange(); + mapToDisplay(true); + validate(); + visible = true; + behaviour.setVisible(true); + postEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_SHOWN)); + // ???AWT: finishHierarchyChange(this, parent, 0); + notifyInputMethod(new Rectangle(x, y, w, h)); + // ???AWT: invalidateRealParent(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Deprecated: replaced by setVisible(boolean) method. + * + * @param b + * the visibility's state. + * @deprecated Replaced by setVisible(boolean) method. + */ + @Deprecated + public void show(boolean b) { + if (b) { + show(); + } else { + hide(); + } + } + + // ???AWT + /* + * void transferFocus(int dir) { Container root = null; if (this instanceof + * Container) { Container cont = (Container) this; if + * (cont.isFocusCycleRoot()) { root = cont.getFocusTraversalRoot(); } } if + * (root == null) { root = getFocusCycleRootAncestor(); } // transfer focus + * up cycle if root is unreachable Component comp = this; while ((root != + * null) && !(root.isFocusCycleRoot() && root.isShowing() && + * root.isEnabled() && root .isFocusable())) { comp = root; root = + * root.getFocusCycleRootAncestor(); } if (root == null) { return; } + * FocusTraversalPolicy policy = root.getFocusTraversalPolicy(); Component + * nextComp = null; switch (dir) { case + * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS: nextComp = + * policy.getComponentAfter(root, comp); break; case + * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS: nextComp = + * policy.getComponentBefore(root, comp); break; } if (nextComp != null) { + * nextComp.requestFocus(false); } } public void transferFocus() { + * toolkit.lockAWT(); try { nextFocus(); } finally { toolkit.unlockAWT(); } + * } public void transferFocusBackward() { toolkit.lockAWT(); try { + * transferFocus(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); } finally { + * toolkit.unlockAWT(); } } public void transferFocusUpCycle() { + * toolkit.lockAWT(); try { KeyboardFocusManager kfm = + * KeyboardFocusManager.getCurrentKeyboardFocusManager(); Container root = + * kfm.getCurrentFocusCycleRoot(); if(root == null) { return; } boolean + * success = false; Component nextComp = null; Container newRoot = root; do + * { nextComp = newRoot instanceof Window ? + * newRoot.getFocusTraversalPolicy() .getDefaultComponent(newRoot) : + * newRoot; newRoot = newRoot.getFocusCycleRootAncestor(); if (nextComp == + * null) { break; } success = nextComp.requestFocusInWindow(); if (newRoot + * == null) { break; } kfm.setGlobalCurrentFocusCycleRoot(newRoot); } while + * (!success); if (!success && root != newRoot) { + * kfm.setGlobalCurrentFocusCycleRoot(root); } } finally { + * toolkit.unlockAWT(); } } + */ + + /** + * Validates that this component has a valid layout. + */ + public void validate() { + toolkit.lockAWT(); + try { + if (!behaviour.isDisplayable()) { + return; + } + validateImpl(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Validate impl. + */ + void validateImpl() { + valid = true; + } + + /** + * Gets the native window. + * + * @return the native window. + */ + NativeWindow getNativeWindow() { + return behaviour.getNativeWindow(); + } + + /** + * Checks whether or not a maximum size is set for the Component. + * + * @return true, if the maximum size is set for the Component, false + * otherwise. + */ + public boolean isMaximumSizeSet() { + toolkit.lockAWT(); + try { + return maximumSize != null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not the minimum size is set for the component. + * + * @return true, if the minimum size is set for the component, false + * otherwise. + */ + public boolean isMinimumSizeSet() { + toolkit.lockAWT(); + try { + return minimumSize != null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks whether or not the preferred size is set for the Component. + * + * @return true, if the preferred size is set for the Component, false + * otherwise. + */ + public boolean isPreferredSizeSet() { + toolkit.lockAWT(); + try { + return preferredSize != null; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the maximum size of the Component. + * + * @return the maximum size of the Component. + */ + public Dimension getMaximumSize() { + toolkit.lockAWT(); + try { + return isMaximumSizeSet() ? new Dimension(maximumSize) : new Dimension(Short.MAX_VALUE, + Short.MAX_VALUE); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the minimum size of the Component. + * + * @return the minimum size of the Component. + */ + public Dimension getMinimumSize() { + toolkit.lockAWT(); + try { + return minimumSize(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Deprecated: replaced by getMinimumSize() method. + * + * @return the Dimension. + * @deprecated Replaced by getMinimumSize() method. + */ + @Deprecated + public Dimension minimumSize() { + toolkit.lockAWT(); + try { + if (isMinimumSizeSet()) { + return (Dimension)minimumSize.clone(); + } + Dimension defSize = getDefaultMinimumSize(); + if (defSize != null) { + return (Dimension)defSize.clone(); + } + return isDisplayable() ? new Dimension(1, 1) : new Dimension(w, h); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the preferred size of the Component. + * + * @return the preferred size of the Component. + */ + public Dimension getPreferredSize() { + toolkit.lockAWT(); + try { + return preferredSize(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Deprecated: replaced by getPreferredSize() method. + * + * @return the Dimension. + * @deprecated Replaced by getPreferredSize() method. + */ + @Deprecated + public Dimension preferredSize() { + toolkit.lockAWT(); + try { + if (isPreferredSizeSet()) { + return new Dimension(preferredSize); + } + Dimension defSize = getDefaultPreferredSize(); + if (defSize != null) { + return new Dimension(defSize); + } + return new Dimension(getMinimumSize()); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the maximum size of the Component. + * + * @param maximumSize + * the new maximum size of the Component. + */ + public void setMaximumSize(Dimension maximumSize) { + Dimension oldMaximumSize; + toolkit.lockAWT(); + try { + oldMaximumSize = this.maximumSize; + if (oldMaximumSize != null) { + oldMaximumSize = oldMaximumSize.getSize(); + } + if (this.maximumSize == null) { + if (maximumSize != null) { + this.maximumSize = new Dimension(maximumSize); + } + } else { + if (maximumSize != null) { + this.maximumSize.setSize(maximumSize); + } else { + this.maximumSize = null; + } + } + } finally { + toolkit.unlockAWT(); + } + firePropertyChange("maximumSize", oldMaximumSize, this.maximumSize); //$NON-NLS-1$ + toolkit.lockAWT(); + try { + // ???AWT: invalidateRealParent(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the minimum size of the Component. + * + * @param minimumSize + * the new minimum size of the Component. + */ + public void setMinimumSize(Dimension minimumSize) { + Dimension oldMinimumSize; + toolkit.lockAWT(); + try { + oldMinimumSize = this.minimumSize; + if (oldMinimumSize != null) { + oldMinimumSize = oldMinimumSize.getSize(); + } + if (this.minimumSize == null) { + if (minimumSize != null) { + this.minimumSize = new Dimension(minimumSize); + } + } else { + if (minimumSize != null) { + this.minimumSize.setSize(minimumSize); + } else { + this.minimumSize = null; + } + } + } finally { + toolkit.unlockAWT(); + } + firePropertyChange("minimumSize", oldMinimumSize, this.minimumSize); //$NON-NLS-1$ + toolkit.lockAWT(); + try { + // ???AWT: invalidateRealParent(); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the preferred size of the Component. + * + * @param preferredSize + * the new preferred size of the Component. + */ + public void setPreferredSize(Dimension preferredSize) { + Dimension oldPreferredSize; + toolkit.lockAWT(); + try { + oldPreferredSize = this.preferredSize; + if (oldPreferredSize != null) { + oldPreferredSize = oldPreferredSize.getSize(); + } + if (this.preferredSize == null) { + if (preferredSize != null) { + this.preferredSize = new Dimension(preferredSize); + } + } else { + if (preferredSize != null) { + this.preferredSize.setSize(preferredSize); + } else { + this.preferredSize = null; + } + } + } finally { + toolkit.unlockAWT(); + } + firePropertyChange("preferredSize", oldPreferredSize, this.preferredSize); //$NON-NLS-1$ + toolkit.lockAWT(); + try { + // ???AWT: invalidateRealParent(); + } finally { + toolkit.unlockAWT(); + } + } + + // ???AWT + /* + * RedrawManager getRedrawManager() { if (parent == null) { return null; } + * return parent.getRedrawManager(); } + */ + + /** + * Checks if is focusability explicitly set. + * + * @return true if component has a focusable peer. + */ + // ???AWT + /* + * boolean isPeerFocusable() { // The recommendations for Windows and Unix + * are that // Canvases, Labels, Panels, Scrollbars, ScrollPanes, Windows, + * // and lightweight Components have non-focusable peers, // and all other + * Components have focusable peers. if (this instanceof Canvas || this + * instanceof Label || this instanceof Panel || this instanceof Scrollbar || + * this instanceof ScrollPane || this instanceof Window || isLightweight()) + * { return false; } return true; } + */ + + /** + * @return true if focusability was explicitly set via a call to + * setFocusable() or via overriding isFocusable() or + * isFocusTraversable(). + */ + boolean isFocusabilityExplicitlySet() { + return calledSetFocusable || overridenIsFocusable; + } + + /** + * Paints the component and all of its subcomponents. + * + * @param g + * the Graphics to be used for painting. + */ + public void paintAll(Graphics g) { + toolkit.lockAWT(); + try { + paint(g); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Updates this Component. + * + * @param g + * the Graphics to be used for updating. + */ + public void update(Graphics g) { + toolkit.lockAWT(); + try { + if (!isLightweight() && !isPrepainter()) { + g.setColor(getBackground()); + g.fillRect(0, 0, w, h); + g.setColor(getForeground()); + } + paint(g); + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Paints this component. + * + * @param g + * the Graphics to be used for painting. + */ + public void paint(Graphics g) { + toolkit.lockAWT(); + try { + // Just to nothing + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Prepares the component to be painted. + * + * @param g + * the Graphics to be used for painting. + */ + void prepaint(Graphics g) { + // Just to nothing. For overriding. + } + + /** + * Checks if is prepainter. + * + * @return true, if is prepainter. + */ + boolean isPrepainter() { + return false; + } + + /** + * Prepare4 hierarchy change. + */ + void prepare4HierarchyChange() { + if (hierarchyChangingCounter++ == 0) { + wasShowing = isShowing(); + wasDisplayable = isDisplayable(); + prepareChildren4HierarchyChange(); + } + } + + /** + * Prepare children4 hierarchy change. + */ + void prepareChildren4HierarchyChange() { + // To be inherited by Container + } + + // ???AWT + /* + * void finishHierarchyChange(Component changed, Container changedParent, + * int ancestorFlags) { if (--hierarchyChangingCounter == 0) { int + * changeFlags = ancestorFlags; if (wasShowing != isShowing()) { changeFlags + * |= HierarchyEvent.SHOWING_CHANGED; } if (wasDisplayable != + * isDisplayable()) { changeFlags |= HierarchyEvent.DISPLAYABILITY_CHANGED; + * } if (changeFlags > 0) { postEvent(new HierarchyEvent(this, + * HierarchyEvent.HIERARCHY_CHANGED, changed, changedParent, changeFlags)); + * } finishChildrenHierarchyChange(changed, changedParent, ancestorFlags); } + * } void finishChildrenHierarchyChange(Component changed, Container + * changedParent, int ancestorFlags) { // To be inherited by Container } + * void postHierarchyBoundsEvents(Component changed, int id) { postEvent(new + * HierarchyEvent(this, id, changed, null, 0)); } + */ + + /** + * Spread hierarchy bounds events. + * + * @param changed + * the changed. + * @param id + * the id. + */ + void spreadHierarchyBoundsEvents(Component changed, int id) { + // To be inherited by Container + } + + /** + * Dispatches an event to this component. + * + * @param e + * the Event. + */ + public final void dispatchEvent(AWTEvent e) { + // ???AWT + /* + * if (e.isConsumed()) { return; } if (e instanceof PaintEvent) { + * toolkit.dispatchAWTEvent(e); processPaintEvent((PaintEvent) e); + * return; } KeyboardFocusManager kfm = + * KeyboardFocusManager.getCurrentKeyboardFocusManager(); if + * (!e.dispatchedByKFM && kfm.dispatchEvent(e)) { return; } if (e + * instanceof KeyEvent) { KeyEvent ke = (KeyEvent) e; // consumes + * KeyEvent which represents a focus traversal key if + * (getFocusTraversalKeysEnabled()) { kfm.processKeyEvent(this, ke); if + * (ke.isConsumed()) { return; } } } if (inputMethodsEnabled && + * dispatchToIM && e.isPosted && dispatchEventToIM(e)) { return; } if + * (e.getID() == WindowEvent.WINDOW_ICONIFIED) { + * notifyInputMethod(null); } AWTEvent.EventDescriptor descriptor = + * toolkit.eventTypeLookup.getEventDescriptor(e); + * toolkit.dispatchAWTEvent(e); if (descriptor != null) { if + * (isEventEnabled(descriptor.eventMask) || + * (getListeners(descriptor.listenerType).length > 0)) { + * processEvent(e); } // input events can be consumed by user listeners: + * if (!e.isConsumed() && ((enabledAWTEvents & descriptor.eventMask) != + * 0)) { postprocessEvent(e, descriptor.eventMask); } } + * postDeprecatedEvent(e); + */ + } + + /** + * Post deprecated event. + * + * @param e + * the e. + */ + private void postDeprecatedEvent(AWTEvent e) { + if (deprecatedEventHandler) { + Event evt = e.getEvent(); + if (evt != null) { + postEvent(evt); + } + } + } + + /** + * Postprocess event. + * + * @param e + * the e. + * @param eventMask + * the event mask. + */ + void postprocessEvent(AWTEvent e, long eventMask) { + toolkit.lockAWT(); + try { + // call system listeners under AWT lock + if (eventMask == AWTEvent.FOCUS_EVENT_MASK) { + preprocessFocusEvent((FocusEvent)e); + } else if (eventMask == AWTEvent.KEY_EVENT_MASK) { + preprocessKeyEvent((KeyEvent)e); + } else if (eventMask == AWTEvent.MOUSE_EVENT_MASK) { + preprocessMouseEvent((MouseEvent)e); + } else if (eventMask == AWTEvent.MOUSE_MOTION_EVENT_MASK) { + preprocessMouseMotionEvent((MouseEvent)e); + } else if (eventMask == AWTEvent.COMPONENT_EVENT_MASK) { + preprocessComponentEvent((ComponentEvent)e); + } else if (eventMask == AWTEvent.MOUSE_WHEEL_EVENT_MASK) { + preprocessMouseWheelEvent((MouseWheelEvent)e); + } else if (eventMask == AWTEvent.INPUT_METHOD_EVENT_MASK) { + preprocessInputMethodEvent((InputMethodEvent)e); + } + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Preprocess input method event. + * + * @param e + * the e. + */ + private void preprocessInputMethodEvent(InputMethodEvent e) { + processInputMethodEventImpl(e, inputMethodListeners.getSystemListeners()); + } + + /** + * Preprocess mouse wheel event. + * + * @param e + * the e. + */ + private void preprocessMouseWheelEvent(MouseWheelEvent e) { + processMouseWheelEventImpl(e, mouseWheelListeners.getSystemListeners()); + } + + /** + * Process mouse wheel event impl. + * + * @param e + * the e. + * @param c + * the c. + */ + private void processMouseWheelEventImpl(MouseWheelEvent e, Collection c) { + for (MouseWheelListener listener : c) { + switch (e.getID()) { + case MouseEvent.MOUSE_WHEEL: + listener.mouseWheelMoved(e); + break; + } + } + } + + /** + * Preprocess component event. + * + * @param e + * the e. + */ + private void preprocessComponentEvent(ComponentEvent e) { + processComponentEventImpl(e, componentListeners.getSystemListeners()); + } + + /** + * Preprocess mouse motion event. + * + * @param e + * the e. + */ + void preprocessMouseMotionEvent(MouseEvent e) { + processMouseMotionEventImpl(e, mouseMotionListeners.getSystemListeners()); + } + + /** + * Preprocess mouse event. + * + * @param e + * the e + */ + void preprocessMouseEvent(MouseEvent e) { + processMouseEventImpl(e, mouseListeners.getSystemListeners()); + } + + /** + * Preprocess key event. + * + * @param e + * the e. + */ + void preprocessKeyEvent(KeyEvent e) { + processKeyEventImpl(e, keyListeners.getSystemListeners()); + } + + /** + * Preprocess focus event. + * + * @param e + * the e. + */ + void preprocessFocusEvent(FocusEvent e) { + processFocusEventImpl(e, focusListeners.getSystemListeners()); + } + + /** + * Processes AWTEvent occurred on this component. + * + * @param e + * the AWTEvent. + */ + protected void processEvent(AWTEvent e) { + long eventMask = toolkit.eventTypeLookup.getEventMask(e); + if (eventMask == AWTEvent.COMPONENT_EVENT_MASK) { + processComponentEvent((ComponentEvent)e); + } else if (eventMask == AWTEvent.FOCUS_EVENT_MASK) { + processFocusEvent((FocusEvent)e); + } else if (eventMask == AWTEvent.KEY_EVENT_MASK) { + processKeyEvent((KeyEvent)e); + } else if (eventMask == AWTEvent.MOUSE_EVENT_MASK) { + processMouseEvent((MouseEvent)e); + } else if (eventMask == AWTEvent.MOUSE_WHEEL_EVENT_MASK) { + processMouseWheelEvent((MouseWheelEvent)e); + } else if (eventMask == AWTEvent.MOUSE_MOTION_EVENT_MASK) { + processMouseMotionEvent((MouseEvent)e); + } else if (eventMask == AWTEvent.HIERARCHY_EVENT_MASK) { + processHierarchyEvent((HierarchyEvent)e); + } else if (eventMask == AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) { + processHierarchyBoundsEvent((HierarchyEvent)e); + } else if (eventMask == AWTEvent.INPUT_METHOD_EVENT_MASK) { + processInputMethodEvent((InputMethodEvent)e); + } + } + + /** + * Gets an array of all listener's objects based on the specified listener + * type and registered to this Component. + * + * @param listenerType + * the listener type. + * @return an array of all listener's objects based on the specified + * listener type and registered to this Component. + */ + @SuppressWarnings("unchecked") + public T[] getListeners(Class listenerType) { + if (ComponentListener.class.isAssignableFrom(listenerType)) { + return (T[])getComponentListeners(); + } else if (FocusListener.class.isAssignableFrom(listenerType)) { + return (T[])getFocusListeners(); + } else if (HierarchyBoundsListener.class.isAssignableFrom(listenerType)) { + return (T[])getHierarchyBoundsListeners(); + } else if (HierarchyListener.class.isAssignableFrom(listenerType)) { + return (T[])getHierarchyListeners(); + } else if (InputMethodListener.class.isAssignableFrom(listenerType)) { + return (T[])getInputMethodListeners(); + } else if (KeyListener.class.isAssignableFrom(listenerType)) { + return (T[])getKeyListeners(); + } else if (MouseWheelListener.class.isAssignableFrom(listenerType)) { + return (T[])getMouseWheelListeners(); + } else if (MouseMotionListener.class.isAssignableFrom(listenerType)) { + return (T[])getMouseMotionListeners(); + } else if (MouseListener.class.isAssignableFrom(listenerType)) { + return (T[])getMouseListeners(); + } else if (PropertyChangeListener.class.isAssignableFrom(listenerType)) { + return (T[])getPropertyChangeListeners(); + } + return (T[])Array.newInstance(listenerType, 0); + } + + /** + * Process paint event. + * + * @param event + * the event. + */ + private void processPaintEvent(PaintEvent event) { + if (redrawManager == null) { + return; + } + Rectangle clipRect = event.getUpdateRect(); + if ((clipRect.width <= 0) || (clipRect.height <= 0)) { + return; + } + Graphics g = getGraphics(); + if (g == null) { + return; + } + initGraphics(g, event); + if (!getIgnoreRepaint()) { + if (event.getID() == PaintEvent.PAINT) { + paint(g); + } else { + update(g); + } + } + g.dispose(); + } + + /** + * Inits the graphics. + * + * @param g + * the g. + * @param e + * the e. + */ + void initGraphics(Graphics g, PaintEvent e) { + Rectangle clip = e.getUpdateRect(); + if (clip instanceof ClipRegion) { + g.setClip(((ClipRegion)clip).getClip()); + } else { + g.setClip(clip); + } + if (isPrepainter()) { + prepaint(g); + } else if (!isLightweight() && (e.getID() == PaintEvent.PAINT)) { + g.setColor(getBackground()); + g.fillRect(0, 0, w, h); + } + g.setFont(getFont()); + g.setColor(getForeground()); + } + + /** + * Enables the events with the specified event mask to be delivered to this + * component. + * + * @param eventsToEnable + * the events mask which specifies the types of events to enable. + */ + protected final void enableEvents(long eventsToEnable) { + toolkit.lockAWT(); + try { + enabledEvents |= eventsToEnable; + deprecatedEventHandler = false; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Enable awt events. + * + * @param eventsToEnable + * the events to enable. + */ + private void enableAWTEvents(long eventsToEnable) { + enabledAWTEvents |= eventsToEnable; + } + + /** + * Disables the events with types specified by the specified event mask from + * being delivered to this component. + * + * @param eventsToDisable + * the event mask specifying the event types. + */ + protected final void disableEvents(long eventsToDisable) { + toolkit.lockAWT(); + try { + enabledEvents &= ~eventsToDisable; + } finally { + toolkit.unlockAWT(); + } + } + + /* + * For use in MouseDispatcher only. Really it checks not only mouse events. + */ + /** + * Checks if is mouse event enabled. + * + * @param eventMask + * the event mask. + * @return true, if is mouse event enabled. + */ + boolean isMouseEventEnabled(long eventMask) { + return (isEventEnabled(eventMask) || (enabledAWTEvents & eventMask) != 0); + } + + /** + * Checks if is event enabled. + * + * @param eventMask + * the event mask. + * @return true, if is event enabled. + */ + boolean isEventEnabled(long eventMask) { + return ((enabledEvents & eventMask) != 0); + } + + /** + * Enables or disables input method support for this component. + * + * @param enable + * true to enable input method support, false to disable it. + */ + public void enableInputMethods(boolean enable) { + toolkit.lockAWT(); + try { + if (!enable) { + removeNotifyInputContext(); + } + inputMethodsEnabled = enable; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets an array of all component's listeners registered for this component. + * + * @return an array of all component's listeners registered for this + * component. + */ + public ComponentListener[] getComponentListeners() { + return componentListeners.getUserListeners(new ComponentListener[0]); + } + + /** + * Adds the specified component listener to the Component for receiving + * component's event. + * + * @param l + * the ComponentListener. + */ + public void addComponentListener(ComponentListener l) { + componentListeners.addUserListener(l); + } + + /** + * Removes the component listener registered for this Component. + * + * @param l + * the ComponentListener. + */ + public void removeComponentListener(ComponentListener l) { + componentListeners.removeUserListener(l); + } + + /** + * Processes a component event that has occurred on this component by + * dispatching them to any registered ComponentListener objects. + * + * @param e + * the ComponentEvent. + */ + protected void processComponentEvent(ComponentEvent e) { + processComponentEventImpl(e, componentListeners.getUserListeners()); + } + + /** + * Process component event impl. + * + * @param e + * the e. + * @param c + * the c. + */ + private void processComponentEventImpl(ComponentEvent e, Collection c) { + for (ComponentListener listener : c) { + switch (e.getID()) { + case ComponentEvent.COMPONENT_HIDDEN: + listener.componentHidden(e); + break; + case ComponentEvent.COMPONENT_MOVED: + listener.componentMoved(e); + break; + case ComponentEvent.COMPONENT_RESIZED: + listener.componentResized(e); + break; + case ComponentEvent.COMPONENT_SHOWN: + listener.componentShown(e); + break; + } + } + } + + /** + * Gets an array of focus listeners registered for this Component. + * + * @return the array of focus listeners registered for this Component. + */ + public FocusListener[] getFocusListeners() { + return focusListeners.getUserListeners(new FocusListener[0]); + } + + /** + * Adds the specified focus listener to the Component for receiving focus + * events. + * + * @param l + * the FocusListener. + */ + public void addFocusListener(FocusListener l) { + focusListeners.addUserListener(l); + } + + /** + * Adds the awt focus listener. + * + * @param l + * the l. + */ + void addAWTFocusListener(FocusListener l) { + enableAWTEvents(AWTEvent.FOCUS_EVENT_MASK); + focusListeners.addSystemListener(l); + } + + /** + * Removes the focus listener registered for this Component. + * + * @param l + * the FocusListener. + */ + public void removeFocusListener(FocusListener l) { + focusListeners.removeUserListener(l); + } + + /** + * Processes a FocusEvent that has occurred on this component by dispatching + * it to the registered listeners. + * + * @param e + * the FocusEvent. + */ + protected void processFocusEvent(FocusEvent e) { + processFocusEventImpl(e, focusListeners.getUserListeners()); + } + + /** + * Process focus event impl. + * + * @param e + * the e. + * @param c + * the c. + */ + private void processFocusEventImpl(FocusEvent e, Collection c) { + for (FocusListener listener : c) { + switch (e.getID()) { + case FocusEvent.FOCUS_GAINED: + listener.focusGained(e); + break; + case FocusEvent.FOCUS_LOST: + listener.focusLost(e); + break; + } + } + } + + /** + * Gets an array of registered HierarchyListeners for this Component. + * + * @return an array of registered HierarchyListeners for this Component. + */ + public HierarchyListener[] getHierarchyListeners() { + return hierarchyListeners.getUserListeners(new HierarchyListener[0]); + } + + /** + * Adds the specified hierarchy listener. + * + * @param l + * the HierarchyListener. + */ + public void addHierarchyListener(HierarchyListener l) { + hierarchyListeners.addUserListener(l); + } + + /** + * Removes the hierarchy listener registered for this component. + * + * @param l + * the HierarchyListener. + */ + public void removeHierarchyListener(HierarchyListener l) { + hierarchyListeners.removeUserListener(l); + } + + /** + * Processes a hierarchy event that has occurred on this component by + * dispatching it to the registered listeners. + * + * @param e + * the HierarchyEvent. + */ + protected void processHierarchyEvent(HierarchyEvent e) { + for (HierarchyListener listener : hierarchyListeners.getUserListeners()) { + switch (e.getID()) { + case HierarchyEvent.HIERARCHY_CHANGED: + listener.hierarchyChanged(e); + break; + } + } + } + + /** + * Gets an array of HierarchyBoundsListener objects registered to this + * Component. + * + * @return an array of HierarchyBoundsListener objects. + */ + public HierarchyBoundsListener[] getHierarchyBoundsListeners() { + return hierarchyBoundsListeners.getUserListeners(new HierarchyBoundsListener[0]); + } + + /** + * Adds the specified hierarchy bounds listener. + * + * @param l + * the HierarchyBoundsListener. + */ + public void addHierarchyBoundsListener(HierarchyBoundsListener l) { + hierarchyBoundsListeners.addUserListener(l); + } + + /** + * Removes the hierarchy bounds listener registered for this Component. + * + * @param l + * the HierarchyBoundsListener. + */ + public void removeHierarchyBoundsListener(HierarchyBoundsListener l) { + hierarchyBoundsListeners.removeUserListener(l); + } + + /** + * Processes a hierarchy bounds event that has occurred on this component by + * dispatching it to the registered listeners. + * + * @param e + * the HierarchyBoundsEvent. + */ + protected void processHierarchyBoundsEvent(HierarchyEvent e) { + for (HierarchyBoundsListener listener : hierarchyBoundsListeners.getUserListeners()) { + switch (e.getID()) { + case HierarchyEvent.ANCESTOR_MOVED: + listener.ancestorMoved(e); + break; + case HierarchyEvent.ANCESTOR_RESIZED: + listener.ancestorResized(e); + break; + } + } + } + + /** + * Gets an array of the key listeners registered to the Component. + * + * @return an array of the key listeners registered to the Component. + */ + public KeyListener[] getKeyListeners() { + return keyListeners.getUserListeners(new KeyListener[0]); + } + + /** + * Adds the specified key listener. + * + * @param l + * the KeyListener. + */ + public void addKeyListener(KeyListener l) { + keyListeners.addUserListener(l); + } + + /** + * Adds the awt key listener. + * + * @param l + * the l. + */ + void addAWTKeyListener(KeyListener l) { + enableAWTEvents(AWTEvent.KEY_EVENT_MASK); + keyListeners.addSystemListener(l); + } + + /** + * Removes the key listener registered for this Component. + * + * @param l + * the KeyListener. + */ + public void removeKeyListener(KeyListener l) { + keyListeners.removeUserListener(l); + } + + /** + * Processes a key event that has occurred on this component by dispatching + * it to the registered listeners. + * + * @param e + * the KeyEvent. + */ + protected void processKeyEvent(KeyEvent e) { + processKeyEventImpl(e, keyListeners.getUserListeners()); + } + + /** + * Process key event impl. + * + * @param e + * the e. + * @param c + * the c. + */ + private void processKeyEventImpl(KeyEvent e, Collection c) { + for (KeyListener listener : c) { + switch (e.getID()) { + case KeyEvent.KEY_PRESSED: + listener.keyPressed(e); + break; + case KeyEvent.KEY_RELEASED: + listener.keyReleased(e); + break; + case KeyEvent.KEY_TYPED: + listener.keyTyped(e); + break; + } + } + } + + /** + * Gets an array of the mouse listeners registered to the Component. + * + * @return an array of the mouse listeners registered to the Component. + */ + public MouseListener[] getMouseListeners() { + return mouseListeners.getUserListeners(new MouseListener[0]); + } + + /** + * Adds the specified mouse listener. + * + * @param l + * the MouseListener. + */ + public void addMouseListener(MouseListener l) { + mouseListeners.addUserListener(l); + } + + /** + * Adds the awt mouse listener. + * + * @param l + * the l. + */ + void addAWTMouseListener(MouseListener l) { + enableAWTEvents(AWTEvent.MOUSE_EVENT_MASK); + mouseListeners.addSystemListener(l); + } + + /** + * Adds the awt mouse motion listener. + * + * @param l + * the l. + */ + void addAWTMouseMotionListener(MouseMotionListener l) { + enableAWTEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK); + mouseMotionListeners.addSystemListener(l); + } + + /** + * Adds the awt component listener. + * + * @param l + * the l. + */ + void addAWTComponentListener(ComponentListener l) { + enableAWTEvents(AWTEvent.COMPONENT_EVENT_MASK); + componentListeners.addSystemListener(l); + } + + /** + * Adds the awt input method listener. + * + * @param l + * the l. + */ + void addAWTInputMethodListener(InputMethodListener l) { + enableAWTEvents(AWTEvent.INPUT_METHOD_EVENT_MASK); + inputMethodListeners.addSystemListener(l); + } + + /** + * Adds the awt mouse wheel listener. + * + * @param l + * the l. + */ + void addAWTMouseWheelListener(MouseWheelListener l) { + enableAWTEvents(AWTEvent.MOUSE_WHEEL_EVENT_MASK); + mouseWheelListeners.addSystemListener(l); + } + + /** + * Removes the mouse listener registered for this Component. + * + * @param l + * the MouseListener. + */ + public void removeMouseListener(MouseListener l) { + mouseListeners.removeUserListener(l); + } + + /** + * Processes a mouse event that has occurred on this component by + * dispatching it to the registered listeners. + * + * @param e + * the MouseEvent. + */ + protected void processMouseEvent(MouseEvent e) { + processMouseEventImpl(e, mouseListeners.getUserListeners()); + } + + /** + * Process mouse event impl. + * + * @param e + * the e. + * @param c + * the c. + */ + private void processMouseEventImpl(MouseEvent e, Collection c) { + for (MouseListener listener : c) { + switch (e.getID()) { + case MouseEvent.MOUSE_CLICKED: + listener.mouseClicked(e); + break; + case MouseEvent.MOUSE_ENTERED: + listener.mouseEntered(e); + break; + case MouseEvent.MOUSE_EXITED: + listener.mouseExited(e); + break; + case MouseEvent.MOUSE_PRESSED: + listener.mousePressed(e); + break; + case MouseEvent.MOUSE_RELEASED: + listener.mouseReleased(e); + break; + } + } + } + + /** + * Process mouse motion event impl. + * + * @param e + * the e. + * @param c + * the c. + */ + private void processMouseMotionEventImpl(MouseEvent e, Collection c) { + for (MouseMotionListener listener : c) { + switch (e.getID()) { + case MouseEvent.MOUSE_DRAGGED: + listener.mouseDragged(e); + break; + case MouseEvent.MOUSE_MOVED: + listener.mouseMoved(e); + break; + } + } + } + + /** + * Gets an array of the mouse motion listeners registered to the Component. + * + * @return an array of the MouseMotionListeners registered to the Component. + */ + public MouseMotionListener[] getMouseMotionListeners() { + return mouseMotionListeners.getUserListeners(new MouseMotionListener[0]); + } + + /** + * Adds the specified mouse motion listener. + * + * @param l + * the MouseMotionListener. + */ + public void addMouseMotionListener(MouseMotionListener l) { + mouseMotionListeners.addUserListener(l); + } + + /** + * Removes the mouse motion listener registered for this component. + * + * @param l + * the MouseMotionListener. + */ + public void removeMouseMotionListener(MouseMotionListener l) { + mouseMotionListeners.removeUserListener(l); + } + + /** + * Processes a mouse motion event that has occurred on this component by + * dispatching it to the registered listeners. + * + * @param e + * the MouseEvent. + */ + protected void processMouseMotionEvent(MouseEvent e) { + processMouseMotionEventImpl(e, mouseMotionListeners.getUserListeners()); + } + + /** + * Gets an array of the mouse wheel listeners registered to the Component. + * + * @return an array of the MouseWheelListeners registered to the Component. + */ + public MouseWheelListener[] getMouseWheelListeners() { + return mouseWheelListeners.getUserListeners(new MouseWheelListener[0]); + } + + /** + * Adds the specified mouse wheel listener. + * + * @param l + * the MouseWheelListener. + */ + public void addMouseWheelListener(MouseWheelListener l) { + mouseWheelListeners.addUserListener(l); + } + + /** + * Removes the mouse wheel listener registered for this component. + * + * @param l + * the MouseWheelListener. + */ + public void removeMouseWheelListener(MouseWheelListener l) { + mouseWheelListeners.removeUserListener(l); + } + + /** + * Processes a mouse wheel event that has occurred on this component by + * dispatching it to the registered listeners. + * + * @param e + * the MouseWheelEvent. + */ + protected void processMouseWheelEvent(MouseWheelEvent e) { + processMouseWheelEventImpl(e, mouseWheelListeners.getUserListeners()); + } + + /** + * Gets an array of the InputMethodListener listeners registered to the + * Component. + * + * @return an array of the InputMethodListener listeners registered to the + * Component. + */ + public InputMethodListener[] getInputMethodListeners() { + return inputMethodListeners.getUserListeners(new InputMethodListener[0]); + } + + /** + * Adds the specified input method listener. + * + * @param l + * the InputMethodListener. + */ + public void addInputMethodListener(InputMethodListener l) { + inputMethodListeners.addUserListener(l); + } + + /** + * Removes the input method listener registered for this component. + * + * @param l + * the InputMethodListener. + */ + public void removeInputMethodListener(InputMethodListener l) { + inputMethodListeners.removeUserListener(l); + } + + /** + * Processes an input method event that has occurred on this component by + * dispatching it to the registered listeners. + * + * @param e + * the InputMethodEvent. + */ + protected void processInputMethodEvent(InputMethodEvent e) { + processInputMethodEventImpl(e, inputMethodListeners.getUserListeners()); + } + + /** + * Process input method event impl. + * + * @param e + * the e. + * @param c + * the c. + */ + private void processInputMethodEventImpl(InputMethodEvent e, Collection c) { + for (InputMethodListener listener : c) { + switch (e.getID()) { + case InputMethodEvent.CARET_POSITION_CHANGED: + listener.caretPositionChanged(e); + break; + case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED: + listener.inputMethodTextChanged(e); + break; + } + } + } + + // ???AWT + /* + * public Point getMousePosition() throws HeadlessException { Point + * absPointerPos = MouseInfo.getPointerInfo().getLocation(); Window + * winUnderPtr = + * toolkit.dispatcher.mouseDispatcher.findWindowAt(absPointerPos); Point + * pointerPos = MouseDispatcher.convertPoint(null, absPointerPos, + * winUnderPtr); boolean isUnderPointer = false; if (winUnderPtr == null) { + * return null; } isUnderPointer = winUnderPtr.isComponentAt(this, + * pointerPos); if (isUnderPointer) { return + * MouseDispatcher.convertPoint(null, absPointerPos, this); } return null; } + */ + + /** + * Set native caret at the given position
+ * Note: this method takes AWT lock inside because it walks through the + * component hierarchy. + * + * @param x + * the x. + * @param y + * the y. + */ + void setCaretPos(final int x, final int y) { + Runnable r = new Runnable() { + public void run() { + toolkit.lockAWT(); + try { + setCaretPosImpl(x, y); + } finally { + toolkit.unlockAWT(); + } + } + }; + if (Thread.currentThread() instanceof EventDispatchThread) { + r.run(); + } else { + toolkit.getSystemEventQueueImpl().postEvent(new InvocationEvent(this, r)); + } + } + + /** + * This method should be called only at event dispatch thread. + * + * @param x + * the x. + * @param y + * the y. + */ + void setCaretPosImpl(int x, int y) { + Component c = this; + while ((c != null) && c.behaviour.isLightweight()) { + x += c.x; + y += c.y; + // ???AWT: c = c.getParent(); + } + if (c == null) { + return; + } + // ???AWT + /* + * if (c instanceof Window) { Insets insets = c.getNativeInsets(); x -= + * insets.left; y -= insets.top; } + * toolkit.getWindowFactory().setCaretPosition(x, y); + */ + } + + // to be overridden in standard components such as Button and List + /** + * Gets the default minimum size. + * + * @return the default minimum size. + */ + Dimension getDefaultMinimumSize() { + return null; + } + + // to be overridden in standard components such as Button and List + /** + * Gets the default preferred size. + * + * @return the default preferred size. + */ + Dimension getDefaultPreferredSize() { + return null; + } + + // to be overridden in standard components such as Button and List + /** + * Reset default size. + */ + void resetDefaultSize() { + } + + // ???AWT + /* + * ComponentBehavior createBehavior() { return new LWBehavior(this); } + */ + + /** + * Gets the default background. + * + * @return the default background. + */ + Color getDefaultBackground() { + // ???AWT: return getWindowAncestor().getDefaultBackground(); + return getBackground(); + } + + /** + * Gets the default foreground. + * + * @return the default foreground. + */ + Color getDefaultForeground() { + // ???AWT return getWindowAncestor().getDefaultForeground(); + return getForeground(); + } + + /** + * Called when native resource for this component is created (for + * heavyweights only). + * + * @param win + * the win. + */ + void nativeWindowCreated(NativeWindow win) { + // to be overridden + } + + /** + * Determine the component's area hidden behind the windows that have higher + * Z-order, including windows of other applications. + * + * @param image + * the image. + * @param destLocation + * the dest location. + * @param destSize + * the dest size. + * @param source + * the source. + * @return the calculated region, or null if it cannot be determined. + */ + // ???AWT + /* + * MultiRectArea getObscuredRegion(Rectangle part) { if (!visible || parent + * == null || !parent.visible) { return null; } Rectangle r = new + * Rectangle(0, 0, w, h); if (part != null) { r = r.intersection(part); } if + * (r.isEmpty()) { return null; } r.translate(x, y); MultiRectArea ret = + * parent.getObscuredRegion(r); if (ret != null) { + * parent.addObscuredRegions(ret, this); ret.translate(-x, -y); + * ret.intersect(new Rectangle(0, 0, w, h)); } return ret; } + */ + + // ???AWT + /* + * private void readObject(ObjectInputStream stream) throws IOException, + * ClassNotFoundException { stream.defaultReadObject(); FieldsAccessor + * accessor = new FieldsAccessor(Component.class, this); + * accessor.set("toolkit", Toolkit.getDefaultToolkit()); //$NON-NLS-1$ + * accessor.set("behaviour", createBehavior()); //$NON-NLS-1$ + * accessor.set("componentLock", new Object()); // $NON-LOCK-1$ + * //$NON-NLS-1$ } + */ + + final void onDrawImage(Image image, Point destLocation, Dimension destSize, Rectangle source) { + ImageParameters imageParams; + if (updatedImages == null) { + updatedImages = new HashMap(); + } + imageParams = updatedImages.get(image); + if (imageParams == null) { + imageParams = new ImageParameters(); + updatedImages.put(image, imageParams); + } + imageParams.addDrawing(destLocation, destSize, source); + } + + public boolean imageUpdate(Image img, int infoflags, int x, int y, int w, int h) { + toolkit.lockAWT(); + try { + boolean done = false; + if ((infoflags & (ALLBITS | FRAMEBITS)) != 0) { + done = true; + } else if ((infoflags & SOMEBITS) != 0 && incrementalImageUpdate) { + done = true; + } + if (done) { + repaint(); + } + return (infoflags & (ABORT | ALLBITS)) == 0; + } finally { + toolkit.unlockAWT(); + } + } + + // ???AWT + /* + * private void invalidateRealParent() { Container realParent = + * getRealParent(); if ((realParent != null) && realParent.isValid()) { + * realParent.invalidate(); } } + */ + + /** + * The Class ImageParameters. + */ + private class ImageParameters { + + /** + * The drawing params. + */ + private final LinkedList drawingParams = new LinkedList(); + + /** + * The size. + */ + Dimension size = new Dimension(Component.this.w, Component.this.h); + + /** + * Adds the drawing. + * + * @param destLocation + * the dest location. + * @param destSize + * the dest size. + * @param source + * the source. + */ + void addDrawing(Point destLocation, Dimension destSize, Rectangle source) { + drawingParams.add(new DrawingParameters(destLocation, destSize, source)); + } + + /** + * Drawing parameters iterator. + * + * @return the iterator< drawing parameters>. + */ + Iterator drawingParametersIterator() { + return drawingParams.iterator(); + } + + /** + * The Class DrawingParameters. + */ + class DrawingParameters { + + /** + * The dest location. + */ + Point destLocation; + + /** + * The dest size. + */ + Dimension destSize; + + /** + * The source. + */ + Rectangle source; + + /** + * Instantiates a new drawing parameters. + * + * @param destLocation + * the dest location. + * @param destSize + * the dest size. + * @param source + * the source. + */ + DrawingParameters(Point destLocation, Dimension destSize, Rectangle source) { + this.destLocation = new Point(destLocation); + if (destSize != null) { + this.destSize = new Dimension(destSize); + } else { + this.destSize = null; + } + if (source != null) { + this.source = new Rectangle(source); + } else { + this.source = null; + } + } + } + } + + /** + * TextComponent support. + * + * @param e + * the e. + * @return true, if dispatch event to im. + */ + // ???AWT + /* + * private TextKit textKit = null; TextKit getTextKit() { return textKit; } + * void setTextKit(TextKit kit) { textKit = kit; } + */ + + /** + * TextField support. + */ + // ???AWT + /* + * private TextFieldKit textFieldKit = null; TextFieldKit getTextFieldKit() + * { return textFieldKit; } void setTextFieldKit(TextFieldKit kit) { + * textFieldKit = kit; } + */ + + /** + * Dispatches input & focus events to input method context. + * + * @param e + * event to pass to InputContext.dispatchEvent(). + * @return true if event was consumed by IM, false otherwise. + */ + private boolean dispatchEventToIM(AWTEvent e) { + InputContext ic = getInputContext(); + if (ic == null) { + return false; + } + int id = e.getID(); + boolean isInputEvent = ((id >= KeyEvent.KEY_FIRST) && (id <= KeyEvent.KEY_LAST)) + || ((id >= MouseEvent.MOUSE_FIRST) && (id <= MouseEvent.MOUSE_LAST)); + if (((id >= FocusEvent.FOCUS_FIRST) && (id <= FocusEvent.FOCUS_LAST)) || isInputEvent) { + ic.dispatchEvent(e); + } + return e.isConsumed(); + } +} diff --git a/awt/java/awt/ComponentBehavior.java b/awt/java/awt/ComponentBehavior.java new file mode 100644 index 000000000..f4e8ffbf2 --- /dev/null +++ b/awt/java/awt/ComponentBehavior.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ +package java.awt; + +import org.apache.harmony.awt.wtk.NativeWindow; + +/** + * The interface of the helper object that encapsulates the difference + * between lightweight and heavyweight components. + */ +interface ComponentBehavior { + + void addNotify(); + + void setBounds(int x, int y, int w, int h, int bMask); + + void setVisible(boolean b); + + Graphics getGraphics(int translationX, int translationY, int width, int height); + + NativeWindow getNativeWindow(); + + boolean isLightweight(); + + void onMove(int x, int y); + + boolean isOpaque(); + + boolean isDisplayable(); + + void setEnabled(boolean value); + + void removeNotify(); + + void setZOrder(int newIndex, int oldIndex); + + boolean setFocus(boolean focus, Component opposite); +} diff --git a/awt/java/awt/ComponentOrientation.java b/awt/java/awt/ComponentOrientation.java new file mode 100644 index 000000000..5acc11a34 --- /dev/null +++ b/awt/java/awt/ComponentOrientation.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov, Dmitry A. Durnev + * @version $Revision$ + */ + +package java.awt; + +import java.io.Serializable; +import java.util.*; + +/** + * The ComponentOrientation class specifies the language-sensitive orientation + * of component's elements or text. It is used to reflect the differences in + * this ordering between different writing systems. The ComponentOrientation + * class indicates the orientation of the elements/text in the horizontal + * direction ("left to right" or "right to left") and in the vertical direction + * ("top to bottom" or "bottom to top"). + * + * @since Android 1.0 + */ +public final class ComponentOrientation implements Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -4113291392143563828L; + + /** + * The Constant LEFT_TO_RIGHT indicates that items run left to right. + */ + public static final ComponentOrientation LEFT_TO_RIGHT = new ComponentOrientation(true, true); + + /** + * The Constant RIGHT_TO_LEFT indicates that items run right to left. + */ + public static final ComponentOrientation RIGHT_TO_LEFT = new ComponentOrientation(true, false); + + /** + * The Constant UNKNOWN indicates that a component's orientation is not set. + */ + public static final ComponentOrientation UNKNOWN = new ComponentOrientation(true, true); + + /** + * The Constant rlLangs. + */ + private static final Set rlLangs = new HashSet(); // RIGHT_TO_LEFT + + // languages + + /** + * The horizontal. + */ + private final boolean horizontal; + + /** + * The left2right. + */ + private final boolean left2right; + + static { + rlLangs.add("ar"); //$NON-NLS-1$ + rlLangs.add("fa"); //$NON-NLS-1$ + rlLangs.add("iw"); //$NON-NLS-1$ + rlLangs.add("ur"); //$NON-NLS-1$ + } + + /** + * Gets the orientation for the given ResourceBundle's localization. + * + * @param bdl + * the ResourceBundle. + * @return the ComponentOrientation. + * @deprecated Use getOrientation(java.util.Locale) method. + */ + @Deprecated + public static ComponentOrientation getOrientation(ResourceBundle bdl) { + Object obj = null; + try { + obj = bdl.getObject("Orientation"); //$NON-NLS-1$ + } catch (MissingResourceException mre) { + obj = null; + } + if (obj instanceof ComponentOrientation) { + return (ComponentOrientation)obj; + } + Locale locale = bdl.getLocale(); + if (locale == null) { + locale = Locale.getDefault(); + } + return getOrientation(locale); + } + + /** + * Gets the orientation for the specified locale. + * + * @param locale + * the specified Locale. + * @return the ComponentOrientation. + */ + public static ComponentOrientation getOrientation(Locale locale) { + String lang = locale.getLanguage(); + return rlLangs.contains(lang) ? RIGHT_TO_LEFT : LEFT_TO_RIGHT; + } + + /** + * Instantiates a new component orientation. + * + * @param hor + * whether the items should be arranged horizontally. + * @param l2r + * whether this orientation specifies a left-to-right flow. + */ + private ComponentOrientation(boolean hor, boolean l2r) { + horizontal = hor; + left2right = l2r; + } + + /** + * Returns true if the text of the of writing systems arranged horizontally. + * + * @return true, if the text is written horizontally, false for a vertical + * arrangement. + */ + public boolean isHorizontal() { + return horizontal; + } + + /** + * Returns true if the text is arranged from left to right. + * + * @return true, for writing systems written from left to right; false for + * right-to-left. + */ + public boolean isLeftToRight() { + return left2right; + } + +} diff --git a/awt/java/awt/Composite.java b/awt/java/awt/Composite.java new file mode 100644 index 000000000..d1730fef4 --- /dev/null +++ b/awt/java/awt/Composite.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt; + +import java.awt.image.ColorModel; + +/** + * The Composite interface allows the methods to compose a draw primitive on the + * graphics area. The classes implementing this interface provides the rules and + * a method to create the context for a particular operation. + * + * @since Android 1.0 + */ +public interface Composite { + + /** + * Creates a CompositeContext which defines the encapsulated and optimized + * environment for a compositing operation. Several contexts can exist for a + * single Composite object. + * + * @param srcColorModel + * the source's ColorModel. + * @param dstColorModel + * the destination's ColorModel. + * @param hints + * the RenderingHints. + * @return the CompositeContext object. + */ + public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, + RenderingHints hints); + +} diff --git a/awt/java/awt/CompositeContext.java b/awt/java/awt/CompositeContext.java new file mode 100644 index 000000000..795640d42 --- /dev/null +++ b/awt/java/awt/CompositeContext.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt; + +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +/** + * The CompositeContext interface specifies the encapsulated and optimized + * environment for a compositing operation. + * + * @since Android 1.0 + */ +public interface CompositeContext { + + /** + * Composes the two source Raster objects and places the result in the + * destination WritableRaster. + * + * @param src + * the source Raster. + * @param dstIn + * the destination Raster. + * @param dstOut + * the WritableRaster object where the result of composing + * operation is stored. + */ + public void compose(Raster src, Raster dstIn, WritableRaster dstOut); + + /** + * Releases resources allocated for a context. + */ + public void dispose(); + +} diff --git a/awt/java/awt/Cursor.java b/awt/java/awt/Cursor.java new file mode 100644 index 000000000..0a0cc8498 --- /dev/null +++ b/awt/java/awt/Cursor.java @@ -0,0 +1,427 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ + +package java.awt; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.awt.wtk.NativeCursor; + +/** + * The Cursor class represents the bitmap of the mouse cursor. + * + * @since Android 1.0 + */ +public class Cursor implements Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 8028237497568985504L; + + /** + * The Constant DEFAULT_CURSOR indicates the default cursor type. + */ + public static final int DEFAULT_CURSOR = 0; + + /** + * The Constant CROSSHAIR_CURSOR cursor type. + */ + public static final int CROSSHAIR_CURSOR = 1; + + /** + * The Constant TEXT_CURSOR cursor type. + */ + public static final int TEXT_CURSOR = 2; + + /** + * The Constant WAIT_CURSOR cursor type. + */ + public static final int WAIT_CURSOR = 3; + + /** + * The Constant SW_RESIZE_CURSOR cursor type. + */ + public static final int SW_RESIZE_CURSOR = 4; + + /** + * The Constant SE_RESIZE_CURSOR cursor type. + */ + public static final int SE_RESIZE_CURSOR = 5; + + /** + * The Constant NW_RESIZE_CURSOR cursor type. + */ + public static final int NW_RESIZE_CURSOR = 6; + + /** + * The Constant NE_RESIZE_CURSOR cursor type. + */ + public static final int NE_RESIZE_CURSOR = 7; + + /** + * The Constant N_RESIZE_CURSOR cursor type. + */ + public static final int N_RESIZE_CURSOR = 8; + + /** + * The Constant S_RESIZE_CURSOR cursor type. + */ + public static final int S_RESIZE_CURSOR = 9; + + /** + * The Constant W_RESIZE_CURSOR cursor type. + */ + public static final int W_RESIZE_CURSOR = 10; + + /** + * The Constant E_RESIZE_CURSOR cursor type. + */ + public static final int E_RESIZE_CURSOR = 11; + + /** + * The Constant HAND_CURSOR cursor type. + */ + public static final int HAND_CURSOR = 12; + + /** + * The Constant MOVE_CURSOR cursor type. + */ + public static final int MOVE_CURSOR = 13; + + /** + * A mapping from names to system custom cursors. + */ + static Map systemCustomCursors; + + /** + * The cursor props. + */ + static Properties cursorProps; + + /** + * The Constant predefinedNames. + */ + static final String[] predefinedNames = { + "Default", "Crosshair", "Text", "Wait", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + "Southwest Resize", "Southeast Resize", //$NON-NLS-1$ //$NON-NLS-2$ + "Northwest Resize", "Northeast Resize", //$NON-NLS-1$ //$NON-NLS-2$ + "North Resize", "South Resize", "West Resize", "East Resize", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + "Hand", "Move" //$NON-NLS-1$ //$NON-NLS-2$ + + }; + + /** + * The predefined set of cursors. + */ + protected static Cursor[] predefined = { + new Cursor(DEFAULT_CURSOR), null, null, null, null, null, null, null, null, null, null, + null, null, null + }; + + /** + * The Constant CUSTOM_CURSOR is associated with all custom cursor types. + * (Those which are not predefined) + */ + public static final int CUSTOM_CURSOR = -1; + + /** + * The name of the cursor. + */ + protected String name; + + /** + * The type of the cursor, chosen from the list of cursor type constants. + */ + private final int type; + + /** + * The native cursor. + */ + private transient NativeCursor nativeCursor; + + /** + * The exact point on the cursor image that indicates which point the cursor + * is selecting (pointing to). The coordinates are given with respect the + * origin of the Image (its upper left corner). + */ + private Point hotSpot; + + /** + * The image to draw on the screen representing the cursor. + */ + private Image image; + + /** + * Instantiates a new cursor with the specified name. + * + * @param name + * the name of cursor. + */ + protected Cursor(String name) { + this(name, null, new Point()); + } + + /** + * Instantiates a new cursor of the specified type. + * + * @param type + * the type of cursor. + */ + public Cursor(int type) { + checkType(type); + this.type = type; + if ((type >= 0) && (type < predefinedNames.length)) { + name = predefinedNames[type] + " Cursor"; //$NON-NLS-1$ + } + } + + /** + * Instantiates a new cursor. + * + * @param name + * the name. + * @param img + * the img. + * @param hotSpot + * the hot spot. + */ + Cursor(String name, Image img, Point hotSpot) { + this.name = name; + type = CUSTOM_CURSOR; + this.hotSpot = hotSpot; + image = img; + } + + /** + * Finalize method overrides the finalize method from Object class. + * + * @throws Throwable + * if the native cursor is not null and throws a Throwable when + * destroyed. + */ + @Override + protected void finalize() throws Throwable { + if (nativeCursor != null) { + nativeCursor.destroyCursor(); + } + } + + /** + * Gets the name of the cursor. + * + * @return the name of the cursor. + */ + public String getName() { + return name; + } + + /** + * Returns the String representation of the cursor. + * + * @return the String representation of the cursor. + */ + @Override + public String toString() { + return getClass().getName() + "[" + name + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Gets the cursor type. + * + * @return the cursor type. + */ + public int getType() { + return type; + } + + /** + * Gets the predefined cursor with the specified type. + * + * @param type + * the type of cursor. + * @return the predefined cursor with the specified type. + */ + public static Cursor getPredefinedCursor(int type) { + checkType(type); + Cursor cursor = predefined[type]; + if (cursor == null) { + cursor = new Cursor(type); + predefined[type] = cursor; + } + return cursor; + } + + /** + * Gets the default cursor. + * + * @return the default cursor. + */ + public static Cursor getDefaultCursor() { + return getPredefinedCursor(DEFAULT_CURSOR); + } + + /** + * Gets the specified system custom cursor. + * + * @param name + * the name of the desired system cursor. + * @return the specific system cursor with the specified name. + * @throws AWTException + * if the desired cursor has malformed data such as an + * incorrectly defined hot spot. + * @throws HeadlessException + * if the isHeadless method of the GraphicsEnvironment returns + * true. + */ + public static Cursor getSystemCustomCursor(String name) throws AWTException, HeadlessException { + Toolkit.checkHeadless(); + return getSystemCustomCursorFromMap(name); + } + + /** + * Gets the specified system custom cursor from the map of system custom + * cursors. + * + * @param name + * the name of the desired cursor. + * @return the desired system custom cursor from the map of system custom + * cursors. + * @throws AWTException + * the AWT exception. + */ + private static Cursor getSystemCustomCursorFromMap(String name) throws AWTException { + loadCursorProps(); + if (systemCustomCursors == null) { + systemCustomCursors = new HashMap(); + } + Cursor cursor = systemCustomCursors.get(name); + if (cursor != null) { + return cursor; + } + // awt.141=failed to parse hotspot property for cursor: + String exMsg = Messages.getString("awt.141") + name; //$NON-NLS-1$ + String nm = "Cursor." + name; //$NON-NLS-1$ + String nameStr = cursorProps.getProperty(nm + ".Name"); //$NON-NLS-1$ + String hotSpotStr = cursorProps.getProperty(nm + ".HotSpot"); //$NON-NLS-1$ + String fileStr = cursorProps.getProperty(nm + ".File"); //$NON-NLS-1$ + int idx = hotSpotStr.indexOf(','); + if (idx < 0) { + throw new AWTException(exMsg); + } + int x, y; + try { + x = new Integer(hotSpotStr.substring(0, idx)).intValue(); + y = new Integer(hotSpotStr.substring(idx + 1, hotSpotStr.length())).intValue(); + } catch (NumberFormatException nfe) { + throw new AWTException(exMsg); + } + Image img = Toolkit.getDefaultToolkit().createImage(fileStr); + cursor = new Cursor(nameStr, img, new Point(x, y)); + systemCustomCursors.put(name, cursor); + + return cursor; + } + + /** + * Load cursor props. + * + * @throws AWTException + * the AWT exception. + */ + private static void loadCursorProps() throws AWTException { + if (cursorProps != null) { + return; + } + String sep = File.separator; + String cursorsDir = "lib" + sep + "images" + sep + "cursors"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + String cursorsAbsDir = System.getProperty("java.home") + sep + //$NON-NLS-1$ + cursorsDir; + String cursorPropsFileName = "cursors.properties"; //$NON-NLS-1$ + String cursorPropsFullFileName = (cursorsAbsDir + sep + cursorPropsFileName); + cursorProps = new Properties(); + try { + cursorProps.load(new FileInputStream(new File(cursorPropsFullFileName))); + } catch (FileNotFoundException e) { + // awt.142=Exception: class {0} {1} occurred while loading: {2} + throw new AWTException(Messages.getString("awt.142",//$NON-NLS-1$ + new Object[] { + e.getClass(), e.getMessage(), cursorPropsFullFileName + })); + } catch (IOException e) { + throw new AWTException(e.getMessage()); + } + + } + + /** + * Check type. + * + * @param type + * the type. + */ + static void checkType(int type) { + // can't use predefined array here because it may not have been + // initialized yet + if ((type < 0) || (type >= predefinedNames.length)) { + // awt.143=illegal cursor type + throw new IllegalArgumentException(Messages.getString("awt.143")); //$NON-NLS-1$ + } + } + + // "lazily" create native cursors: + /** + * Gets the native cursor. + * + * @return the native cursor. + */ + NativeCursor getNativeCursor() { + if (nativeCursor != null) { + return nativeCursor; + } + Toolkit toolkit = Toolkit.getDefaultToolkit(); + if (type != CUSTOM_CURSOR) { + nativeCursor = toolkit.createNativeCursor(type); + } else { + nativeCursor = toolkit.createCustomNativeCursor(image, hotSpot, name); + } + return nativeCursor; + } + + /** + * Sets the native cursor. + * + * @param nativeCursor + * the new native cursor. + */ + void setNativeCursor(NativeCursor nativeCursor) { + this.nativeCursor = nativeCursor; + } +} diff --git a/awt/java/awt/Dimension.java b/awt/java/awt/Dimension.java new file mode 100644 index 000000000..6777962e7 --- /dev/null +++ b/awt/java/awt/Dimension.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt; + +import java.awt.geom.Dimension2D; +import java.io.Serializable; + +import org.apache.harmony.misc.HashCode; + +/** + * The Dimension represents the size (width and height) of a component. The + * width and height values can be negative, but in that case the behavior of + * some methods is unexpected. + * + * @since Android 1.0 + */ +public class Dimension extends Dimension2D implements Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 4723952579491349524L; + + /** + * The width dimension. + */ + public int width; + + /** + * The height dimension. + */ + public int height; + + /** + * Instantiates a new Dimension with the same data as the specified + * Dimension. + * + * @param d + * the Dimension to copy the data from when creating the new + * Dimension object. + */ + public Dimension(Dimension d) { + this(d.width, d.height); + } + + /** + * Instantiates a new Dimension with zero width and height. + */ + public Dimension() { + this(0, 0); + } + + /** + * Instantiates a new Dimension with the specified width and height. + * + * @param width + * the width of the new Dimension. + * @param height + * the height of the new Dimension. + */ + public Dimension(int width, int height) { + setSize(width, height); + } + + /** + * Returns the hash code of the Dimension. + * + * @return the hash code of the Dimension. + */ + @Override + public int hashCode() { + HashCode hash = new HashCode(); + hash.append(width); + hash.append(height); + return hash.hashCode(); + } + + /** + * Compares this Dimension object with the specified object. + * + * @param obj + * the Object to be compared. + * @return true, if the specified Object is a Dimension with the same width + * and height data as this Dimension. + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Dimension) { + Dimension d = (Dimension)obj; + return (d.width == width && d.height == height); + } + return false; + } + + /** + * Returns the String associated to this Dimension object. + * + * @return the String associated to this Dimension object. + */ + @Override + public String toString() { + // The output format based on 1.5 release behaviour. It could be + // obtained in the following way + // System.out.println(new Dimension().toString()) + return getClass().getName() + "[width=" + width + ",height=" + height + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + /** + * Sets the size of this Dimension object with the specified width and + * height. + * + * @param width + * the width of the Dimension. + * @param height + * the height of the Dimension. + */ + public void setSize(int width, int height) { + this.width = width; + this.height = height; + } + + /** + * Sets the size of this Dimension object by copying the data from the + * specified Dimension object. + * + * @param d + * the Dimension that gives the new size values. + */ + public void setSize(Dimension d) { + setSize(d.width, d.height); + } + + /** + * Sets the size of this Dimension object with the specified double width + * and height. + * + * @param width + * the width of the Dimension. + * @param height + * the height of the Dimension. + * @see java.awt.geom.Dimension2D#setSize(double, double) + */ + @Override + public void setSize(double width, double height) { + setSize((int)Math.ceil(width), (int)Math.ceil(height)); + } + + /** + * Gets the size of the Dimension. + * + * @return the size of the Dimension. + */ + public Dimension getSize() { + return new Dimension(width, height); + } + + /** + * Gets the height of the Dimension. + * + * @return the height of the Dimension. + * @see java.awt.geom.Dimension2D#getHeight() + */ + @Override + public double getHeight() { + return height; + } + + /** + * Gets the width of the Dimension. + * + * @return the width of the Dimension. + * @see java.awt.geom.Dimension2D#getWidth() + */ + @Override + public double getWidth() { + return width; + } + +} diff --git a/awt/java/awt/Dispatcher.java b/awt/java/awt/Dispatcher.java new file mode 100644 index 000000000..d457af460 --- /dev/null +++ b/awt/java/awt/Dispatcher.java @@ -0,0 +1,723 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov, Dmitry A. Durnev + * @version $Revision$ + */ +package java.awt; + +import java.awt.event.ComponentEvent; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.PaintEvent; +import java.awt.event.WindowEvent; + +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.awt.wtk.NativeEvent; +import org.apache.harmony.awt.wtk.NativeWindow; + + +/** + * Helper package-private class for managing lightweight components & + * dispatching events from heavyweight source + */ +class Dispatcher { + + //???AWT: final PopupDispatcher popupDispatcher = new PopupDispatcher(); + + //???AWT: final FocusDispatcher focusDispatcher; + + final MouseGrabManager mouseGrabManager = new MouseGrabManager(); + + final MouseDispatcher mouseDispatcher; + + private final ComponentDispatcher componentDispatcher = new ComponentDispatcher(); + + private final KeyDispatcher keyDispatcher = new KeyDispatcher(); + + private final Toolkit toolkit; + + int clickInterval = 250; + + /** + * @param toolkit - AWT toolkit + */ + Dispatcher(Toolkit toolkit) { + this.toolkit = toolkit; + + //???AWT: focusDispatcher = new FocusDispatcher(toolkit); + mouseDispatcher = new MouseDispatcher(mouseGrabManager, toolkit); + } + + /** + * Dispatch native event: produce appropriate AWT events, + * update component's fields when needed + * @param event - native event to dispatch + * @return - true means default processing by OS is not needed + */ + public boolean onEvent(NativeEvent event) { + int eventId = event.getEventId(); + + if (eventId == NativeEvent.ID_CREATED) { + return toolkit.onWindowCreated(event.getWindowId()); + } else if (eventId == NativeEvent.ID_MOUSE_GRAB_CANCELED) { + return mouseGrabManager.onGrabCanceled(); + //???AWT +// } else if (popupDispatcher.onEvent(event)) { +// return false; + } else { + Component src = toolkit.getComponentById(event.getWindowId()); + + if (src != null) { + if (((eventId >= ComponentEvent.COMPONENT_FIRST) && (eventId <= ComponentEvent.COMPONENT_LAST)) + || ((eventId >= WindowEvent.WINDOW_FIRST) && (eventId <= WindowEvent.WINDOW_LAST)) + || (eventId == NativeEvent.ID_INSETS_CHANGED) + || (eventId == NativeEvent.ID_BOUNDS_CHANGED) + || (eventId == NativeEvent.ID_THEME_CHANGED)) { + return componentDispatcher.dispatch(src, event); + } else if ((eventId >= MouseEvent.MOUSE_FIRST) + && (eventId <= MouseEvent.MOUSE_LAST)) { + return mouseDispatcher.dispatch(src, event); + } else if (eventId == PaintEvent.PAINT) { + //???AWT: src.redrawManager.addPaintRegion(src, event.getClipRects()); + return true; + } + } + if ((eventId >= FocusEvent.FOCUS_FIRST) + && (eventId <= FocusEvent.FOCUS_LAST)) { + + //???AWT: return focusDispatcher.dispatch(src, event); + return false; + } else if ((eventId >= KeyEvent.KEY_FIRST) + && (eventId <= KeyEvent.KEY_LAST)) { + return keyDispatcher.dispatch(src, event); + } + } + + return false; + } + + /** + * The dispatcher of native events that affect + * component's state or bounds + */ + final class ComponentDispatcher { + + /** + * Handle native event that affects component's state or bounds + * @param src - the component updated by the event + * @param event - the native event + * @return - as in Dispatcher.onEvent() + * @see Dispatcher#onEvent(NativeEvent) + */ + boolean dispatch(Component src, NativeEvent event) { + int id = event.getEventId(); + + if ((id == NativeEvent.ID_INSETS_CHANGED) + || (id == NativeEvent.ID_THEME_CHANGED)) { + return dispatchInsets(event, src); + } else if ((id >= WindowEvent.WINDOW_FIRST) + && (id <= WindowEvent.WINDOW_LAST)) { + return dispatchWindow(event, src); + } else { + return dispatchPureComponent(event, src); + } + } + + /** + * Handle the change of top-level window's native decorations + * @param event - the native event + * @param src - the component updated by the event + * @return - as in Dispatcher.onEvent() + * @see Dispatcher#onEvent(NativeEvent) + */ + boolean dispatchInsets(NativeEvent event, Component src) { + //???AWT + /* + if (src instanceof Window) { + ((Window) src).setNativeInsets(event.getInsets()); + } + */ + return false; + } + + /** + * Handle the change of top-level window's state + * @param event - the native event + * @param src - the component updated by the event + * @return - as in Dispatcher.onEvent() + * @see Dispatcher#onEvent(NativeEvent) + */ + boolean dispatchWindow(NativeEvent event, Component src) { + //???AWT + /* + Window window = (Window) src; + int id = event.getEventId(); + + if (id == WindowEvent.WINDOW_CLOSING) { + toolkit.getSystemEventQueueImpl().postEvent( + new WindowEvent(window, WindowEvent.WINDOW_CLOSING)); + + return true; + } else if (id == WindowEvent.WINDOW_STATE_CHANGED) { + if (window instanceof Frame) { + ((Frame) window) + .updateExtendedState(event.getWindowState()); + } + } + */ + + return false; + } + + /** + * Handle the change of component's size and/or position + * @param event - the native event + * @param src - the component updated by the event + * @return - as in Dispatcher.onEvent() + * @see Dispatcher#onEvent(NativeEvent) + */ + private boolean dispatchPureComponent(NativeEvent event, Component src) { + Rectangle rect = event.getWindowRect(); + Point loc = rect.getLocation(); + int mask; + + switch (event.getEventId()) { + case NativeEvent.ID_BOUNDS_CHANGED: + mask = 0; + break; + case ComponentEvent.COMPONENT_MOVED: + mask = NativeWindow.BOUNDS_NOSIZE; + break; + case ComponentEvent.COMPONENT_RESIZED: + mask = NativeWindow.BOUNDS_NOMOVE; + break; + default: + // awt.12E=Unknown component event id. + throw new RuntimeException(Messages.getString("awt.12E")); //$NON-NLS-1$ + } + + //???AWT + /* + if (!(src instanceof Window)) { + Component compTo = src.getParent(); + Component compFrom = src.getHWAncestor(); + + if ((compTo != null) && (compFrom != null)) { + loc = MouseDispatcher.convertPoint(compFrom, loc, compTo); + } + } else { + int windowState = event.getWindowState(); + + if ((windowState >= 0) && (src instanceof Frame)) { + ((Frame) src).updateExtendedState(windowState); + } + } + src.setBounds(loc.x, loc.y, rect.width, rect.height, mask, false); + */ + + return false; + } + + } + + /** + * The dispatcher of the keyboard events + */ + final class KeyDispatcher { + + /** + * Handle the keyboard event using the KeyboardFocusManager + * @param src - the component receiving the event + * @param event - the native event + * @return - as in Dispatcher.onEvent() + * @see Dispatcher#onEvent(NativeEvent) + */ + boolean dispatch(Component src, NativeEvent event) { + int id = event.getEventId(); + int modifiers = event.getInputModifiers(); + int location = event.getKeyLocation(); + int code = event.getVKey(); + StringBuffer chars = event.getKeyChars(); + int charsLength = chars.length(); + long time = event.getTime(); + char keyChar = event.getLastChar(); + + //???AWT + /* + if (src == null) { + //retarget focus proxy key events to focusOwner: + Window focusProxyOwner = toolkit.getFocusProxyOwnerById(event + .getWindowId()); + if (focusProxyOwner == null) { + return false; + } + src = KeyboardFocusManager.actualFocusOwner; + } + */ + + EventQueue eventQueue = toolkit.getSystemEventQueueImpl(); + + if (src != null) { + eventQueue.postEvent(new KeyEvent(src, id, time, modifiers, + code, keyChar, location)); + // KEY_TYPED goes after KEY_PRESSED + if (id == KeyEvent.KEY_PRESSED) { + for (int i = 0; i < charsLength; i++) { + keyChar = chars.charAt(i); + if (keyChar != KeyEvent.CHAR_UNDEFINED) { + eventQueue.postEvent(new KeyEvent(src, + KeyEvent.KEY_TYPED, time, modifiers, + KeyEvent.VK_UNDEFINED, keyChar, + KeyEvent.KEY_LOCATION_UNKNOWN)); + } + } + } + } + + return false; + } + + } + + /** + * Retargets the mouse events to the grab owner when mouse is grabbed, + * grab and ungrab mouse when mouse buttons are pressed and released + */ + + static final class MouseGrabManager { + + /** + * The top-level window holding the mouse grab + * that was explicitly started by startGrab() method + */ + //???AWT: private Window nativeGrabOwner = null; + /** + * The component that owns the synthetic + * mouse grab while at least one of the + * mouse buttons is pressed + */ + private Component syntheticGrabOwner = null; + + /** + * Previous value of syntheticGrabOwner + */ + private Component lastSyntheticGrabOwner = null; + + /** + * Number of mouse buttons currently pressed + */ + private int syntheticGrabDepth = 0; + + /** + * The callback to be called when the explicit mouse grab ends + */ + private Runnable whenCanceled; + + /** + * Explicitly start the mouse grab + * @param grabWindow - the window that will own the grab + * @param whenCanceled - the callback to call when the grab ends. + * This parameter can be null + */ + //???AWT + /* + void startGrab(Window grabWindow, Runnable whenCanceled) { + + if (nativeGrabOwner != null) { + // awt.12F=Attempt to start nested mouse grab + throw new RuntimeException(Messages.getString("awt.12F")); //$NON-NLS-1$ + } + + NativeWindow win = grabWindow.getNativeWindow(); + if (win == null) { + // awt.130=Attempt to grab mouse in not displayable window + throw new RuntimeException(Messages.getString("awt.130")); //$NON-NLS-1$ + } + + nativeGrabOwner = grabWindow; + this.whenCanceled = whenCanceled; + win.grabMouse(); + } + */ + + /** + * Ends the explicit mouse grab. If the non-null callback was provided + * in the startGrab() method, this callback is called + */ + void endGrab() { + //???AWT + /* + if (nativeGrabOwner == null) { + return; + } + + Window grabWindow = nativeGrabOwner; + nativeGrabOwner = null; + NativeWindow win = grabWindow.getNativeWindow(); + + if (win != null) { + win.ungrabMouse(); + if (whenCanceled != null) { + whenCanceled.run(); + whenCanceled = null; + } + } + */ + } + + /** + * Ends both explicit and synthetic grans + * @return - always returns false + */ + boolean onGrabCanceled() { + endGrab(); + resetSyntheticGrab(); + + return false; + } + + /** + * Starts the synthetic mouse grab, increases the counter + * of currently pressed mouse buttons + * @param source - the component where mouse press event occured + * @return - the component that owns the synthetic grab + */ + Component onMousePressed(Component source) { + if (syntheticGrabDepth == 0) { + syntheticGrabOwner = source; + lastSyntheticGrabOwner = source; + } + syntheticGrabDepth++; + + return syntheticGrabOwner; + } + + /** + * Decreases the counter of currently pressed mouse buttons, + * ends the synthetic mouse grab, when this counter becomes zero + * @param source - the component where mouse press event occured + * @return - the component that owns the synthetic grab, + * or source parameter if mouse grab was released + */ + Component onMouseReleased(Component source) { + Component ret = source; + + //???AWT + /* + if (syntheticGrabOwner != null && nativeGrabOwner == null) { + ret = syntheticGrabOwner; + } + */ + syntheticGrabDepth--; + if (syntheticGrabDepth <= 0) { + resetSyntheticGrab(); + lastSyntheticGrabOwner = null; + } + + return ret; + } + + /** + * Update the state of synthetic ouse gram + * when the mouse is moved/dragged + * @param event - the native event + */ + void preprocessEvent(NativeEvent event) { + int id = event.getEventId(); + switch (id) { + case MouseEvent.MOUSE_MOVED: + if (syntheticGrabOwner != null) { + syntheticGrabOwner = null; + syntheticGrabDepth = 0; + } + if (lastSyntheticGrabOwner != null) { + lastSyntheticGrabOwner = null; + } + case MouseEvent.MOUSE_DRAGGED: + if (syntheticGrabOwner == null + && lastSyntheticGrabOwner != null) { + syntheticGrabOwner = lastSyntheticGrabOwner; + syntheticGrabDepth = 0; + int mask = event.getInputModifiers(); + syntheticGrabDepth += (mask & InputEvent.BUTTON1_DOWN_MASK) != 0 ? 1 + : 0; + syntheticGrabDepth += (mask & InputEvent.BUTTON2_DOWN_MASK) != 0 ? 1 + : 0; + syntheticGrabDepth += (mask & InputEvent.BUTTON3_DOWN_MASK) != 0 ? 1 + : 0; + } + } + } + + /** + * @return the component that currently owns the synthetic grab + */ + Component getSyntheticGrabOwner() { + return syntheticGrabOwner; + } + + /** + * ends synthetic grab + */ + private void resetSyntheticGrab() { + syntheticGrabOwner = null; + syntheticGrabDepth = 0; + } + + } + + /** + * Dispatches native events related to the pop-up boxes + * (the non-component windows such as menus and drop lists) + */ +// final class PopupDispatcher { +// +// private PopupBox activePopup; +// +// private PopupBox underCursor; +// +// private final MouseGrab grab = new MouseGrab(); +// +// /** +// * Handles the mouse grab for pop-up boxes +// */ +// private final class MouseGrab { +// private int depth; +// +// private PopupBox owner; +// +// private final Point start = new Point(); +// +// /** +// * Starts the grab when mouse is pressed +// * @param src - the pop-up box where mouse event has occured +// * @param where - the mouse pointer location +// * @return - the grab owner +// */ +// PopupBox mousePressed(PopupBox src, Point where) { +// if (depth == 0) { +// owner = src; +// start.setLocation(where); +// } +// depth++; +// return owner; +// } +// +// /** +// * Ends the grab when all mousebuttons are released +// * @param src - the pop-up box where mouse event has occured +// * @param where - the mouse pointer location +// * @return - the grab owner, or src parameter if the grab has ended +// */ +// PopupBox mouseReleased(PopupBox src, Point where) { +// PopupBox ret = (owner != null) ? owner : src; +// if (depth == 0) { +// return ret; +// } +// depth--; +// if (depth == 0) { +// PopupBox tgt = owner; +// owner = null; +// if (tgt != null && src == null) { +// Point a = new Point(start); +// Point b = new Point(where); +// Point pos = tgt.getScreenLocation(); +// a.translate(-pos.x, -pos.y); +// b.translate(-pos.x, -pos.y); +// if (tgt.closeOnUngrab(a, b)) { +// return null; +// } +// } +// } +// return ret; +// } +// +// /** +// * Set the grab owner to null +// */ +// void reset() { +// depth = 0; +// owner = null; +// start.setLocation(0, 0); +// } +// +// /** +// * @return - the pop-up box currently owning the grab +// */ +// public PopupBox getOwner() { +// return owner; +// } +// } +// +// /** +// * Call the mouse event handler of the pop-up box +// * @param src - the pop-up box where the mouse event occured +// * @param eventId - the event ID, one of MouseEvent.MOUSE_* constants +// * @param where - the mouse pointer location +// * @param event - native event +// */ +// private void mouseEvent(PopupBox src, int eventId, Point where, +// NativeEvent event) { +// Point pos = src.getScreenLocation(); +// pos.setLocation(where.x - pos.x, where.y - pos.y); +// +// src.onMouseEvent(eventId, pos, event.getMouseButton(), event +// .getTime(), event.getInputModifiers(), event +// .getWheelRotation()); +// } +// +// /** +// * Handle the native event targeted by a pop-up box. This could be +// * paint event, mouse or keyboard event. +// * @param event - the native event +// * @return - false if the event was handled and doesn't +// * need the further processing; true when the further +// * processing is needed +// */ +// boolean onEvent(NativeEvent event) { +// PopupBox src = toolkit.getPopupBoxById(event.getWindowId()); +// int id = event.getEventId(); +// +// if ((id == PaintEvent.PAINT)) { +// if (src != null) { +// src.paint(event.getClipRects()); +// return true; +// } +// Component c = toolkit.getComponentById(event.getWindowId()); +// if ((c != null) && (c instanceof Frame)) { +// ((Frame) c).paintMenuBar(event.getClipRects()); +// } +// return false; +// } +// +// if ((id >= MouseEvent.MOUSE_FIRST) && (id <= MouseEvent.MOUSE_LAST)) { +// Point where = event.getScreenPos(); +// +// if (src != underCursor) { +// if (underCursor != null) { +// mouseEvent(underCursor, MouseEvent.MOUSE_EXITED, where, +// event); +// } +// underCursor = src; +// if (underCursor != null) { +// mouseEvent(underCursor, MouseEvent.MOUSE_ENTERED, +// where, event); +// underCursor.setDefaultCursor(); +// } +// } +// if (id == MouseEvent.MOUSE_EXITED) { +// underCursor = null; +// } +// +// if ((activePopup == null) && (src == null || !src.isMenuBar())) { +// return false; +// } +// +// if (id == MouseEvent.MOUSE_PRESSED) { +// src = grab.mousePressed(src, where); +// } else if (id == MouseEvent.MOUSE_RELEASED) { +// src = grab.mouseReleased(src, where); +// } else if (src == null) { +// src = grab.getOwner(); +// } +// +// PopupBox wasActive = activePopup; +// +// if (src != null) { +// mouseEvent(src, id, where, event); +// return src.isMenu() || src.contains(where); +// } +// +// if (wasActive != null && activePopup == null) { +// return wasActive.isMenu(); +// } +// +// if ((id == MouseEvent.MOUSE_PRESSED) +// || (id == MouseEvent.MOUSE_RELEASED)) { +// boolean isMenu = activePopup.isMenu(); +// deactivateAll(); +// return !isMenu; +// } +// return true; +// } +// +// if (activePopup == null) { +// return false; +// } +// +// if ((id >= KeyEvent.KEY_FIRST) && (id <= KeyEvent.KEY_LAST)) { +// boolean isMenu = activePopup.isMenu(); +// activePopup.dispatchKeyEvent(id, event.getVKey(), event +// .getTime(), event.getInputModifiers()); +// +// return isMenu; +// } +// +// return false; +// } +// +// /** +// * Remember the pop-up as active and grab the mouse on it +// * @param popup - the pop-up box to activate +// */ +// void activate(final PopupBox popup) { +// if (activePopup == null) { +// +// activePopup = popup; +// mouseGrabManager.startGrab(popup.getOwner(), new Runnable() { +// public void run() { +// deactivate(popup); +// } +// }); +// } +// } +// +// /** +// * Deactivate the currently active pop-up box +// */ +// void deactivateAll() { +// deactivate(activePopup); +// } +// +// /** +// * Deactivate the pop-up box, end the mouse grab +// */ +// void deactivate(PopupBox popup) { +// grab.reset(); +// +// if (activePopup != null && activePopup == popup) { +// activePopup = null; +// mouseGrabManager.endGrab(); +// popup.hide(); +// underCursor = null; +// } +// } +// +// /** +// * Check that the pop-up box is currently active +// * @param popup - the pop-up box to check +// * @return - true if active +// */ +// boolean isActive(PopupBox popup) { +// return (popup == activePopup) && (popup != null); +// } +// } + +} \ No newline at end of file diff --git a/awt/java/awt/DisplayMode.java b/awt/java/awt/DisplayMode.java new file mode 100644 index 000000000..802101019 --- /dev/null +++ b/awt/java/awt/DisplayMode.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +/** + * The DisplayMode class contains the bit depth, height, width and refresh rate + * of a GraphicsDevice. + * + * @since Android 1.0 + */ +public final class DisplayMode { + + /** + * The width. + */ + private final int width; + + /** + * The height. + */ + private final int height; + + /** + * The bit depth. + */ + private final int bitDepth; + + /** + * The refresh rate. + */ + private final int refreshRate; + + /** + * The Constant Value BIT_DEPTH_MULTI indicates the bit depth + */ + + public static final int BIT_DEPTH_MULTI = -1; + + /** + * The Constant REFRESH_RATE_UNKNOWN indicates the refresh rate. + */ + public static final int REFRESH_RATE_UNKNOWN = 0; + + /** + * Creates a new DisplayMode object with the specified parameters. + * + * @param width + * the width of the display. + * @param height + * the height of the display. + * @param bitDepth + * the bit depth of the display. + * @param refreshRate + * the refresh rate of the display. + */ + + public DisplayMode(int width, int height, int bitDepth, int refreshRate) { + this.width = width; + this.height = height; + this.bitDepth = bitDepth; + this.refreshRate = refreshRate; + } + + /** + * Compares if this DisplayMode is equal to the specified object or not. + * + * @param dm + * the Object to be compared. + * @return true, if the specified object is a DisplayMode with the same data + * values as this DisplayMode, false otherwise. + */ + + @Override + public boolean equals(Object dm) { + if (dm instanceof DisplayMode) { + return equals((DisplayMode)dm); + } + return false; + } + + /** + * Compares if this DisplayMode is equal to the specified DisplayMode object + * or not. + * + * @param dm + * the DisplayMode to be compared. + * @return true, if all of the data values of this DisplayMode are equal to + * the values of the specified DisplayMode object, false otherwise. + */ + public boolean equals(DisplayMode dm) { + if (dm == null) { + return false; + } + if (dm.bitDepth != bitDepth) { + return false; + } + if (dm.refreshRate != refreshRate) { + return false; + } + if (dm.width != width) { + return false; + } + if (dm.height != height) { + return false; + } + return true; + } + + /** + * Gets the bit depth of the DisplayMode, returns BIT_DEPTH_MULTI value if + * multiple bit depths are supported in this display mode. + * + * @return the bit depth of the DisplayMode. + */ + public int getBitDepth() { + return bitDepth; + } + + /** + * Gets the height of the DisplayMode. + * + * @return the height of the DisplayMode. + */ + public int getHeight() { + return height; + } + + /** + * Gets the refresh rate of the DisplayMode, returns REFRESH_RATE_UNKNOWN + * value if the information is not available. + * + * @return the refresh rate of the DisplayMode. + */ + public int getRefreshRate() { + return refreshRate; + } + + /** + * Gets the width of the DisplayMode. + * + * @return the width of the DisplayMode. + */ + public int getWidth() { + return width; + } +} diff --git a/awt/java/awt/Event.java b/awt/java/awt/Event.java new file mode 100644 index 000000000..226a61fff --- /dev/null +++ b/awt/java/awt/Event.java @@ -0,0 +1,596 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ + +package java.awt; + +import java.io.Serializable; + +/** + * The Event class is obsolete and has been replaced by AWTEvent class. + * + * @since Android 1.0 + */ +public class Event implements Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 5488922509400504703L; + + /** + * The Constant SHIFT_MASK indicates that the Shift key is down when the + * event occurred. + */ + public static final int SHIFT_MASK = 1; + + /** + * The Constant CTRL_MASK indicates that the Control key is down when the + * event occurred. + */ + public static final int CTRL_MASK = 2; + + /** + * The Constant META_MASK indicates that the Meta key is down when t he + * event occurred (or the right mouse button). + */ + public static final int META_MASK = 4; + + /** + * The Constant ALT_MASK indicates that the Alt key is down when the event + * occurred (or the middle mouse button). + */ + public static final int ALT_MASK = 8; + + /** + * The Constant HOME indicates Home key. + */ + public static final int HOME = 1000; + + /** + * The Constant END indicates End key. + */ + public static final int END = 1001; + + /** + * The Constant PGUP indicates Page Up key. + */ + public static final int PGUP = 1002; + + /** + * The Constant PGDN indicates Page Down key. + */ + public static final int PGDN = 1003; + + /** + * The Constant UP indicates Up key. + */ + public static final int UP = 1004; + + /** + * The Constant DOWN indicates Down key. + */ + public static final int DOWN = 1005; + + /** + * The Constant LEFT indicates Left key. + */ + public static final int LEFT = 1006; + + /** + * The Constant RIGHT indicates Right key. + */ + public static final int RIGHT = 1007; + + /** + * The Constant F1 indicates F1 key. + */ + public static final int F1 = 1008; + + /** + * The Constant F2 indicates F2 key. + */ + public static final int F2 = 1009; + + /** + * The Constant F3 indicates F3 key. + */ + public static final int F3 = 1010; + + /** + * The Constant F4 indicates F4 key. + */ + public static final int F4 = 1011; + + /** + * The Constant F5 indicates F5 key. + */ + public static final int F5 = 1012; + + /** + * The Constant F6 indicates F6 key. + */ + public static final int F6 = 1013; + + /** + * The Constant F7 indicates F7 key. + */ + public static final int F7 = 1014; + + /** + * The Constant F8 indicates F8 key. + */ + public static final int F8 = 1015; + + /** + * The Constant F9 indicates F9 key. + */ + public static final int F9 = 1016; + + /** + * The Constant F10 indicates F10 key. + */ + public static final int F10 = 1017; + + /** + * The Constant F11 indicates F11 key. + */ + public static final int F11 = 1018; + + /** + * The Constant F12 indicates F12 key. + */ + public static final int F12 = 1019; + + /** + * The Constant PRINT_SCREEN indicates Print Screen key. + */ + public static final int PRINT_SCREEN = 1020; + + /** + * The Constant SCROLL_LOCK indicates Scroll Lock key. + */ + public static final int SCROLL_LOCK = 1021; + + /** + * The Constant CAPS_LOCK indicates Caps Lock key. + */ + public static final int CAPS_LOCK = 1022; + + /** + * The Constant NUM_LOCK indicates Num Lock key. + */ + public static final int NUM_LOCK = 1023; + + /** + * The Constant PAUSE indicates Pause key. + */ + public static final int PAUSE = 1024; + + /** + * The Constant INSERT indicates Insert key. + */ + public static final int INSERT = 1025; + + /** + * The Constant ENTER indicates Enter key. + */ + public static final int ENTER = 10; + + /** + * The Constant BACK_SPACE indicates Back Space key. + */ + public static final int BACK_SPACE = 8; + + /** + * The Constant TAB indicates TAb key. + */ + public static final int TAB = 9; + + /** + * The Constant ESCAPE indicates Escape key. + */ + public static final int ESCAPE = 27; + + /** + * The Constant DELETE indicates Delete key. + */ + public static final int DELETE = 127; + + /** + * The Constant WINDOW_DESTROY indicates an event when the user has asked + * the window manager to kill the window. + */ + public static final int WINDOW_DESTROY = 201; + + /** + * The Constant WINDOW_EXPOSE indicates an event when the user has asked the + * window manager to expose the window. + */ + public static final int WINDOW_EXPOSE = 202; + + /** + * The Constant WINDOW_ICONIFY indicates an event when the user has asked + * the window manager to iconify the window. + */ + public static final int WINDOW_ICONIFY = 203; + + /** + * The Constant WINDOW_DEICONIFY indicates an event when the user has asked + * the window manager to deiconify the window. + */ + public static final int WINDOW_DEICONIFY = 204; + + /** + * The Constant WINDOW_MOVED indicates an event when the user has asked the + * window manager to move the window. + */ + public static final int WINDOW_MOVED = 205; + + /** + * The Constant KEY_PRESS indicates an event when the user presses a normal + * key. + */ + public static final int KEY_PRESS = 401; + + /** + * The Constant KEY_RELEASE indicates an event when the user releases a + * normal key. + */ + public static final int KEY_RELEASE = 402; + + /** + * The Constant KEY_ACTION indicates an event when the user pressed a + * non-ASCII action key. + */ + public static final int KEY_ACTION = 403; + + /** + * The Constant KEY_ACTION_RELEASE indicates an event when the user released + * a non-ASCII action key. + */ + public static final int KEY_ACTION_RELEASE = 404; + + /** + * The Constant MOUSE_DOWN indicates an event when the user has pressed the + * mouse button. + */ + public static final int MOUSE_DOWN = 501; + + /** + * The Constant MOUSE_UP indicates an event when the user has released the + * mouse button. + */ + public static final int MOUSE_UP = 502; + + /** + * The Constant MOUSE_MOVE indicates an event when the user has moved the + * mouse with no button pressed. + */ + public static final int MOUSE_MOVE = 503; + + /** + * The Constant MOUSE_ENTER indicates an event when the mouse has entered a + * component. + */ + public static final int MOUSE_ENTER = 504; + + /** + * The Constant MOUSE_EXIT indicates an event when the mouse has exited a + * component. + */ + public static final int MOUSE_EXIT = 505; + + /** + * The Constant MOUSE_DRAG indicates an event when the user has moved a + * mouse with the pressed button. + */ + public static final int MOUSE_DRAG = 506; + + /** + * The Constant SCROLL_LINE_UP indicates an event when the user has + * activated line-up area of scrollbar. + */ + public static final int SCROLL_LINE_UP = 601; + + /** + * The Constant SCROLL_LINE_DOWN indicates an event when the user has + * activated line-down area of scrollbar. + */ + public static final int SCROLL_LINE_DOWN = 602; + + /** + * The Constant SCROLL_PAGE_UP indicates an event when the user has + * activated page up area of scrollbar. + */ + public static final int SCROLL_PAGE_UP = 603; + + /** + * The Constant SCROLL_PAGE_DOWN indicates an event when the user has + * activated page down area of scrollbar. + */ + public static final int SCROLL_PAGE_DOWN = 604; + + /** + * The Constant SCROLL_ABSOLUTE indicates an event when the user has moved + * the bubble in a scroll bar. + */ + public static final int SCROLL_ABSOLUTE = 605; + + /** + * The Constant SCROLL_BEGIN indicates a scroll begin event. + */ + public static final int SCROLL_BEGIN = 606; + + /** + * The Constant SCROLL_END indicates a scroll end event. + */ + public static final int SCROLL_END = 607; + + /** + * The Constant LIST_SELECT indicates that an item in a list has been + * selected. + */ + public static final int LIST_SELECT = 701; + + /** + * The Constant LIST_DESELECT indicates that an item in a list has been + * unselected. + */ + public static final int LIST_DESELECT = 702; + + /** + * The Constant ACTION_EVENT indicates that the user wants some action to + * occur. + */ + public static final int ACTION_EVENT = 1001; + + /** + * The Constant LOAD_FILE indicates a file loading event. + */ + public static final int LOAD_FILE = 1002; + + /** + * The Constant SAVE_FILE indicates a file saving event. + */ + public static final int SAVE_FILE = 1003; + + /** + * The Constant GOT_FOCUS indicates that a component got the focus. + */ + public static final int GOT_FOCUS = 1004; + + /** + * The Constant LOST_FOCUS indicates that the component lost the focus. + */ + public static final int LOST_FOCUS = 1005; + + /** + * The target is the component with which the event is associated. + */ + public Object target; + + /** + * The when is timestamp when event has occured. + */ + public long when; + + /** + * The id indicates the type of the event. + */ + public int id; + + /** + * The x coordinate of event. + */ + public int x; + + /** + * The y coordinate of event. + */ + public int y; + + /** + * The key code of key event. + */ + public int key; + + /** + * The state of the modifier keys (given by a bitmask). + */ + public int modifiers; + + /** + * The click count indicates the number of consecutive clicks. + */ + public int clickCount; + + /** + * The argument of the event. + */ + public Object arg; + + /** + * The next event. + */ + public Event evt; + + /** + * Instantiates a new event with the specified target component, event type, + * and argument. + * + * @param target + * the target component. + * @param id + * the event type. + * @param arg + * the argument. + */ + public Event(Object target, int id, Object arg) { + this(target, 0l, id, 0, 0, 0, 0, arg); + } + + /** + * Instantiates a new event with the specified target component, time stamp, + * event type, x and y coordinates, keyboard key, state of the modifier + * keys, and an argument set to null. + * + * @param target + * the target component. + * @param when + * the time stamp. + * @param id + * the event type. + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @param key + * the key. + * @param modifiers + * the modifier keys state. + */ + public Event(Object target, long when, int id, int x, int y, int key, int modifiers) { + this(target, when, id, x, y, key, modifiers, null); + } + + /** + * Instantiates a new event with the specified target component, time stamp, + * event type, x and y coordinates, keyboard key, state of the modifier + * keys, and an argument. + * + * @param target + * the target component. + * @param when + * the time stamp. + * @param id + * the event type. + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @param key + * the key. + * @param modifiers + * the modifier keys state. + * @param arg + * the specified argument. + */ + public Event(Object target, long when, int id, int x, int y, int key, int modifiers, Object arg) { + this.target = target; + this.when = when; + this.id = id; + this.x = x; + this.y = y; + this.key = key; + this.modifiers = modifiers; + this.arg = arg; + } + + /** + * Returns a string representation of this Event. + * + * @return a string representation of this Event. + */ + @Override + public String toString() { + /* + * The format is based on 1.5 release behavior which can be revealed by + * the following code: Event e = new Event(new Button(), 0l, + * Event.KEY_PRESS, 0, 0, Event.TAB, Event.SHIFT_MASK, "arg"); + * System.out.println(e); + */ + + return getClass().getName() + "[" + paramString() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Returns a string representing the state of this Event. + * + * @return a string representing the state of this Event. + */ + protected String paramString() { + return "id=" + id + ",x=" + x + ",y=" + y + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + (key != 0 ? ",key=" + key + getModifiersString() : "") + //$NON-NLS-1$ //$NON-NLS-2$ + ",target=" + target + //$NON-NLS-1$ + (arg != null ? ",arg=" + arg : ""); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Gets a string representation of the modifiers. + * + * @return a string representation of the modifiers. + */ + private String getModifiersString() { + String strMod = ""; //$NON-NLS-1$ + if (shiftDown()) { + strMod += ",shift"; //$NON-NLS-1$ + } + if (controlDown()) { + strMod += ",control"; //$NON-NLS-1$ + } + if (metaDown()) { + strMod += ",meta"; //$NON-NLS-1$ + } + return strMod; + } + + /** + * Translates x and y coordinates of his event to the x+dx and x+dy + * coordinates. + * + * @param dx + * the distance by which the event's x coordinate is increased. + * @param dy + * the distance by which the event's y coordinate is increased. + */ + public void translate(int dx, int dy) { + x += dx; + y += dy; + } + + /** + * Checks if Control key is down or not. + * + * @return true, if Control key is down; false otherwise. + */ + public boolean controlDown() { + return (modifiers & CTRL_MASK) != 0; + } + + /** + * Checks if Meta key is down or not. + * + * @return true, if Meta key is down; false otherwise. + */ + public boolean metaDown() { + return (modifiers & META_MASK) != 0; + } + + /** + * Checks if Shift key is down or not. + * + * @return true, if Shift key is down; false otherwise. + */ + public boolean shiftDown() { + return (modifiers & SHIFT_MASK) != 0; + } + +} diff --git a/awt/java/awt/EventDispatchThread.java b/awt/java/awt/EventDispatchThread.java new file mode 100644 index 000000000..442c8a278 --- /dev/null +++ b/awt/java/awt/EventDispatchThread.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov, Pavel Dolgov + * @version $Revision$ + */ +package java.awt; + +import org.apache.harmony.awt.wtk.NativeEvent; +import org.apache.harmony.awt.wtk.NativeEventQueue; + +class EventDispatchThread extends Thread { + + private static final class MarkerEvent extends AWTEvent { + MarkerEvent(Object source, int id) { + super(source, id); + } + } + + final Dispatcher dispatcher; + final Toolkit toolkit; + private NativeEventQueue nativeQueue; + + protected volatile boolean shutdownPending = false; + + /** + * Initialise and run the main event loop + */ + @Override + public void run() { + nativeQueue = toolkit.getNativeEventQueue(); + + try { + runModalLoop(null); + } finally { + toolkit.shutdownWatchdog.forceShutdown(); + } + } + + void runModalLoop(ModalContext context) { + long lastPaintTime = System.currentTimeMillis(); + while (!shutdownPending && (context == null || context.isModalLoopRunning())) { + try { + EventQueue eventQueue = toolkit.getSystemEventQueueImpl(); + + NativeEvent ne = nativeQueue.getNextEvent(); + if (ne != null) { + dispatcher.onEvent(ne); + MarkerEvent marker = new MarkerEvent(this, 0); + eventQueue.postEvent(marker); + for (AWTEvent ae = eventQueue.getNextEventNoWait(); + (ae != null) && (ae != marker); + ae = eventQueue.getNextEventNoWait()) { + eventQueue.dispatchEvent(ae); + } + } else { + toolkit.shutdownWatchdog.setNativeQueueEmpty(true); + AWTEvent ae = eventQueue.getNextEventNoWait(); + if (ae != null) { + eventQueue.dispatchEvent(ae); + long curTime = System.currentTimeMillis(); + if (curTime - lastPaintTime > 10) { + toolkit.onQueueEmpty(); + lastPaintTime = System.currentTimeMillis(); + } + } else { + toolkit.shutdownWatchdog.setAwtQueueEmpty(true); + toolkit.onQueueEmpty(); + lastPaintTime = System.currentTimeMillis(); + waitForAnyEvent(); + } + } + } catch (Throwable t) { + // TODO: Exception handler should be implemented + // t.printStackTrace(); + } + } + } + + private void waitForAnyEvent() { + EventQueue eventQueue = toolkit.getSystemEventQueueImpl(); + if (!eventQueue.isEmpty() || !nativeQueue.isEmpty()) { + return; + } + Object eventMonitor = nativeQueue.getEventMonitor(); + synchronized(eventMonitor) { + try { + eventMonitor.wait(); + } catch (InterruptedException e) {} + } + } + + void shutdown() { + shutdownPending = true; + } + + EventDispatchThread(Toolkit toolkit, Dispatcher dispatcher ) { + this.toolkit = toolkit; + this.dispatcher = dispatcher; + setName("AWT-EventDispatchThread"); //$NON-NLS-1$ + setDaemon(true); + } + +} diff --git a/awt/java/awt/EventQueue.java b/awt/java/awt/EventQueue.java new file mode 100644 index 000000000..126a59306 --- /dev/null +++ b/awt/java/awt/EventQueue.java @@ -0,0 +1,320 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov, Pavel Dolgov + * @version $Revision$ + */ + +package java.awt; + +import java.awt.event.InvocationEvent; +import java.lang.reflect.InvocationTargetException; +import java.util.EmptyStackException; + +/** + * The EventQueue class manages events. It is a platform-independent class that + * queues events both from the underlying peer classes and from trusted + * application classes. + * + * @since Android 1.0 + */ +public class EventQueue { + + /** + * The core ref. + */ + private final EventQueueCoreAtomicReference coreRef = new EventQueueCoreAtomicReference(); + + /** + * The Class EventQueueCoreAtomicReference. + */ + private static final class EventQueueCoreAtomicReference { + + /** + * The core. + */ + private EventQueueCore core; + + /* synchronized */ + /** + * Gets the. + * + * @return the event queue core. + */ + EventQueueCore get() { + return core; + } + + /* synchronized */ + /** + * Sets the. + * + * @param newCore + * the new core. + */ + void set(EventQueueCore newCore) { + core = newCore; + } + } + + /** + * Returns true if the calling thread is the current AWT EventQueue's + * dispatch thread. + * + * @return true, if the calling thread is the current AWT EventQueue's + * dispatch thread; false otherwise. + */ + public static boolean isDispatchThread() { + return Thread.currentThread() instanceof EventDispatchThread; + } + + /** + * Posts an InvocationEvent which executes the run() method on a Runnable + * when dispatched by the AWT event dispatcher thread. + * + * @param runnable + * the Runnable whose run method should be executed synchronously + * on the EventQueue. + */ + public static void invokeLater(Runnable runnable) { + Toolkit toolkit = Toolkit.getDefaultToolkit(); + InvocationEvent event = new InvocationEvent(toolkit, runnable); + toolkit.getSystemEventQueueImpl().postEvent(event); + } + + /** + * Posts an InvocationEvent which executes the run() method on a Runnable + * when dispatched by the AWT event dispatcher thread and the notifyAll + * method is called on it immediately after run returns. + * + * @param runnable + * the Runnable whose run method should be executed synchronously + * on the EventQueue. + * @throws InterruptedException + * if another thread has interrupted this thread. + * @throws InvocationTargetException + * if an error occurred while running the runnable. + */ + public static void invokeAndWait(Runnable runnable) throws InterruptedException, + InvocationTargetException { + + if (isDispatchThread()) { + throw new Error(); + } + + final Toolkit toolkit = Toolkit.getDefaultToolkit(); + final Object notifier = new Object(); // $NON-LOCK-1$ + InvocationEvent event = new InvocationEvent(toolkit, runnable, notifier, true); + + synchronized (notifier) { + toolkit.getSystemEventQueueImpl().postEvent(event); + notifier.wait(); + } + + Exception exception = event.getException(); + + if (exception != null) { + throw new InvocationTargetException(exception); + } + } + + /** + * Gets the system event queue. + * + * @return the system event queue. + */ + private static EventQueue getSystemEventQueue() { + Thread th = Thread.currentThread(); + if (th instanceof EventDispatchThread) { + return ((EventDispatchThread)th).toolkit.getSystemEventQueueImpl(); + } + return null; + } + + /** + * Gets the most recent event's timestamp. This event was dispatched from + * the EventQueue associated with the calling thread. + * + * @return the timestamp of the last Event to be dispatched, or + * System.currentTimeMillis() if this method is invoked from a + * thread other than an event-dispatching thread. + */ + public static long getMostRecentEventTime() { + EventQueue eq = getSystemEventQueue(); + return (eq != null) ? eq.getMostRecentEventTimeImpl() : System.currentTimeMillis(); + } + + /** + * Gets the most recent event time impl. + * + * @return the most recent event time impl. + */ + private long getMostRecentEventTimeImpl() { + return getCore().getMostRecentEventTime(); + } + + /** + * Returns the the currently dispatched event by the EventQueue associated + * with the calling thread. + * + * @return the currently dispatched event or null if this method is invoked + * from a thread other than an event-dispatching thread. + */ + public static AWTEvent getCurrentEvent() { + EventQueue eq = getSystemEventQueue(); + return (eq != null) ? eq.getCurrentEventImpl() : null; + } + + /** + * Gets the current event impl. + * + * @return the current event impl. + */ + private AWTEvent getCurrentEventImpl() { + return getCore().getCurrentEvent(); + } + + /** + * Instantiates a new event queue. + */ + public EventQueue() { + setCore(new EventQueueCore(this)); + } + + /** + * Instantiates a new event queue. + * + * @param t + * the t. + */ + EventQueue(Toolkit t) { + setCore(new EventQueueCore(this, t)); + } + + /** + * Posts a event to the EventQueue. + * + * @param event + * AWTEvent. + */ + public void postEvent(AWTEvent event) { + event.isPosted = true; + getCore().postEvent(event); + } + + /** + * Returns an event from the EventQueue and removes it from this queue. + * + * @return the next AWTEvent. + * @throws InterruptedException + * is thrown if another thread interrupts this thread. + */ + public AWTEvent getNextEvent() throws InterruptedException { + return getCore().getNextEvent(); + } + + /** + * Gets the next event no wait. + * + * @return the next event no wait. + */ + AWTEvent getNextEventNoWait() { + return getCore().getNextEventNoWait(); + } + + /** + * Returns the first event of the EventQueue (without removing it from the + * queue). + * + * @return the the first AWT event of the EventQueue. + */ + public AWTEvent peekEvent() { + return getCore().peekEvent(); + } + + /** + * Returns the first event of the EventQueue with the specified ID (without + * removing it from the queue). + * + * @param id + * the type ID of event. + * @return the first event of the EventQueue with the specified ID. + */ + public AWTEvent peekEvent(int id) { + return getCore().peekEvent(id); + } + + /** + * Replaces the existing EventQueue with the specified EventQueue. Any + * pending events are transferred to the new EventQueue. + * + * @param newEventQueue + * the new event queue. + */ + public void push(EventQueue newEventQueue) { + getCore().push(newEventQueue); + } + + /** + * Stops dispatching events using this EventQueue. Any pending events are + * transferred to the previous EventQueue. + * + * @throws EmptyStackException + * is thrown if no previous push was made on this EventQueue. + */ + protected void pop() throws EmptyStackException { + getCore().pop(); + } + + /** + * Dispatches the specified event. + * + * @param event + * the AWTEvent. + */ + protected void dispatchEvent(AWTEvent event) { + getCore().dispatchEventImpl(event); + } + + /** + * Checks if the queue is empty. + * + * @return true, if is empty. + */ + boolean isEmpty() { + return getCore().isEmpty(); + } + + /** + * Gets the core. + * + * @return the core. + */ + EventQueueCore getCore() { + return coreRef.get(); + } + + /** + * Sets the core. + * + * @param newCore + * the new core. + */ + void setCore(EventQueueCore newCore) { + coreRef.set((newCore != null) ? newCore : new EventQueueCore(this)); + } +} diff --git a/awt/java/awt/EventQueueCore.java b/awt/java/awt/EventQueueCore.java new file mode 100644 index 000000000..ffc7c46f4 --- /dev/null +++ b/awt/java/awt/EventQueueCore.java @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.InputMethodEvent; +import java.awt.event.InvocationEvent; +import java.awt.event.MouseEvent; +import java.util.LinkedList; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The events storage for EventQueue + */ +final class EventQueueCore { + + private final LinkedList queueStack = new LinkedList(); + private final LinkedList events = new LinkedList(); + + private Toolkit toolkit; + private EventQueue activeQueue; + private Thread dispatchThread; + + AWTEvent currentEvent; + long mostRecentEventTime = System.currentTimeMillis(); + + EventQueueCore(EventQueue eq) { + synchronized (this) { + queueStack.addLast(eq); + activeQueue = eq; + } + } + + EventQueueCore(EventQueue eq, Toolkit t) { + synchronized (this) { + queueStack.addLast(eq); + activeQueue = eq; + setToolkit(t); + } + } + + synchronized long getMostRecentEventTime() { + return mostRecentEventTime; + } + + synchronized AWTEvent getCurrentEvent() { + return currentEvent; + } + + synchronized boolean isSystemEventQueue() { + return toolkit != null; + } + + private void setToolkit(Toolkit t) { + toolkit = t; + if (toolkit != null) { + toolkit.setSystemEventQueueCore(this); + dispatchThread = toolkit.dispatchThread; + } + } + + synchronized void postEvent(AWTEvent event) { + //???AWT + /* + events.addLast(event); + if ((toolkit == null) && (dispatchThread == null)) { + dispatchThread = new EventQueueThread(this); + dispatchThread.start(); + } + // TODO: add event coalescing + if (toolkit != null) { + toolkit.shutdownWatchdog.setAwtQueueEmpty(false); + if (!GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance()) { + notifyEventMonitor(toolkit); + } + } + notifyAll(); + */ + } + + void notifyEventMonitor(Toolkit t) { + Object em = t.getNativeEventQueue().getEventMonitor(); + synchronized (em) { + em.notifyAll(); + } + } + + synchronized AWTEvent getNextEvent() throws InterruptedException { + while (events.isEmpty()) { + wait(); + } + AWTEvent event = events.removeFirst(); + // TODO: add event coalescing + return event; + } + + synchronized AWTEvent peekEvent() { + return events.isEmpty() ? null : events.getFirst(); + } + + synchronized AWTEvent peekEvent(int id) { + for (AWTEvent event : events) { + if (event.getID() == id) { + return event; + } + } + return null; + } + + synchronized void dispatchEvent(AWTEvent event) { + updateCurrentEventAndTime(event); + try { + activeQueue.dispatchEvent(event); + } finally { + currentEvent = null; + } + } + + void dispatchEventImpl(AWTEvent event) { + if (event instanceof ActiveEvent) { + updateCurrentEventAndTime(event); + try { + ((ActiveEvent) event).dispatch(); + } finally { + currentEvent = null; + } + return; + } + + Object src = event.getSource(); + + if (src instanceof Component) { + if (preprocessComponentEvent(event)) { + ((Component) src).dispatchEvent(event); + } + } else { + if (toolkit != null) { + toolkit.dispatchAWTEvent(event); + } + if (src instanceof MenuComponent) { + ((MenuComponent) src).dispatchEvent(event); + } + } + } + + private final boolean preprocessComponentEvent(AWTEvent event) { + if (event instanceof MouseEvent) { + return preprocessMouseEvent((MouseEvent)event); + } + return true; + } + + private final boolean preprocessMouseEvent(MouseEvent event) { + //???AWT + /* + if (toolkit != null && toolkit.mouseEventPreprocessor != null) { + toolkit.lockAWT(); + try { + return toolkit.mouseEventPreprocessor.preprocess(event); + } finally { + toolkit.unlockAWT(); + } + } + return true; + */ + return true; + } + + private void updateCurrentEventAndTime(AWTEvent event) { + currentEvent = event; + long when = 0; + if (event instanceof ActionEvent) { + when = ((ActionEvent) event).getWhen(); + } else if (event instanceof InputEvent) { + when = ((InputEvent) event).getWhen(); + } else if (event instanceof InputMethodEvent) { + when = ((InputMethodEvent) event).getWhen(); + } else if (event instanceof InvocationEvent) { + when = ((InvocationEvent) event).getWhen(); + } + if (when != 0) { + mostRecentEventTime = when; + } + } + + synchronized void push(EventQueue newEventQueue) { + // TODO: handle incorrect situations + if (queueStack.isEmpty()) { + // awt.6B=Queue stack is empty + throw new IllegalStateException(Messages.getString("awt.6B")); //$NON-NLS-1$ + } + + queueStack.addLast(newEventQueue); + activeQueue = newEventQueue; + activeQueue.setCore(this); + } + + synchronized void pop() { + EventQueue removed = queueStack.removeLast(); + if (removed != activeQueue) { + // awt.6C=Event queue stack is broken + throw new IllegalStateException(Messages.getString("awt.6C")); //$NON-NLS-1$ + } + activeQueue = queueStack.getLast(); + removed.setCore(null); + } + + synchronized AWTEvent getNextEventNoWait() { + try { + return events.isEmpty() ? null : activeQueue.getNextEvent(); + } catch (InterruptedException e) { + return null; + } + } + + synchronized boolean isEmpty() { + return (currentEvent == null) && events.isEmpty(); + } + + synchronized boolean isEmpty(long timeout) { + if (!isEmpty()) { + return false; + } + try { + wait(timeout); + } catch (InterruptedException e) {} + return isEmpty(); + } + + synchronized EventQueue getActiveEventQueue() { + return activeQueue; + } +} diff --git a/awt/java/awt/Font.java b/awt/java/awt/Font.java new file mode 100644 index 000000000..4ed93438d --- /dev/null +++ b/awt/java/awt/Font.java @@ -0,0 +1,1541 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.awt; + +import com.android.internal.awt.AndroidGraphics2D; + +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.LineMetrics; +import java.awt.font.TextAttribute; +import java.awt.font.TransformAttribute; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.io.File; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.text.CharacterIterator; +import java.text.AttributedCharacterIterator.Attribute; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import org.apache.harmony.awt.gl.font.AndroidGlyphVector; +import org.apache.harmony.awt.gl.font.CommonGlyphVector; +import org.apache.harmony.awt.gl.font.FontPeerImpl; +import org.apache.harmony.awt.gl.font.FontMetricsImpl; +import org.apache.harmony.awt.gl.font.LineMetricsImpl; +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.luni.util.NotImplementedException; +import org.apache.harmony.misc.HashCode; + +/** + * The Font class represents fonts for rendering text. This class allow to map + * characters to glyphs. + *

+ * A glyph is a shape used to render a character or a sequence of characters. + * For example one character of Latin writing system represented by one glyph, + * but in complex writing system such as South and South-East Asian there is + * more complicated correspondence between characters and glyphs. + *

+ * The Font object is identified by two types of names. The logical font name is + * the name that is used to construct the font. The font name is the name of a + * particular font face (for example, Arial Bold). The family name is the font's + * family name that specifies the typographic design across several faces (for + * example, Arial). In all the Font is identified by three attributes: the + * family name, the style (such as bold or italic), and the size. + * + * @since Android 1.0 + */ +public class Font implements Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -4206021311591459213L; + + // Identity Transform attribute + /** + * The Constant IDENTITY_TRANSFORM. + */ + private static final TransformAttribute IDENTITY_TRANSFORM = new TransformAttribute( + new AffineTransform()); + + /** + * The Constant PLAIN indicates font's plain style. + */ + public static final int PLAIN = 0; + + /** + * The Constant BOLD indicates font's bold style. + */ + public static final int BOLD = 1; + + /** + * The Constant ITALIC indicates font's italic style. + */ + public static final int ITALIC = 2; + + /** + * The Constant ROMAN_BASELINE indicated roman baseline. + */ + public static final int ROMAN_BASELINE = 0; + + /** + * The Constant CENTER_BASELINE indicates center baseline. + */ + public static final int CENTER_BASELINE = 1; + + /** + * The Constant HANGING_BASELINE indicates hanging baseline. + */ + public static final int HANGING_BASELINE = 2; + + /** + * The Constant TRUETYPE_FONT indicates a font resource of type TRUETYPE. + */ + public static final int TRUETYPE_FONT = 0; + + /** + * The Constant TYPE1_FONT indicates a font resource of type TYPE1. + */ + public static final int TYPE1_FONT = 1; + + /** + * The Constant LAYOUT_LEFT_TO_RIGHT indicates that text is left to right. + */ + public static final int LAYOUT_LEFT_TO_RIGHT = 0; + + /** + * The Constant LAYOUT_RIGHT_TO_LEFT indicates that text is right to left. + */ + public static final int LAYOUT_RIGHT_TO_LEFT = 1; + + /** + * The Constant LAYOUT_NO_START_CONTEXT indicates that the text in the char + * array before the indicated start should not be examined. + */ + public static final int LAYOUT_NO_START_CONTEXT = 2; + + /** + * The Constant LAYOUT_NO_LIMIT_CONTEXT indicates that text in the char + * array after the indicated limit should not be examined. + */ + public static final int LAYOUT_NO_LIMIT_CONTEXT = 4; + + /** + * The Constant DEFAULT_FONT. + */ + static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 12); //$NON-NLS-1$ + + /** + * The name of the Font. + */ + protected String name; + + /** + * The style of the Font. + */ + protected int style; + + /** + * The size of the Font. + */ + protected int size; + + /** + * The point size of the Font. + */ + protected float pointSize; + + // Flag if the Font object transformed + /** + * The transformed. + */ + private boolean transformed; + + // Set of font attributes + /** + * The requested attributes. + */ + private Hashtable fRequestedAttributes; + + // font peer object corresponding to this Font + /** + * The font peer. + */ + private transient FontPeerImpl fontPeer; + + // number of glyphs in this Font + /** + * The num glyphs. + */ + private transient int numGlyphs = -1; + + // code for missing glyph for this Font + /** + * The missing glyph code. + */ + private transient int missingGlyphCode = -1; + + /** + * Writes object to ObjectOutputStream. + * + * @param out + * ObjectOutputStream. + * @throws IOException + * Signals that an I/O exception has occurred. + */ + private void writeObject(java.io.ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + } + + /** + * Reads object from ObjectInputStream object and set native platform + * dependent fields to default values. + * + * @param in + * ObjectInputStream object. + * @throws IOException + * Signals that an I/O exception has occurred. + * @throws ClassNotFoundException + * the class not found exception. + */ + private void readObject(java.io.ObjectInputStream in) throws IOException, + ClassNotFoundException { + in.defaultReadObject(); + + numGlyphs = -1; + missingGlyphCode = -1; + + } + + /** + * Instantiates a new Font with the specified attributes. The Font will be + * created with default attributes if the attribute's parameter is null. + * + * @param attributes + * the attributes to be assigned to the new Font, or null. + */ + public Font(Map attributes) { + Object currAttr; + + // Default values are taken from the documentation of the Font class. + // See Font constructor, decode and getFont sections. + + this.name = "default"; //$NON-NLS-1$ + this.size = 12; + this.pointSize = 12; + this.style = Font.PLAIN; + + if (attributes != null) { + + fRequestedAttributes = new Hashtable(attributes); + + currAttr = attributes.get(TextAttribute.SIZE); + if (currAttr != null) { + this.pointSize = ((Float)currAttr).floatValue(); + this.size = (int)Math.ceil(this.pointSize); + } + + currAttr = attributes.get(TextAttribute.POSTURE); + if (currAttr != null && currAttr.equals(TextAttribute.POSTURE_OBLIQUE)) { + this.style |= Font.ITALIC; + } + + currAttr = attributes.get(TextAttribute.WEIGHT); + if ((currAttr != null) + && (((Float)currAttr).floatValue() >= (TextAttribute.WEIGHT_BOLD).floatValue())) { + this.style |= Font.BOLD; + } + + currAttr = attributes.get(TextAttribute.FAMILY); + if (currAttr != null) { + this.name = (String)currAttr; + } + + currAttr = attributes.get(TextAttribute.TRANSFORM); + if (currAttr != null) { + if (currAttr instanceof TransformAttribute) { + this.transformed = !((TransformAttribute)currAttr).getTransform().isIdentity(); + } else if (currAttr instanceof AffineTransform) { + this.transformed = !((AffineTransform)currAttr).isIdentity(); + } + } + + } else { + fRequestedAttributes = new Hashtable(5); + fRequestedAttributes.put(TextAttribute.TRANSFORM, IDENTITY_TRANSFORM); + + this.transformed = false; + + fRequestedAttributes.put(TextAttribute.FAMILY, name); + + fRequestedAttributes.put(TextAttribute.SIZE, new Float(this.size)); + + if ((this.style & Font.BOLD) != 0) { + fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); + } else { + fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR); + } + if ((this.style & Font.ITALIC) != 0) { + fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); + } else { + fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR); + } + + } + } + + /** + * Instantiates a new Font with the specified name, style and size. + * + * @param name + * the name of font. + * @param style + * the style of font. + * @param size + * the size of font. + */ + public Font(String name, int style, int size) { + + this.name = (name != null) ? name : "Default"; //$NON-NLS-1$ + this.size = (size >= 0) ? size : 0; + this.style = (style & ~0x03) == 0 ? style : Font.PLAIN; + this.pointSize = this.size; + + fRequestedAttributes = new Hashtable(5); + + fRequestedAttributes.put(TextAttribute.TRANSFORM, IDENTITY_TRANSFORM); + + this.transformed = false; + + fRequestedAttributes.put(TextAttribute.FAMILY, this.name); + fRequestedAttributes.put(TextAttribute.SIZE, new Float(this.size)); + + if ((this.style & Font.BOLD) != 0) { + fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); + } else { + fRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR); + } + if ((this.style & Font.ITALIC) != 0) { + fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); + } else { + fRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_REGULAR); + } + } + + /** + * Returns true if this Font has a glyph for the specified character. + * + * @param c + * the character. + * @return true if this Font has a glyph for the specified character, false + * otherwise. + */ + public boolean canDisplay(char c) { + FontPeerImpl peer = (FontPeerImpl)this.getPeer(); + return peer.canDisplay(c); + } + + /** + * Returns true if the Font can display the characters of the the specified + * text from the specified start position to the specified limit position. + * + * @param text + * the text. + * @param start + * the start offset (in the character array). + * @param limit + * the limit offset (in the character array). + * @return the a character's position in the text that this Font can not + * display, or -1 if this Font can display all characters in this + * text. + */ + public int canDisplayUpTo(char[] text, int start, int limit) { + int st = start; + int result; + while ((st < limit) && canDisplay(text[st])) { + st++; + } + + if (st == limit) { + result = -1; + } else { + result = st; + } + + return result; + } + + /** + * Returns true if the Font can display the characters of the the specified + * CharacterIterator from the specified start position and the specified + * limit position. + * + * @param iter + * the CharacterIterator. + * @param start + * the start offset. + * @param limit + * the limit offset. + * @return the a character's position in the CharacterIterator that this + * Font can not display, or -1 if this Font can display all + * characters in this text. + */ + public int canDisplayUpTo(CharacterIterator iter, int start, int limit) { + int st = start; + char c = iter.setIndex(start); + int result; + + while ((st < limit) && (canDisplay(c))) { + st++; + c = iter.next(); + } + if (st == limit) { + result = -1; + } else { + result = st; + } + + return result; + } + + /** + * Returns true if this Font can display a specified String. + * + * @param str + * the String. + * @return the a character's position in the String that this Font can not + * display, or -1 if this Font can display all characters in this + * text. + */ + public int canDisplayUpTo(String str) { + char[] chars = str.toCharArray(); + return canDisplayUpTo(chars, 0, chars.length); + } + + /** + * Creates a GlyphVector of associating characters to glyphs based on the + * Unicode map of this Font. + * + * @param frc + * the FontRenderContext. + * @param chars + * the characters array. + * @return the GlyphVector of associating characters to glyphs based on the + * Unicode map of this Font. + */ + public GlyphVector createGlyphVector(FontRenderContext frc, char[] chars) { + return new AndroidGlyphVector(chars, frc, this, 0); + } + + /** + * Creates a GlyphVector of associating characters contained in the + * specified CharacterIterator to glyphs based on the Unicode map of this + * Font. + * + * @param frc + * the FontRenderContext. + * @param iter + * the CharacterIterator. + * @return the GlyphVector of associating characters contained in the + * specified CharacterIterator to glyphs based on the Unicode map of + * this Font. + */ + public GlyphVector createGlyphVector(FontRenderContext frc, CharacterIterator iter) { + throw new RuntimeException("Not implemented!"); //$NON-NLS-1$ + } + + /** + * Creates a GlyphVector of associating characters to glyphs based on the + * Unicode map of this Font. + * + * @param frc + * the FontRenderContext. + * @param glyphCodes + * the specified integer array of glyph codes. + * @return the GlyphVector of associating characters to glyphs based on the + * Unicode map of this Font. + * @throws NotImplementedException + * if this method is not implemented by a subclass. + */ + public GlyphVector createGlyphVector(FontRenderContext frc, int[] glyphCodes) + throws org.apache.harmony.luni.util.NotImplementedException { + throw new RuntimeException("Not implemented!"); //$NON-NLS-1$ + } + + /** + * Creates a GlyphVector of associating characters to glyphs based on the + * Unicode map of this Font. + * + * @param frc + * the FontRenderContext. + * @param str + * the specified String. + * @return the GlyphVector of associating characters to glyphs based on the + * Unicode map of this Font. + */ + public GlyphVector createGlyphVector(FontRenderContext frc, String str) { + return new AndroidGlyphVector(str.toCharArray(), frc, this, 0); + + } + + /** + * Returns the font style constant value corresponding to one of the font + * style names ("BOLD", "ITALIC", "BOLDITALIC"). This method returns + * Font.PLAIN if the argument is not one of the predefined style names. + * + * @param fontStyleName + * font style name. + * @return font style constant value corresponding to the font style name + * specified. + */ + private static int getFontStyle(String fontStyleName) { + int result = Font.PLAIN; + + if (fontStyleName.toUpperCase().equals("BOLDITALIC")) { //$NON-NLS-1$ + result = Font.BOLD | Font.ITALIC; + } else if (fontStyleName.toUpperCase().equals("BOLD")) { //$NON-NLS-1$ + result = Font.BOLD; + } else if (fontStyleName.toUpperCase().equals("ITALIC")) { //$NON-NLS-1$ + result = Font.ITALIC; + } + + return result; + } + + /** + * Decodes the specified string which described the Font. The string should + * have the following format: fontname-style-pointsize. The style can be + * PLAIN, BOLD, BOLDITALIC, or ITALIC. + * + * @param str + * the string which describes the font. + * @return the Font from the specified string. + */ + public static Font decode(String str) { + // XXX: Documentation doesn't describe all cases, e.g. fonts face names + // with + // symbols that are suggested as delimiters in the documentation. + // In this decode implementation only ***-***-*** format is used with + // '-' + // as the delimiter to avoid unexpected parse results of font face names + // with spaces. + + if (str == null) { + return DEFAULT_FONT; + } + + StringTokenizer strTokens; + String delim = "-"; //$NON-NLS-1$ + String substr; + + int fontSize = DEFAULT_FONT.size; + int fontStyle = DEFAULT_FONT.style; + String fontName = DEFAULT_FONT.name; + + strTokens = new StringTokenizer(str.trim(), delim); + + // Font Name + if (strTokens.hasMoreTokens()) { + fontName = strTokens.nextToken(); // first token is the font name + } + + // Font Style or Size (if the style is undefined) + if (strTokens.hasMoreTokens()) { + substr = strTokens.nextToken(); + + try { + // if second token is the font size + fontSize = Integer.parseInt(substr); + } catch (NumberFormatException e) { + // then second token is the font style + fontStyle = getFontStyle(substr); + } + + } + + // Font Size + if (strTokens.hasMoreTokens()) { + try { + fontSize = Integer.parseInt(strTokens.nextToken()); + } catch (NumberFormatException e) { + } + } + + return new Font(fontName, fontStyle, fontSize); + } + + /** + * Performs the specified affine transform to the Font and returns a new + * Font. + * + * @param trans + * the AffineTransform. + * @return the Font object. + * @throws IllegalArgumentException + * if affine transform parameter is null. + */ + @SuppressWarnings("unchecked") + public Font deriveFont(AffineTransform trans) { + + if (trans == null) { + // awt.94=transform can not be null + throw new IllegalArgumentException(Messages.getString("awt.94")); //$NON-NLS-1$ + } + + Hashtable derivefRequestedAttributes = (Hashtable)fRequestedAttributes + .clone(); + + derivefRequestedAttributes.put(TextAttribute.TRANSFORM, new TransformAttribute(trans)); + + return new Font(derivefRequestedAttributes); + + } + + /** + * Returns a new Font that is a copy of the current Font modified so that + * the size is the specified size. + * + * @param size + * the size of font. + * @return the Font object. + */ + @SuppressWarnings("unchecked") + public Font deriveFont(float size) { + Hashtable derivefRequestedAttributes = (Hashtable)fRequestedAttributes + .clone(); + derivefRequestedAttributes.put(TextAttribute.SIZE, new Float(size)); + return new Font(derivefRequestedAttributes); + } + + /** + * Returns a new Font that is a copy of the current Font modified so that + * the style is the specified style. + * + * @param style + * the style of font. + * @return the Font object. + */ + @SuppressWarnings("unchecked") + public Font deriveFont(int style) { + Hashtable derivefRequestedAttributes = (Hashtable)fRequestedAttributes + .clone(); + + if ((style & Font.BOLD) != 0) { + derivefRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); + } else if (derivefRequestedAttributes.get(TextAttribute.WEIGHT) != null) { + derivefRequestedAttributes.remove(TextAttribute.WEIGHT); + } + + if ((style & Font.ITALIC) != 0) { + derivefRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); + } else if (derivefRequestedAttributes.get(TextAttribute.POSTURE) != null) { + derivefRequestedAttributes.remove(TextAttribute.POSTURE); + } + + return new Font(derivefRequestedAttributes); + } + + /** + * Returns a new Font that is a copy of the current Font modified to match + * the specified style and with the specified affine transform applied to + * its glyphs. + * + * @param style + * the style of font. + * @param trans + * the AffineTransform. + * @return the Font object. + */ + @SuppressWarnings("unchecked") + public Font deriveFont(int style, AffineTransform trans) { + + if (trans == null) { + // awt.94=transform can not be null + throw new IllegalArgumentException(Messages.getString("awt.94")); //$NON-NLS-1$ + } + Hashtable derivefRequestedAttributes = (Hashtable)fRequestedAttributes + .clone(); + + if ((style & BOLD) != 0) { + derivefRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); + } else if (derivefRequestedAttributes.get(TextAttribute.WEIGHT) != null) { + derivefRequestedAttributes.remove(TextAttribute.WEIGHT); + } + + if ((style & ITALIC) != 0) { + derivefRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); + } else if (derivefRequestedAttributes.get(TextAttribute.POSTURE) != null) { + derivefRequestedAttributes.remove(TextAttribute.POSTURE); + } + derivefRequestedAttributes.put(TextAttribute.TRANSFORM, new TransformAttribute(trans)); + + return new Font(derivefRequestedAttributes); + } + + /** + * Returns a new Font that is a copy of the current Font modified so that + * the size and style are the specified size and style. + * + * @param style + * the style of font. + * @param size + * the size of font. + * @return the Font object. + */ + @SuppressWarnings("unchecked") + public Font deriveFont(int style, float size) { + Hashtable derivefRequestedAttributes = (Hashtable)fRequestedAttributes + .clone(); + + if ((style & BOLD) != 0) { + derivefRequestedAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); + } else if (derivefRequestedAttributes.get(TextAttribute.WEIGHT) != null) { + derivefRequestedAttributes.remove(TextAttribute.WEIGHT); + } + + if ((style & ITALIC) != 0) { + derivefRequestedAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); + } else if (derivefRequestedAttributes.get(TextAttribute.POSTURE) != null) { + derivefRequestedAttributes.remove(TextAttribute.POSTURE); + } + + derivefRequestedAttributes.put(TextAttribute.SIZE, new Float(size)); + return new Font(derivefRequestedAttributes); + + } + + /** + * Returns a new Font object with a new set of font attributes. + * + * @param attributes + * the map of attributes. + * @return the Font. + */ + @SuppressWarnings("unchecked") + public Font deriveFont(Map attributes) { + Attribute[] avalAttributes = this.getAvailableAttributes(); + + Hashtable derivefRequestedAttributes = (Hashtable)fRequestedAttributes + .clone(); + Object currAttribute; + for (Attribute element : avalAttributes) { + currAttribute = attributes.get(element); + if (currAttribute != null) { + derivefRequestedAttributes.put(element, currAttribute); + } + } + return new Font(derivefRequestedAttributes); + } + + /** + * Compares the specified Object with the current Font. + * + * @param obj + * the Object to be compared. + * @return true, if the specified Object is an instance of Font with the + * same family, size, and style as this Font, false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj != null) { + try { + Font font = (Font)obj; + + return ((this.style == font.style) && (this.size == font.size) + && this.name.equals(font.name) && (this.pointSize == font.pointSize) && (this + .getTransform()).equals(font.getTransform())); + } catch (ClassCastException e) { + } + } + + return false; + } + + /** + * Gets the map of font's attributes. + * + * @return the map of font's attributes. + */ + @SuppressWarnings("unchecked") + public Map getAttributes() { + return (Map)fRequestedAttributes.clone(); + } + + /** + * Gets the keys of all available attributes. + * + * @return the keys array of all available attributes. + */ + public Attribute[] getAvailableAttributes() { + Attribute[] attrs = { + TextAttribute.FAMILY, TextAttribute.POSTURE, TextAttribute.SIZE, + TextAttribute.TRANSFORM, TextAttribute.WEIGHT, TextAttribute.SUPERSCRIPT, + TextAttribute.WIDTH + }; + return attrs; + } + + /** + * Gets the baseline for this character. + * + * @param c + * the character. + * @return the baseline for this character. + */ + public byte getBaselineFor(char c) { + // TODO: implement using TT BASE table data + return 0; + } + + /** + * Gets the family name of the Font. + * + * @return the family name of the Font. + */ + public String getFamily() { + if (fRequestedAttributes != null) { + fRequestedAttributes.get(TextAttribute.FAMILY); + } + return null; + } + + /** + * Returns the family name of this Font associated with the specified + * locale. + * + * @param l + * the locale. + * @return the family name of this Font associated with the specified + * locale. + */ + public String getFamily(Locale l) { + if (l == null) { + // awt.01='{0}' parameter is null + throw new NullPointerException(Messages.getString("awt.01", "Locale")); //$NON-NLS-1$ //$NON-NLS-2$ + } + return getFamily(); + } + + /** + * Gets a Font with the specified attribute set. + * + * @param attributes + * the attributes to be assigned to the new Font. + * @return the Font. + */ + public static Font getFont(Map attributes) { + Font fnt = (Font)attributes.get(TextAttribute.FONT); + if (fnt != null) { + return fnt; + } + return new Font(attributes); + } + + /** + * Gets a Font object from the system properties list with the specified + * name or returns the specified Font if there is no such property. + * + * @param sp + * the specified property name. + * @param f + * the Font. + * @return the Font object from the system properties list with the + * specified name or the specified Font if there is no such + * property. + */ + public static Font getFont(String sp, Font f) { + String pr = System.getProperty(sp); + if (pr == null) { + return f; + } + return decode(pr); + } + + /** + * Gets a Font object from the system properties list with the specified + * name. + * + * @param sp + * the system property name. + * @return the Font, or null if there is no such property with the specified + * name. + */ + public static Font getFont(String sp) { + return getFont(sp, null); + } + + /** + * Gets the font name. + * + * @return the font name. + */ + public String getFontName() { + if (fRequestedAttributes != null) { + fRequestedAttributes.get(TextAttribute.FAMILY); + } + return null; + } + + /** + * Returns the font name associated with the specified locale. + * + * @param l + * the locale. + * @return the font name associated with the specified locale. + */ + public String getFontName(Locale l) { + return getFamily(); + } + + /** + * Returns a LineMetrics object created with the specified parameters. + * + * @param chars + * the chars array. + * @param start + * the start offset. + * @param end + * the end offset. + * @param frc + * the FontRenderContext. + * @return the LineMetrics for the specified parameters. + */ + public LineMetrics getLineMetrics(char[] chars, int start, int end, FontRenderContext frc) { + if (frc == null) { + // awt.00=FontRenderContext is null + throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$ + } + + // FontMetrics fm = AndroidGraphics2D.getInstance().getFontMetrics(); + FontMetrics fm = new FontMetricsImpl(this); + float[] fmet = { + fm.getAscent(), fm.getDescent(), fm.getLeading() + }; + return new LineMetricsImpl(chars.length, fmet, null); + } + + /** + * Returns a LineMetrics object created with the specified parameters. + * + * @param iter + * the CharacterIterator. + * @param start + * the start offset. + * @param end + * the end offset. + * @param frc + * the FontRenderContext. + * @return the LineMetrics for the specified parameters. + */ + public LineMetrics getLineMetrics(CharacterIterator iter, int start, int end, + FontRenderContext frc) { + + if (frc == null) { + // awt.00=FontRenderContext is null + throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$ + } + + String resultString; + int iterCount; + + iterCount = end - start; + if (iterCount < 0) { + resultString = ""; //$NON-NLS-1$ + } else { + char[] chars = new char[iterCount]; + int i = 0; + for (char c = iter.setIndex(start); c != CharacterIterator.DONE && (i < iterCount); c = iter + .next()) { + chars[i] = c; + i++; + } + resultString = new String(chars); + } + return this.getLineMetrics(resultString, frc); + } + + /** + * Returns a LineMetrics object created with the specified parameters. + * + * @param str + * the String. + * @param frc + * the FontRenderContext. + * @return the LineMetrics for the specified parameters. + */ + public LineMetrics getLineMetrics(String str, FontRenderContext frc) { + // FontMetrics fm = AndroidGraphics2D.getInstance().getFontMetrics(); + FontMetrics fm = new FontMetricsImpl(this); + float[] fmet = { + fm.getAscent(), fm.getDescent(), fm.getLeading() + }; + // Log.i("FONT FMET", fmet.toString()); + return new LineMetricsImpl(str.length(), fmet, null); + + } + + /** + * Returns a LineMetrics object created with the specified parameters. + * + * @param str + * the String. + * @param start + * the start offset. + * @param end + * the end offset. + * @param frc + * the FontRenderContext. + * @return the LineMetrics for the specified parameters. + */ + public LineMetrics getLineMetrics(String str, int start, int end, FontRenderContext frc) { + return this.getLineMetrics(str.substring(start, end), frc); + } + + /** + * Gets the logical bounds of the specified String in the specified + * FontRenderContext. The logical bounds contains the origin, ascent, + * advance, and height. + * + * @param ci + * the specified CharacterIterator. + * @param start + * the start offset. + * @param end + * the end offset. + * @param frc + * the FontRenderContext. + * @return a Rectangle2D object. + */ + public Rectangle2D getStringBounds(CharacterIterator ci, int start, int end, + FontRenderContext frc) { + int first = ci.getBeginIndex(); + int finish = ci.getEndIndex(); + char[] chars; + + if (start < first) { + // awt.95=Wrong start index: {0} + throw new IndexOutOfBoundsException(Messages.getString("awt.95", start)); //$NON-NLS-1$ + } + if (end > finish) { + // awt.96=Wrong finish index: {0} + throw new IndexOutOfBoundsException(Messages.getString("awt.96", end)); //$NON-NLS-1$ + } + if (start > end) { + // awt.97=Wrong range length: {0} + throw new IndexOutOfBoundsException(Messages.getString("awt.97", //$NON-NLS-1$ + (end - start))); + } + + if (frc == null) { + throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$ + } + + chars = new char[end - start]; + + ci.setIndex(start); + for (int i = 0; i < chars.length; i++) { + chars[i] = ci.current(); + ci.next(); + } + + return this.getStringBounds(chars, 0, chars.length, frc); + + } + + /** + * Gets the logical bounds of the specified String in the specified + * FontRenderContext. The logical bounds contains the origin, ascent, + * advance, and height. + * + * @param str + * the specified String. + * @param frc + * the FontRenderContext. + * @return a Rectangle2D object. + */ + public Rectangle2D getStringBounds(String str, FontRenderContext frc) { + char[] chars = str.toCharArray(); + return this.getStringBounds(chars, 0, chars.length, frc); + + } + + /** + * Gets the logical bounds of the specified String in the specified + * FontRenderContext. The logical bounds contains the origin, ascent, + * advance, and height. + * + * @param str + * the specified String. + * @param start + * the start offset. + * @param end + * the end offset. + * @param frc + * the FontRenderContext. + * @return a Rectangle2D object. + */ + public Rectangle2D getStringBounds(String str, int start, int end, FontRenderContext frc) { + + return this.getStringBounds((str.substring(start, end)), frc); + } + + /** + * Gets the logical bounds of the specified String in the specified + * FontRenderContext. The logical bounds contains the origin, ascent, + * advance, and height. + * + * @param chars + * the specified character array. + * @param start + * the start offset. + * @param end + * the end offset. + * @param frc + * the FontRenderContext. + * @return a Rectangle2D object. + */ + public Rectangle2D getStringBounds(char[] chars, int start, int end, FontRenderContext frc) { + if (start < 0) { + // awt.95=Wrong start index: {0} + throw new IndexOutOfBoundsException(Messages.getString("awt.95", start)); //$NON-NLS-1$ + } + if (end > chars.length) { + // awt.96=Wrong finish index: {0} + throw new IndexOutOfBoundsException(Messages.getString("awt.96", end)); //$NON-NLS-1$ + } + if (start > end) { + // awt.97=Wrong range length: {0} + throw new IndexOutOfBoundsException(Messages.getString("awt.97", //$NON-NLS-1$ + (end - start))); + } + + if (frc == null) { + throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$ + } + + FontPeerImpl peer = (FontPeerImpl)this.getPeer(); + + final int TRANSFORM_MASK = AffineTransform.TYPE_GENERAL_ROTATION + | AffineTransform.TYPE_GENERAL_TRANSFORM; + Rectangle2D bounds; + + AffineTransform transform = getTransform(); + + // XXX: for transforms where an angle between basis vectors is not 90 + // degrees Rectanlge2D class doesn't fit as Logical bounds. + if ((transform.getType() & TRANSFORM_MASK) == 0) { + int width = 0; + for (int i = start; i < end; i++) { + width += peer.charWidth(chars[i]); + } + // LineMetrics nlm = peer.getLineMetrics(); + + LineMetrics nlm = getLineMetrics(chars, start, end, frc); + + bounds = transform.createTransformedShape( + new Rectangle2D.Float(0, -nlm.getAscent(), width, nlm.getHeight())) + .getBounds2D(); + } else { + int len = end - start; + char[] subChars = new char[len]; + System.arraycopy(chars, start, subChars, 0, len); + bounds = createGlyphVector(frc, subChars).getLogicalBounds(); + } + return bounds; + } + + /** + * Gets the character's maximum bounds as defined in the specified + * FontRenderContext. + * + * @param frc + * the FontRenderContext. + * @return the character's maximum bounds. + */ + public Rectangle2D getMaxCharBounds(FontRenderContext frc) { + if (frc == null) { + // awt.00=FontRenderContext is null + throw new NullPointerException(Messages.getString("awt.00")); //$NON-NLS-1$ + } + + FontPeerImpl peer = (FontPeerImpl)this.getPeer(); + + Rectangle2D bounds = peer.getMaxCharBounds(frc); + AffineTransform transform = getTransform(); + // !! Documentation doesn't describe meaning of max char bounds + // for the fonts that have rotate transforms. For all transforms + // returned bounds are the bounds of transformed maxCharBounds + // Rectangle2D that corresponds to the font with identity transform. + // TODO: resolve this issue to return correct bounds + bounds = transform.createTransformedShape(bounds).getBounds2D(); + + return bounds; + } + + /** + * Returns a new GlyphVector object performing full layout of the text. + * + * @param frc + * the FontRenderContext. + * @param chars + * the character array to be layout. + * @param start + * the start offset of the text to use for the GlyphVector. + * @param count + * the count of characters to use for the GlyphVector. + * @param flags + * the flag indicating text direction: LAYOUT_RIGHT_TO_LEFT, + * LAYOUT_LEFT_TO_RIGHT. + * @return the GlyphVector. + */ + public GlyphVector layoutGlyphVector(FontRenderContext frc, char[] chars, int start, int count, + int flags) { + // TODO: implement method for bidirectional text. + // At the moment only LTR and RTL texts supported. + if (start < 0) { + // awt.95=Wrong start index: {0} + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.95", //$NON-NLS-1$ + start)); + } + + if (count < 0) { + // awt.98=Wrong count value, can not be negative: {0} + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.98", //$NON-NLS-1$ + count)); + } + + if (start + count > chars.length) { + // awt.99=Wrong [start + count] is out of range: {0} + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.99", //$NON-NLS-1$ + (start + count))); + } + + char[] out = new char[count]; + System.arraycopy(chars, start, out, 0, count); + + return new CommonGlyphVector(out, frc, this, flags); + } + + /** + * Returns the String representation of this Font. + * + * @return the String representation of this Font. + */ + @Override + public String toString() { + String stl = "plain"; //$NON-NLS-1$ + String result; + + if (this.isBold() && this.isItalic()) { + stl = "bolditalic"; //$NON-NLS-1$ + } + if (this.isBold() && !this.isItalic()) { + stl = "bold"; //$NON-NLS-1$ + } + + if (!this.isBold() && this.isItalic()) { + stl = "italic"; //$NON-NLS-1$ + } + + result = this.getClass().getName() + "[family=" + this.getFamily() + //$NON-NLS-1$ + ",name=" + this.name + //$NON-NLS-1$ + ",style=" + stl + //$NON-NLS-1$ + ",size=" + this.size + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + return result; + } + + /** + * Gets the postscript name of this Font. + * + * @return the postscript name of this Font. + */ + public String getPSName() { + FontPeerImpl peer = (FontPeerImpl)this.getPeer(); + return peer.getPSName(); + } + + /** + * Gets the logical name of this Font. + * + * @return the logical name of this Font. + */ + public String getName() { + return (this.name); + } + + /** + * Gets the peer of this Font. + * + * @return the peer of this Font. + * @deprecated Font rendering is platform independent now. + */ + @Deprecated + public java.awt.peer.FontPeer getPeer() { + if (fontPeer == null) { + fontPeer = (FontPeerImpl)Toolkit.getDefaultToolkit().getGraphicsFactory().getFontPeer( + this); + } + return fontPeer; + + } + + /** + * Gets the transform acting on this Font (from the Font's attributes). + * + * @return the transformation of this Font. + */ + public AffineTransform getTransform() { + Object transform = fRequestedAttributes.get(TextAttribute.TRANSFORM); + + if (transform != null) { + if (transform instanceof TransformAttribute) { + return ((TransformAttribute)transform).getTransform(); + } + if (transform instanceof AffineTransform) { + return new AffineTransform((AffineTransform)transform); + } + } else { + transform = new AffineTransform(); + } + return (AffineTransform)transform; + + } + + /** + * Checks if this font is transformed or not. + * + * @return true, if this font is transformed, false otherwise. + */ + public boolean isTransformed() { + return this.transformed; + } + + /** + * Checks if this font has plain style or not. + * + * @return true, if this font has plain style, false otherwise. + */ + public boolean isPlain() { + return (this.style == PLAIN); + } + + /** + * Checks if this font has italic style or not. + * + * @return true, if this font has italic style, false otherwise. + */ + public boolean isItalic() { + return (this.style & ITALIC) != 0; + } + + /** + * Checks if this font has bold style or not. + * + * @return true, if this font has bold style, false otherwise. + */ + public boolean isBold() { + return (this.style & BOLD) != 0; + } + + /** + * Returns true if this Font has uniform line metrics. + * + * @return true if this Font has uniform line metrics, false otherwise. + */ + public boolean hasUniformLineMetrics() { + FontPeerImpl peer = (FontPeerImpl)this.getPeer(); + return peer.hasUniformLineMetrics(); + } + + /** + * Returns hash code of this Font object. + * + * @return the hash code of this Font object. + */ + @Override + public int hashCode() { + HashCode hash = new HashCode(); + + hash.append(this.name); + hash.append(this.style); + hash.append(this.size); + + return hash.hashCode(); + } + + /** + * Gets the style of this Font. + * + * @return the style of this Font. + */ + public int getStyle() { + return this.style; + } + + /** + * Gets the size of this Font. + * + * @return the size of this Font. + */ + public int getSize() { + return this.size; + } + + /** + * Gets the number of glyphs for this Font. + * + * @return the number of glyphs for this Font. + */ + public int getNumGlyphs() { + if (numGlyphs == -1) { + FontPeerImpl peer = (FontPeerImpl)this.getPeer(); + this.numGlyphs = peer.getNumGlyphs(); + } + return this.numGlyphs; + } + + /** + * Gets the glyphCode which is used as default glyph when this Font does not + * have a glyph for a specified Unicode. + * + * @return the missing glyph code. + */ + public int getMissingGlyphCode() { + if (missingGlyphCode == -1) { + FontPeerImpl peer = (FontPeerImpl)this.getPeer(); + this.missingGlyphCode = peer.getMissingGlyphCode(); + } + return this.missingGlyphCode; + } + + /** + * Gets the float value of font's size. + * + * @return the float value of font's size. + */ + public float getSize2D() { + return this.pointSize; + } + + /** + * Gets the italic angle of this Font. + * + * @return the italic angle of this Font. + */ + public float getItalicAngle() { + FontPeerImpl peer = (FontPeerImpl)this.getPeer(); + return peer.getItalicAngle(); + } + + /** + * Creates the font with the specified font format and font file. + * + * @param fontFormat + * the font format. + * @param fontFile + * the file object represented the input data for the font. + * @return the Font. + * @throws FontFormatException + * is thrown if fontFile does not contain the required font + * tables for the specified format. + * @throws IOException + * signals that an I/O exception has occurred. + */ + public static Font createFont(int fontFormat, File fontFile) throws FontFormatException, + IOException { + // ???AWT not supported + InputStream is = new FileInputStream(fontFile); + try { + return createFont(fontFormat, is); + } finally { + is.close(); + } + } + + /** + * Creates the font with the specified font format and input stream. + * + * @param fontFormat + * the font format. + * @param fontStream + * the input stream represented input data for the font. + * @return the Font. + * @throws FontFormatException + * is thrown if fontFile does not contain the required font + * tables for the specified format. + * @throws IOException + * signals that an I/O exception has occurred. + */ + public static Font createFont(int fontFormat, InputStream fontStream) + throws FontFormatException, IOException { + + // ???AWT not supported + + BufferedInputStream buffStream; + int bRead = 0; + int size = 8192; + // memory page size, for the faster reading + byte buf[] = new byte[size]; + + if (fontFormat != TRUETYPE_FONT) { // awt.9A=Unsupported font format + throw new IllegalArgumentException(Messages.getString("awt.9A")); //$NON-NLS-1$ + } + + /* Get font file in system-specific directory */ + + File fontFile = Toolkit.getDefaultToolkit().getGraphicsFactory().getFontManager() + .getTempFontFile(); + + // BEGIN android-modified + buffStream = new BufferedInputStream(fontStream, 8192); + // END android-modified + FileOutputStream fOutStream = new FileOutputStream(fontFile); + + bRead = buffStream.read(buf, 0, size); + + while (bRead != -1) { + fOutStream.write(buf, 0, bRead); + bRead = buffStream.read(buf, 0, size); + } + + buffStream.close(); + fOutStream.close(); + + Font font = null; + + font = Toolkit.getDefaultToolkit().getGraphicsFactory().embedFont( + fontFile.getAbsolutePath()); + if (font == null) { // awt.9B=Can't create font - bad font data + throw new FontFormatException(Messages.getString("awt.9B")); //$NON-NLS-1$ + } + return font; + } + +} diff --git a/awt/java/awt/FontFormatException.java b/awt/java/awt/FontFormatException.java new file mode 100644 index 000000000..806711a76 --- /dev/null +++ b/awt/java/awt/FontFormatException.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt; + +/** + * The FontFormatException class is used to provide notification and information + * that font can't be created. + * + * @since Android 1.0 + */ +public class FontFormatException extends Exception { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -4481290147811361272L; + + /** + * Instantiates a new font format exception with detailed message. + * + * @param reason + * the detailed message. + */ + public FontFormatException(String reason) { + super(reason); + } + +} diff --git a/awt/java/awt/FontMetrics.java b/awt/java/awt/FontMetrics.java new file mode 100644 index 000000000..90826265a --- /dev/null +++ b/awt/java/awt/FontMetrics.java @@ -0,0 +1,466 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt; + +import java.awt.font.FontRenderContext; +import java.awt.font.LineMetrics; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; +import java.text.CharacterIterator; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The FontMetrics class contains information about the rendering of a + * particular font on a particular screen. + *

+ * Each character in the Font has three values that help define where to place + * it: an ascent, a descent, and an advance. The ascent is the distance the + * character extends above the baseline. The descent is the distance the + * character extends below the baseline. The advance width defines the position + * at which the next character should be placed. + *

+ * An array of characters or a string has an ascent, a descent, and an advance + * width too. The ascent or descent of the array is specified by the maximum + * ascent or descent of the characters in the array. The advance width is the + * sum of the advance widths of each of the characters in the character array. + *

+ * + * @since Android 1.0 + */ +public abstract class FontMetrics implements Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 1681126225205050147L; + + /** + * The font from which the FontMetrics is created. + */ + protected Font font; + + /** + * Instantiates a new font metrics from the specified Font. + * + * @param fnt + * the Font. + */ + protected FontMetrics(Font fnt) { + this.font = fnt; + } + + /** + * Returns the String representation of this FontMetrics. + * + * @return the string. + */ + @Override + public String toString() { + return this.getClass().getName() + "[font=" + this.getFont() + //$NON-NLS-1$ + "ascent=" + this.getAscent() + //$NON-NLS-1$ + ", descent=" + this.getDescent() + //$NON-NLS-1$ + ", height=" + this.getHeight() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Gets the font associated with this FontMetrics. + * + * @return the font associated with this FontMetrics. + */ + public Font getFont() { + return font; + } + + /** + * Gets the height of the text line in this Font. + * + * @return the height of the text line in this Font. + */ + public int getHeight() { + return this.getAscent() + this.getDescent() + this.getLeading(); + } + + /** + * Gets the font ascent of the Font associated with this FontMetrics. The + * font ascent is the distance from the font's baseline to the top of most + * alphanumeric characters. + * + * @return the ascent of the Font associated with this FontMetrics. + */ + public int getAscent() { + return 0; + } + + /** + * Gets the font descent of the Font associated with this FontMetrics. The + * font descent is the distance from the font's baseline to the bottom of + * most alphanumeric characters with descenders. + * + * @return the descent of the Font associated with this FontMetrics. + */ + public int getDescent() { + return 0; + } + + /** + * Gets the leading of the Font associated with this FontMetrics. + * + * @return the leading of the Font associated with this FontMetrics. + */ + public int getLeading() { + return 0; + } + + /** + * Gets the LineMetrics object for the specified CharacterIterator in the + * specified Graphics. + * + * @param ci + * the CharacterIterator. + * @param beginIndex + * the offset. + * @param limit + * the number of characters to be used. + * @param context + * the Graphics. + * @return the LineMetrics object for the specified CharacterIterator in the + * specified Graphics. + */ + public LineMetrics getLineMetrics(CharacterIterator ci, int beginIndex, int limit, + Graphics context) { + return font.getLineMetrics(ci, beginIndex, limit, this.getFRCFromGraphics(context)); + } + + /** + * Gets the LineMetrics object for the specified String in the specified + * Graphics. + * + * @param str + * the String. + * @param context + * the Graphics. + * @return the LineMetrics object for the specified String in the specified + * Graphics. + */ + public LineMetrics getLineMetrics(String str, Graphics context) { + return font.getLineMetrics(str, this.getFRCFromGraphics(context)); + } + + /** + * Gets the LineMetrics object for the specified character array in the + * specified Graphics. + * + * @param chars + * the character array. + * @param beginIndex + * the offset of array. + * @param limit + * the number of characters to be used. + * @param context + * the Graphics. + * @return the LineMetrics object for the specified character array in the + * specified Graphics. + */ + public LineMetrics getLineMetrics(char[] chars, int beginIndex, int limit, Graphics context) { + return font.getLineMetrics(chars, beginIndex, limit, this.getFRCFromGraphics(context)); + } + + /** + * Gets the LineMetrics object for the specified String in the specified + * Graphics. + * + * @param str + * the String. + * @param beginIndex + * the offset. + * @param limit + * the number of characters to be used. + * @param context + * the Graphics. + * @return the LineMetrics object for the specified String in the specified + * Graphics. + */ + public LineMetrics getLineMetrics(String str, int beginIndex, int limit, Graphics context) { + return font.getLineMetrics(str, beginIndex, limit, this.getFRCFromGraphics(context)); + } + + /** + * Returns the character's maximum bounds in the specified Graphics context. + * + * @param context + * the Graphics context. + * @return the character's maximum bounds in the specified Graphics context. + */ + public Rectangle2D getMaxCharBounds(Graphics context) { + return this.font.getMaxCharBounds(this.getFRCFromGraphics(context)); + } + + /** + * Gets the bounds of the specified CharacterIterator in the specified + * Graphics context. + * + * @param ci + * the CharacterIterator. + * @param beginIndex + * the begin offset of the array. + * @param limit + * the number of characters. + * @param context + * the Graphics. + * @return the bounds of the specified CharacterIterator in the specified + * Graphics context. + */ + public Rectangle2D getStringBounds(CharacterIterator ci, int beginIndex, int limit, + Graphics context) { + return font.getStringBounds(ci, beginIndex, limit, this.getFRCFromGraphics(context)); + } + + /** + * Gets the bounds of the specified String in the specified Graphics + * context. + * + * @param str + * the String. + * @param beginIndex + * the begin offset of the array. + * @param limit + * the number of characters. + * @param context + * the Graphics. + * @return the bounds of the specified String in the specified Graphics + * context. + */ + public Rectangle2D getStringBounds(String str, int beginIndex, int limit, Graphics context) { + return font.getStringBounds(str, beginIndex, limit, this.getFRCFromGraphics(context)); + } + + /** + * Gets the bounds of the specified characters array in the specified + * Graphics context. + * + * @param chars + * the characters array. + * @param beginIndex + * the begin offset of the array. + * @param limit + * the number of characters. + * @param context + * the Graphics. + * @return the bounds of the specified characters array in the specified + * Graphics context. + */ + public Rectangle2D getStringBounds(char[] chars, int beginIndex, int limit, Graphics context) { + return font.getStringBounds(chars, beginIndex, limit, this.getFRCFromGraphics(context)); + } + + /** + * Gets the bounds of the specified String in the specified Graphics + * context. + * + * @param str + * the String. + * @param context + * the Graphics. + * @return the bounds of the specified String in the specified Graphics + * context. + */ + public Rectangle2D getStringBounds(String str, Graphics context) { + return font.getStringBounds(str, this.getFRCFromGraphics(context)); + } + + /** + * Checks if the Font has uniform line metrics or not. The Font can contain + * characters of other fonts for covering character set. In this case the + * Font isn't uniform. + * + * @return true, if the Font has uniform line metrics, false otherwise. + */ + public boolean hasUniformLineMetrics() { + return this.font.hasUniformLineMetrics(); + } + + /** + * Returns the distance from the leftmost point to the rightmost point on + * the string's baseline showing the specified array of bytes in this Font. + * + * @param data + * the array of bytes to be measured. + * @param off + * the start offset. + * @param len + * the number of bytes to be measured. + * @return the advance width of the array. + */ + public int bytesWidth(byte[] data, int off, int len) { + int width = 0; + if ((off >= data.length) || (off < 0)) { + // awt.13B=offset off is out of range + throw new IllegalArgumentException(Messages.getString("awt.13B")); //$NON-NLS-1$ + } + + if ((off + len > data.length)) { + // awt.13C=number of elemets len is out of range + throw new IllegalArgumentException(Messages.getString("awt.13C")); //$NON-NLS-1$ + } + + for (int i = off; i < off + len; i++) { + width += charWidth(data[i]); + } + + return width; + } + + /** + * Returns the distance from the leftmost point to the rightmost point on + * the string's baseline showing the specified array of characters in this + * Font. + * + * @param data + * the array of characters to be measured. + * @param off + * the start offset. + * @param len + * the number of bytes to be measured. + * @return the advance width of the array. + */ + public int charsWidth(char[] data, int off, int len) { + int width = 0; + if ((off >= data.length) || (off < 0)) { + // awt.13B=offset off is out of range + throw new IllegalArgumentException(Messages.getString("awt.13B")); //$NON-NLS-1$ + } + + if ((off + len > data.length)) { + // awt.13C=number of elemets len is out of range + throw new IllegalArgumentException(Messages.getString("awt.13C")); //$NON-NLS-1$ + } + + for (int i = off; i < off + len; i++) { + width += charWidth(data[i]); + } + + return width; + } + + /** + * Returns the distance from the leftmost point to the rightmost point of + * the specified character in this Font. + * + * @param ch + * the specified Unicode point code of character to be measured. + * @return the advance width of the character. + */ + public int charWidth(int ch) { + return 0; + } + + /** + * Returns the distance from the leftmost point to the rightmost point of + * the specified character in this Font. + * + * @param ch + * the specified character to be measured. + * @return the advance width of the character. + */ + public int charWidth(char ch) { + return 0; + } + + /** + * Gets the maximum advance width of character in this Font. + * + * @return the maximum advance width of character in this Font. + */ + public int getMaxAdvance() { + return 0; + } + + /** + * Gets the maximum font ascent of the Font associated with this + * FontMetrics. + * + * @return the maximum font ascent of the Font associated with this + * FontMetrics. + */ + public int getMaxAscent() { + return 0; + } + + /** + * Gets the maximum font descent of character in this Font. + * + * @return the maximum font descent of character in this Font. + * @deprecated Replaced by getMaxDescent() method. + */ + @Deprecated + public int getMaxDecent() { + return 0; + } + + /** + * Gets the maximum font descent of character in this Font. + * + * @return the maximum font descent of character in this Font. + */ + public int getMaxDescent() { + return 0; + } + + /** + * Gets the advance widths of the characters in the Font. + * + * @return the advance widths of the characters in the Font. + */ + public int[] getWidths() { + return null; + } + + /** + * Returns the advance width for the specified String in this Font. + * + * @param str + * String to be measured. + * @return the the advance width for the specified String in this Font. + */ + public int stringWidth(String str) { + return 0; + } + + /** + * Returns a FontRenderContext instance of the Graphics context specified. + * + * @param context + * the specified Graphics context. + * @return a FontRenderContext of the specified Graphics context. + */ + private FontRenderContext getFRCFromGraphics(Graphics context) { + FontRenderContext frc; + if (context instanceof Graphics2D) { + frc = ((Graphics2D)context).getFontRenderContext(); + } else { + frc = new FontRenderContext(null, false, false); + } + + return frc; + } +} diff --git a/awt/java/awt/GradientPaint.java b/awt/java/awt/GradientPaint.java new file mode 100644 index 000000000..3b32ef530 --- /dev/null +++ b/awt/java/awt/GradientPaint.java @@ -0,0 +1,255 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The GradientPaint class defines a way to fill a Shape with a linear color + * gradient pattern. + *

+ * The GradientPaint's fill pattern is determined by two points and two colors, + * plus the cyclic mode option. Each of the two points is painted with its + * corresponding color, and on the line segment connecting the two points, the + * color is proportionally changed between the two colors. For points on the + * same line which are not between the two specified points (outside of the + * connecting segment) their color is determined by the cyclic mode option. If + * the mode is cyclic, then the rest of the line repeats the color pattern of + * the connecting segment, cycling back and forth between the two colors. If + * not, the mode is acyclic which means that all points on the line outside the + * connecting line segment are given the same color as the closest of the two + * specified points. + *

+ * The color of points that are not on the line connecting the two specified + * points are given by perpendicular projection: by taking the set of lines + * perpendicular to the connecting line and for each one, the whole line is + * colored with the same color. + * + * @since Android 1.0 + */ +public class GradientPaint implements Paint { + + /** + * The start point color. + */ + Color color1; + + /** + * The end color point. + */ + Color color2; + + /** + * The location of the start point. + */ + Point2D point1; + + /** + * The location of the end point. + */ + Point2D point2; + + /** + * The indicator of cycle filling. If TRUE filling repeated outside points + * stripe, if FALSE solid color filling outside. + */ + boolean cyclic; + + /** + * Instantiates a new GradientPaint with cyclic or acyclic mode. + * + * @param point1 + * the first specified point. + * @param color1 + * the Color of the first specified point. + * @param point2 + * the second specified point. + * @param color2 + * the Color of the second specified point. + * @param cyclic + * the cyclic mode - true if the gradient pattern should cycle + * repeatedly between the two colors; false otherwise. + */ + public GradientPaint(Point2D point1, Color color1, Point2D point2, Color color2, boolean cyclic) { + if (point1 == null || point2 == null) { + // awt.6D=Point is null + throw new NullPointerException(Messages.getString("awt.6D")); //$NON-NLS-1$ + } + if (color1 == null || color2 == null) { + // awt.6E=Color is null + throw new NullPointerException(Messages.getString("awt.6E")); //$NON-NLS-1$ + } + + this.point1 = point1; + this.point2 = point2; + this.color1 = color1; + this.color2 = color2; + this.cyclic = cyclic; + } + + /** + * Instantiates a new GradientPaint with cyclic or acyclic mode; points are + * specified by coordinates. + * + * @param x1 + * the X coordinate of the first point. + * @param y1 + * the Y coordinate of the first point. + * @param color1 + * the color of the first point. + * @param x2 + * the X coordinate of the second point. + * @param y2 + * the Y coordinate of the second point. + * @param color2 + * the color of the second point. + * @param cyclic + * the cyclic mode - true if the gradient pattern should cycle + * repeatedly between the two colors; false otherwise. + */ + public GradientPaint(float x1, float y1, Color color1, float x2, float y2, Color color2, + boolean cyclic) { + this(new Point2D.Float(x1, y1), color1, new Point2D.Float(x2, y2), color2, cyclic); + } + + /** + * Instantiates a new acyclic GradientPaint; points are specified by + * coordinates. + * + * @param x1 + * the X coordinate of the first point. + * @param y1 + * the Y coordinate of the first point. + * @param color1 + * the color of the first point. + * @param x2 + * the X coordinate of the second point. + * @param y2 + * the Y coordinate of the second point. + * @param color2 + * the color of the second point. + */ + public GradientPaint(float x1, float y1, Color color1, float x2, float y2, Color color2) { + this(x1, y1, color1, x2, y2, color2, false); + } + + /** + * Instantiates a new acyclic GradientPaint. + * + * @param point1 + * the first specified point. + * @param color1 + * the Color of the first specified point. + * @param point2 + * the second specified point. + * @param color2 + * the Color of the second specified point. + */ + public GradientPaint(Point2D point1, Color color1, Point2D point2, Color color2) { + this(point1, color1, point2, color2, false); + } + + /** + * Creates PaintContext for a color pattern generating. + * + * @param cm + * the ColorModel of the Paint data. + * @param deviceBounds + * the bounding Rectangle of graphics primitives being rendered + * in the device space. + * @param userBounds + * the bounding Rectangle of graphics primitives being rendered + * in the user space. + * @param t + * the AffineTransform from user space into device space. + * @param hints + * the RrenderingHints object. + * @return the PaintContext for color pattern generating. + * @see java.awt.Paint#createContext(java.awt.image.ColorModel, + * java.awt.Rectangle, java.awt.geom.Rectangle2D, + * java.awt.geom.AffineTransform, java.awt.RenderingHints) + */ + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, AffineTransform t, RenderingHints hints) { + return new GradientPaintContext(cm, t, point1, color1, point2, color2, cyclic); + } + + /** + * Gets the color of the first point. + * + * @return the color of the first point. + */ + public Color getColor1() { + return color1; + } + + /** + * Gets the color of the second point. + * + * @return the color of the second point. + */ + public Color getColor2() { + return color2; + } + + /** + * Gets the first point. + * + * @return the Point object - the first point. + */ + public Point2D getPoint1() { + return point1; + } + + /** + * Gets the second point. + * + * @return the Point object - the second point. + */ + public Point2D getPoint2() { + return point2; + } + + /** + * Gets the transparency mode for the GradientPaint. + * + * @return the transparency mode for the GradientPaint. + * @see java.awt.Transparency#getTransparency() + */ + public int getTransparency() { + int a1 = color1.getAlpha(); + int a2 = color2.getAlpha(); + return (a1 == 0xFF && a2 == 0xFF) ? OPAQUE : TRANSLUCENT; + } + + /** + * Returns the GradientPaint mode: true for cyclic mode, false for acyclic + * mode. + * + * @return true if the gradient cycles repeatedly between the two colors; + * false otherwise. + */ + public boolean isCyclic() { + return cyclic; + } +} diff --git a/awt/java/awt/GradientPaintContext.java b/awt/java/awt/GradientPaintContext.java new file mode 100644 index 000000000..74575f56d --- /dev/null +++ b/awt/java/awt/GradientPaintContext.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.image.ColorModel; +import java.awt.image.DataBufferInt; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +class GradientPaintContext implements PaintContext { + + /** + * The size of noncyclic part of color lookup table + */ + static int LOOKUP_SIZE = 256; + + /** + * The index mask to lookup color in the table + */ + static int LOOKUP_MASK = 0x1FF; + + /** + * The min value equivalent to zero. If absolute value less then ZERO it considered as zero. + */ + static double ZERO = 1E-10; + + /** + * The ColorModel user defined for PaintContext + */ + ColorModel cm; + + /** + * The indicator of cycle filling. + */ + boolean cyclic; + + /** + * The integer color value of the start point + */ + int c1; + + /** + * The integer color value of the end point + */ + int c2; + + /** + * The lookup gradient color table + */ + int[] table; + + /** + * The tempopary pre-calculated value to evalutae color index + */ + int dx; + + /** + * The tempopary pre-calculated value to evalutae color index + */ + int dy; + + /** + * The tempopary pre-calculated value to evalutae color index + */ + int delta; + + /** + * Constructs a new GradientPaintcontext + * @param cm - not used + * @param t - the fill transformation + * @param point1 - the start fill point + * @param color1 - color of the start point + * @param point2 - the end fill point + * @param color2 - color of the end point + * @param cyclic - the indicator of cycle filling + */ + GradientPaintContext(ColorModel cm, AffineTransform t, Point2D point1, Color color1, Point2D point2, Color color2, boolean cyclic) { + this.cyclic = cyclic; + this.cm = ColorModel.getRGBdefault(); + + c1 = color1.getRGB(); + c2 = color2.getRGB(); + + double px = point2.getX() - point1.getX(); + double py = point2.getY() - point1.getY(); + + Point2D p = t.transform(point1, null); + Point2D bx = new Point2D.Double(px, py); + Point2D by = new Point2D.Double(py, -px); + + t.deltaTransform(bx, bx); + t.deltaTransform(by, by); + + double vec = bx.getX() * by.getY() - bx.getY() * by.getX(); + + if (Math.abs(vec) < ZERO) { + dx = dy = delta = 0; + table = new int[1]; + table[0] = c1; + } else { + double mult = LOOKUP_SIZE * 256 / vec; + dx = (int)(by.getX() * mult); + dy = (int)(by.getY() * mult); + delta = (int)((p.getX() * by.getY() - p.getY() * by.getX()) * mult); + createTable(); + } + } + + /** + * Create color index lookup table. Calculate 256 step trasformation from + * the start point color to the end point color. Colors multiplied by 256 to do integer calculations. + */ + void createTable() { + double ca = (c1 >> 24) & 0xFF; + double cr = (c1 >> 16) & 0xFF; + double cg = (c1 >> 8) & 0xFF; + double cb = c1 & 0xFF; + + double k = 1.0 / LOOKUP_SIZE; + double da = (((c2 >> 24) & 0xFF) - ca) * k; + double dr = (((c2 >> 16) & 0xFF) - cr) * k; + double dg = (((c2 >> 8) & 0xFF) - cg) * k; + double db = ((c2 & 0xFF) - cb) * k; + + table = new int[cyclic ? LOOKUP_SIZE + LOOKUP_SIZE : LOOKUP_SIZE]; + for(int i = 0; i < LOOKUP_SIZE; i++) { + table[i] = + (int)ca << 24 | + (int)cr << 16 | + (int)cg << 8 | + (int)cb; + ca += da; + cr += dr; + cg += dg; + cb += db; + } + if (cyclic) { + for(int i = 0; i < LOOKUP_SIZE; i++) { + table[LOOKUP_SIZE + LOOKUP_SIZE - 1 - i] = table[i]; + } + } + } + + public ColorModel getColorModel() { + return cm; + } + + public void dispose() { + } + + public Raster getRaster(int x, int y, int w, int h) { + WritableRaster rast = cm.createCompatibleWritableRaster(w, h); + + int[] buf = ((DataBufferInt)rast.getDataBuffer()).getData(); + + int c = x * dy - y * dx - delta; + int cx = dy; + int cy = - w * dy - dx; + int k = 0; + + if (cyclic) { + for(int j = 0; j < h; j++) { + for(int i = 0; i < w; i++) { + buf[k++] = table[(c >> 8) & LOOKUP_MASK]; + c += cx; + } + c += cy; + } + } else { + for(int j = 0; j < h; j++) { + for(int i = 0; i < w; i++) { + int index = c >> 8; + buf[k++] = index < 0 ? c1 : index >= LOOKUP_SIZE ? c2 : table[index]; + c += cx; + } + c += cy; + } + } + + return rast; + } + +} + diff --git a/awt/java/awt/Graphics.java b/awt/java/awt/Graphics.java new file mode 100644 index 000000000..2d6e79fca --- /dev/null +++ b/awt/java/awt/Graphics.java @@ -0,0 +1,924 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +import java.awt.image.ImageObserver; +import java.text.AttributedCharacterIterator; + +/** + * The abstract Graphics class allows applications to draw on a screen or other + * rendering target. There are several properties which define rendering + * options: origin point, clipping area, color, font.
+ *
+ * The origin point specifies the beginning of the clipping area coordinate + * system. All coordinates used in rendering operations are computed with + * respect to this point. The clipping area defines the boundaries where + * rendering operations can be performed. Rendering operations can't modify + * pixels outside of the clipping area.
+ *
+ * The draw and fill methods allow applications to drawing shapes, text, images + * with specified font and color options in the specified part of the screen. + * + * @since Android 1.0 + */ +public abstract class Graphics { + + // Constructors + + /** + * Instantiates a new Graphics. This constructor is default for Graphics and + * can not be called directly. + */ + protected Graphics() { + } + + // Public methods + + /** + * Creates a copy of the Graphics object with a new origin and a new + * specified clip area. The new clip area is the rectangle defined by the + * origin point with coordinates X,Y and the given width and height. The + * coordinates of all subsequent rendering operations will be computed with + * respect to the new origin and can be performed only within the range of + * the clipping area dimensions. + * + * @param x + * the X coordinate of the original point. + * @param y + * the Y coordinate of the original point. + * @param width + * the width of clipping area. + * @param height + * the height of clipping area. + * @return the Graphics object with new origin point and clipping area. + */ + public Graphics create(int x, int y, int width, int height) { + Graphics res = create(); + res.translate(x, y); + res.clipRect(0, 0, width, height); + return res; + } + + /** + * Draws the highlighted outline of a rectangle. + * + * @param x + * the X coordinate of the rectangle's top left corner. + * @param y + * the Y coordinate of the rectangle's top left corner. + * @param width + * the width of rectangle. + * @param height + * the height of rectangle. + * @param raised + * a boolean value that determines whether the rectangle is drawn + * as raised or indented. + */ + public void draw3DRect(int x, int y, int width, int height, boolean raised) { + // Note: lighter/darker colors should be used to draw 3d rect. + // The resulting rect is (width+1)x(height+1). Stroke and paint + // attributes of + // the Graphics2D should be reset to the default values. + // fillRect is used instead of drawLine to bypass stroke + // reset/set and rasterization. + + Color color = getColor(); + Color colorUp, colorDown; + if (raised) { + colorUp = color.brighter(); + colorDown = color.darker(); + } else { + colorUp = color.darker(); + colorDown = color.brighter(); + } + + setColor(colorUp); + fillRect(x, y, width, 1); + fillRect(x, y + 1, 1, height); + + setColor(colorDown); + fillRect(x + width, y, 1, height); + fillRect(x + 1, y + height, width, 1); + } + + /** + * Draws the text represented by byte array. This method uses the current + * font and color for rendering. + * + * @param bytes + * the byte array which contains the text to be drawn. + * @param off + * the offset within the byte array of the text to be drawn. + * @param len + * the number of bytes of text to draw. + * @param x + * the X coordinate where the text is to be drawn. + * @param y + * the Y coordinate where the text is to be drawn. + */ + public void drawBytes(byte[] bytes, int off, int len, int x, int y) { + drawString(new String(bytes, off, len), x, y); + } + + /** + * Draws the text represented by character array. This method uses the + * current font and color for rendering. + * + * @param chars + * the character array. + * @param off + * the offset within the character array of the text to be drawn. + * @param len + * the number of characters which will be drawn. + * @param x + * the X coordinate where the text is to be drawn. + * @param y + * the Y coordinate where the text is to be drawn. + */ + public void drawChars(char[] chars, int off, int len, int x, int y) { + drawString(new String(chars, off, len), x, y); + } + + /** + * Draws the outline of a polygon which is defined by Polygon object. + * + * @param p + * the Polygon object. + */ + public void drawPolygon(Polygon p) { + drawPolygon(p.xpoints, p.ypoints, p.npoints); + } + + /** + * Draws the rectangle with the specified width and length and top left + * corner coordinates. + * + * @param x + * the X coordinate of the rectangle's top left corner. + * @param y + * the Y coordinate of the rectangle's top left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + */ + public void drawRect(int x, int y, int width, int height) { + int[] xpoints = { + x, x, x + width, x + width + }; + int[] ypoints = { + y, y + height, y + height, y + }; + + drawPolygon(xpoints, ypoints, 4); + } + + /** + * Fills the highlighted outline of a rectangle. + * + * @param x + * the X coordinate of the rectangle's top left corner. + * @param y + * the Y coordinate of the rectangle's top left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + * @param raised + * a boolean value that determines whether the rectangle is drawn + * as raised or indented. + */ + public void fill3DRect(int x, int y, int width, int height, boolean raised) { + // Note: lighter/darker colors should be used to draw 3d rect. + // The resulting rect is (width)x(height), same as fillRect. + // Stroke and paint attributes of the Graphics2D should be reset + // to the default values. fillRect is used instead of drawLine to + // bypass stroke reset/set and line rasterization. + + Color color = getColor(); + Color colorUp, colorDown; + if (raised) { + colorUp = color.brighter(); + colorDown = color.darker(); + setColor(color); + } else { + colorUp = color.darker(); + colorDown = color.brighter(); + setColor(colorUp); + } + + width--; + height--; + fillRect(x + 1, y + 1, width - 1, height - 1); + + setColor(colorUp); + fillRect(x, y, width, 1); + fillRect(x, y + 1, 1, height); + + setColor(colorDown); + fillRect(x + width, y, 1, height); + fillRect(x + 1, y + height, width, 1); + } + + /** + * Fills the polygon with the current color. + * + * @param p + * the Polygon object. + */ + public void fillPolygon(Polygon p) { + fillPolygon(p.xpoints, p.ypoints, p.npoints); + } + + /** + * Disposes of the Graphics. + */ + @Override + public void finalize() { + } + + /** + * Gets the bounds of the current clipping area as a rectangle and copies it + * to an existing rectangle. + * + * @param r + * a Rectangle object where the current clipping area bounds are + * to be copied. + * @return the bounds of the current clipping area. + */ + public Rectangle getClipBounds(Rectangle r) { + Shape clip = getClip(); + + if (clip != null) { + // TODO: Can we get shape bounds without creating Rectangle object? + Rectangle b = clip.getBounds(); + r.x = b.x; + r.y = b.y; + r.width = b.width; + r.height = b.height; + } + + return r; + } + + /** + * Gets the bounds of the current clipping area as a rectangle. + * + * @return a Rectangle object. + * @deprecated Use {@link #getClipBounds()} + */ + @Deprecated + public Rectangle getClipRect() { + return getClipBounds(); + } + + /** + * Gets the font metrics of the current font. The font metrics object + * contains information about the rendering of a particular font. + * + * @return the font metrics of current font. + */ + public FontMetrics getFontMetrics() { + return getFontMetrics(getFont()); + } + + /** + * Determines whether or not the specified rectangle intersects the current + * clipping area. + * + * @param x + * the X coordinate of the rectangle. + * @param y + * the Y coordinate of the rectangle. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + * @return true, if the specified rectangle intersects the current clipping + * area, false otherwise. + */ + public boolean hitClip(int x, int y, int width, int height) { + // TODO: Create package private method Rectangle.intersects(int, int, + // int, int); + return getClipBounds().intersects(new Rectangle(x, y, width, height)); + } + + /** + * Returns string which represents this Graphics object. + * + * @return the string which represents this Graphics object. + */ + @Override + public String toString() { + // TODO: Think about string representation of Graphics. + return "Graphics"; //$NON-NLS-1$ + } + + // Abstract methods + + /** + * Clears the specified rectangle. This method fills specified rectangle + * with background color. + * + * @param x + * the X coordinate of the rectangle. + * @param y + * the Y coordinate of the rectangle. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + */ + public abstract void clearRect(int x, int y, int width, int height); + + /** + * Intersects the current clipping area with a new rectangle. If the current + * clipping area is not defined, the rectangle becomes a new clipping area. + * Rendering operations are only allowed within the new the clipping area. + * + * @param x + * the X coordinate of the rectangle for intersection. + * @param y + * the Y coordinate of the rectangle for intersection. + * @param width + * the width of the rectangle for intersection. + * @param height + * the height of the rectangle for intersection. + */ + public abstract void clipRect(int x, int y, int width, int height); + + /** + * Copies the rectangle area to another area specified by a distance (dx, + * dy) from the original rectangle's location. Positive dx and dy values + * give a new location defined by translation to the right and down from the + * original location, negative dx and dy values - to the left and up. + * + * @param sx + * the X coordinate of the rectangle which will be copied. + * @param sy + * the Y coordinate of the rectangle which will be copied. + * @param width + * the width of the rectangle which will be copied. + * @param height + * the height of the rectangle which will be copied. + * @param dx + * the horizontal distance from the source rectangle's location + * to the copy's location. + * @param dy + * the vertical distance from the source rectangle's location to + * the copy's location. + */ + public abstract void copyArea(int sx, int sy, int width, int height, int dx, int dy); + + /** + * Creates a new copy of this Graphics. + * + * @return a new Graphics context which is a copy of this Graphics. + */ + public abstract Graphics create(); + + /** + * Disposes of the Graphics. This Graphics object can not be used after + * calling this method. + */ + public abstract void dispose(); + + /** + * Draws the arc covering the specified rectangle and using the current + * color. The rectangle is defined by the origin point (X, Y) and dimensions + * (width and height). The arc center is the the center of specified + * rectangle. The angle origin is 3 o'clock position, the positive angle is + * counted as a counter-clockwise rotation, the negative angle is counted as + * clockwise rotation. + * + * @param x + * the X origin coordinate of the rectangle which scales the arc. + * @param y + * the Y origin coordinate of the rectangle which scales the arc. + * @param width + * the width of the rectangle which scales the arc. + * @param height + * the height of the rectangle which scales the arc. + * @param sa + * start angle - the origin angle of arc. + * @param ea + * arc angle - the angular arc value relative to the start angle. + */ + public abstract void drawArc(int x, int y, int width, int height, int sa, int ea); + + /** + * Draws the specified image with the defined background color. The top left + * corner of image will be drawn at point (x, y) in current coordinate + * system. The image loading process notifies the specified Image Observer. + * This method returns true if the image has loaded, otherwise it returns + * false. + * + * @param img + * the image which will be drawn. + * @param x + * the X coordinate of the image top left corner. + * @param y + * the Y coordinate of the image top left corner. + * @param bgcolor + * the background color. + * @param observer + * the ImageObserver object which should be notified about image + * loading process. + * @return true, if loading image is successful or image is null, false + * otherwise. + */ + public abstract boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer); + + /** + * Draws the specified image. The top left corner of image will be drawn at + * point (x, y) in current coordinate system. The image loading process + * notifies the specified Image Observer. This method returns true if the + * image has loaded, otherwise it returns false. + * + * @param img + * the image which will be drawn. + * @param x + * the X coordinate of the image top left corner. + * @param y + * the Y coordinate of the image top left corner. + * @param observer + * the ImageObserver object which should be notified about image + * loading process. + * @return true, if loading image is successful or image is null, otherwise + * false. + */ + public abstract boolean drawImage(Image img, int x, int y, ImageObserver observer); + + /** + * Scales the specified image to fit in the specified rectangle and draws it + * with the defined background color. The top left corner of the image will + * be drawn at the point (x, y) in current coordinate system. The non-opaque + * pixels will be drawn in the background color. The image loading process + * notifies the specified Image Observer. This method returns true if the + * image has loaded, otherwise it returns false. + * + * @param img + * the image which will be drawn. + * @param x + * the X coordinate of the image's top left corner. + * @param y + * the Y coordinate of the image's top left corner. + * @param width + * the width of rectangle which scales the image. + * @param height + * the height of rectangle which scales the image. + * @param bgcolor + * the background color. + * @param observer + * the ImageObserver object which should be notified about image + * loading process. + * @return true, if loading image is successful or image is null, otherwise + * false. + */ + public abstract boolean drawImage(Image img, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer); + + /** + * Scales the specified image to fit in the specified rectangle and draws + * it. The top left corner of the image will be drawn at the point (x, y) in + * current coordinate system. The image loading process notifies the + * specified Image Observer. This method returns true if the image has + * loaded, otherwise it returns false. + * + * @param img + * the image which will be drawn. + * @param x + * the X coordinate of the image top left corner. + * @param y + * the Y coordinate of the image top left corner. + * @param width + * the width of rectangle which scales the image. + * @param height + * the height of rectangle which scales the image. + * @param observer + * the ImageObserver object which should be notified about image + * loading process. + * @return true, if loading image is successful or image is null, otherwise + * false. + */ + public abstract boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer); + + /** + * Scales the specified area of the specified image to fit in the rectangle + * area defined by its corners coordinates and draws the sub-image with the + * specified background color. The sub-image to be drawn is defined by its + * top left corner coordinates (sx1, sy1) and bottom right corner + * coordinates (sx2, sy2) computed with respect to the origin (top left + * corner) of the source image. The non opaque pixels will be drawn in the + * background color. The image loading process notifies specified Image + * Observer. This method returns true if the image has loaded, otherwise it + * returns false. + * + * @param img + * the image which will be drawn. + * @param dx1 + * the X top left corner coordinate of the destination rectangle + * area. + * @param dy1 + * the Y top left corner coordinate of the destination rectangle + * area. + * @param dx2 + * the X bottom right corner coordinate of the destination + * rectangle area. + * @param dy2 + * the Y bottom right corner coordinate of the destination + * rectangle area. + * @param sx1 + * the X top left corner coordinate of the area to be drawn + * within the source image. + * @param sy1 + * the Y top left corner coordinate of the area to be drawn + * within the source image. + * @param sx2 + * the X bottom right corner coordinate of the area to be drawn + * within the source image. + * @param sy2 + * the Y bottom right corner coordinate of the area to be drawn + * within the source image. + * @param bgcolor + * the background color. + * @param observer + * the ImageObserver object which should be notified about image + * loading process. + * @return true, if loading image is successful or image is null, false + * otherwise. + */ + public abstract boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, + int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer); + + /** + * Scales the specified area of the specified image to fit in the rectangle + * area defined by its corners coordinates and draws the sub-image. The + * sub-image to be drawn is defined by its top left corner coordinates (sx1, + * sy1) and bottom right corner coordinates (sx2, sy2) computed with respect + * to the origin (top left corner) of the source image. The image loading + * process notifies specified Image Observer. This method returns true if + * the image has loaded, otherwise it returns false. + * + * @param img + * the image which will be drawn. + * @param dx1 + * the X top left corner coordinate of the destination rectangle + * area. + * @param dy1 + * the Y top left corner coordinate of the destination rectangle + * area. + * @param dx2 + * the X bottom right corner coordinate of the destination + * rectangle area. + * @param dy2 + * the Y bottom right corner coordinate of the destination + * rectangle area. + * @param sx1 + * the X top left corner coordinate of the area to be drawn + * within the source image. + * @param sy1 + * the Y top left corner coordinate of the area to be drawn + * within the source image. + * @param sx2 + * the X bottom right corner coordinate of the area to be drawn + * within the source image. + * @param sy2 + * the Y bottom right corner coordinate of the area to be drawn + * within the source image. + * @param observer + * the ImageObserver object which should be notified about image + * loading process. + * @return true, if loading image is successful or image is null, false + * otherwise. + */ + public abstract boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, + int sy1, int sx2, int sy2, ImageObserver observer); + + /** + * Draws a line from the point (x1, y1) to the point (x2, y2). This method + * draws the line with current color which can be changed by setColor(Color + * c) method. + * + * @param x1 + * the X coordinate of the first point. + * @param y1 + * the Y coordinate of the first point. + * @param x2 + * the X coordinate of the second point. + * @param y2 + * the Y coordinate of the second point. + */ + public abstract void drawLine(int x1, int y1, int x2, int y2); + + /** + * Draws the outline of an oval to fit in the rectangle defined by the given + * width, height, and top left corner. + * + * @param x + * the X top left corner oval coordinate. + * @param y + * the Y top left corner oval coordinate. + * @param width + * the oval width. + * @param height + * the oval height. + */ + public abstract void drawOval(int x, int y, int width, int height); + + /** + * Draws the outline of a polygon. The polygon vertices are defined by + * points with xpoints[i], ypoints[i] as coordinates. The polygon edges are + * the lines from the points with (xpoints[i-1], ypoints[i-1]) coordinates + * to the points with (xpoints[i], ypoints[i]) coordinates, for 0 < i < + * npoints +1. + * + * @param xpoints + * the array of X coordinates of the polygon vertices. + * @param ypoints + * the array of Y coordinates of the polygon vertices. + * @param npoints + * the number of polygon vertices/points. + */ + public abstract void drawPolygon(int[] xpoints, int[] ypoints, int npoints); + + /** + * Draws a set of connected lines which are defined by the x and y + * coordinate arrays. The polyline is closed if coordinates of the first + * point are the same as coordinates of the last point. + * + * @param xpoints + * the array of X point coordinates. + * @param ypoints + * the array of Y point coordinates. + * @param npoints + * the number of points. + */ + public abstract void drawPolyline(int[] xpoints, int[] ypoints, int npoints); + + /** + * Draws the outline of a rectangle with round corners. + * + * @param x + * the X coordinate of the rectangle's top left corner. + * @param y + * the Y coordinate of the rectangle's top left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + * @param arcWidth + * the arc width for the corners. + * @param arcHeight + * the arc height for the corners. + */ + public abstract void drawRoundRect(int x, int y, int width, int height, int arcWidth, + int arcHeight); + + /** + * Draws a text defined by an iterator. The iterator should specify the font + * for every character. + * + * @param iterator + * the iterator. + * @param x + * the X coordinate of the first character. + * @param y + * the Y coordinate of the first character. + */ + public abstract void drawString(AttributedCharacterIterator iterator, int x, int y); + + /** + * Draws a text defined by a string. This method draws the text with current + * font and color. + * + * @param str + * the string. + * @param x + * the X coordinate of the first character. + * @param y + * the Y coordinate of the first character. + */ + public abstract void drawString(String str, int x, int y); + + /** + * Fills the arc covering the rectangle and using the current color. The + * rectangle is defined by the origin point (X, Y) and dimensions (width and + * height). The arc center is the the center of specified rectangle. The + * angle origin is at the 3 o'clock position, and a positive angle gives + * counter-clockwise rotation, a negative angle gives clockwise rotation. + * + * @param x + * the X origin coordinate of the rectangle which scales the arc. + * @param y + * the Y origin coordinate of the rectangle which scales the arc. + * @param width + * the width of the rectangle which scales the arc. + * @param height + * the height of the rectangle which scales the arc. + * @param sa + * start angle - the origin angle of arc. + * @param ea + * arc angle - the angular arc value relative to the start angle. + */ + public abstract void fillArc(int x, int y, int width, int height, int sa, int ea); + + /** + * Fills an oval with the current color where the oval is defined by the + * bounding rectangle with the given width, height, and top left corner. + * + * @param x + * the X top left corner oval coordinate. + * @param y + * the Y top left corner oval coordinate. + * @param width + * the oval width. + * @param height + * the oval height. + */ + public abstract void fillOval(int x, int y, int width, int height); + + /** + * Fills a polygon with the current color. The polygon vertices are defined + * by the points with xpoints[i], ypoints[i] as coordinates. The polygon + * edges are the lines from the points with (xpoints[i-1], ypoints[i-1]) + * coordinates to the points with (xpoints[i], ypoints[i]) coordinates, for + * 0 < i < npoints +1. + * + * @param xpoints + * the array of X coordinates of the polygon vertices. + * @param ypoints + * the array of Y coordinates of the polygon vertices. + * @param npoints + * the number of polygon vertices/points. + */ + public abstract void fillPolygon(int[] xpoints, int[] ypoints, int npoints); + + /** + * Fills a rectangle with the current color. The rectangle is defined by its + * width and length and top left corner coordinates. + * + * @param x + * the X coordinate of the rectangle's top left corner. + * @param y + * the Y coordinate of the rectangle's top left corner. + * @param width + * the width of rectangle. + * @param height + * the height of rectangle. + */ + public abstract void fillRect(int x, int y, int width, int height); + + /** + * Fills a round cornered rectangle with the current color. + * + * @param x + * the X coordinate of the top left corner of the bounding + * rectangle. + * @param y + * the Y coordinate of the top left corner of the bounding + * rectangle. + * @param width + * the width of the bounding rectangle. + * @param height + * the height of the bounding rectangle. + * @param arcWidth + * the arc width at the corners. + * @param arcHeight + * the arc height at the corners. + */ + public abstract void fillRoundRect(int x, int y, int width, int height, int arcWidth, + int arcHeight); + + /** + * Gets the clipping area.
+ *
+ * + * @return a Shape object of the clipping area or null if it is not set. + */ + public abstract Shape getClip(); + + /** + * Gets the bounds of the current clipping area as a rectangle. + * + * @return a Rectangle object which represents the bounds of the current + * clipping area. + */ + public abstract Rectangle getClipBounds(); + + /** + * Gets the current color of Graphics. + * + * @return the current color. + */ + public abstract Color getColor(); + + /** + * Gets the current font of Graphics. + * + * @return the current font. + */ + public abstract Font getFont(); + + /** + * Gets the font metrics of the specified font. The font metrics object + * contains information about the rendering of a particular font. + * + * @param font + * the specified font. + * @return the font metrics for the specified font. + */ + public abstract FontMetrics getFontMetrics(Font font); + + /** + * Sets the new clipping area specified by rectangle. The new clipping area + * doesn't depend on the window's visibility. Rendering operations can't be + * performed outside new clipping area. + * + * @param x + * the X coordinate of the new clipping rectangle. + * @param y + * the Y coordinate of the new clipping rectangle. + * @param width + * the width of the new clipping rectangle. + * @param height + * the height of the new clipping rectangle. + */ + public abstract void setClip(int x, int y, int width, int height); + + /** + * Sets the new clipping area to be the area specified by Shape object. The + * new clipping area doesn't depend on the window's visibility. Rendering + * operations can't be performed outside new clipping area. + * + * @param clip + * the Shape object which represents new clipping area. + */ + public abstract void setClip(Shape clip); + + /** + * Sets the current Graphics color. All rendering operations with this + * Graphics will use this color. + * + * @param c + * the new color. + */ + public abstract void setColor(Color c); + + /** + * Sets the current Graphics font. All rendering operations with this + * Graphics will use this font. + * + * @param font + * the new font. + */ + public abstract void setFont(Font font); + + /** + * Sets the paint mode for the Graphics which overwrites all rendering + * operations with the current color. + */ + public abstract void setPaintMode(); + + /** + * Sets the XOR mode for the Graphics which changes a pixel from the current + * color to the specified XOR color.
+ *
+ * + * @param color + * the new XOR mode. + */ + public abstract void setXORMode(Color color); + + /** + * Translates the origin of Graphics current coordinate system to the point + * with X, Y coordinates in the current coordinate system. All rendering + * operation in this Graphics will be related to the new origin. + * + * @param x + * the X coordinate of the origin. + * @param y + * the Y coordinate of the origin. + */ + public abstract void translate(int x, int y); +} diff --git a/awt/java/awt/Graphics2D.java b/awt/java/awt/Graphics2D.java new file mode 100644 index 000000000..04a7319de --- /dev/null +++ b/awt/java/awt/Graphics2D.java @@ -0,0 +1,513 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.awt; + +import java.awt.font.GlyphVector; +import java.awt.font.FontRenderContext; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +/** + * The Graphics2D class extends Graphics class and provides more capabilities + * for rendering text, images, shapes. This provides methods to perform + * transformation of coordinate system, color management, and text layout. The + * following attributes exist for rendering: + *

    + *
  • Color - current Graphics2D color;
  • + *
  • Font - current Graphics2D font;
  • + *
  • Stroke - pen with a width of 1 pixel;
  • + *
  • Transform - current Graphics2D Transformation;
  • + *
  • Composite - alpha compositing rules for combining source and destination + * colors.
  • + *
+ * + * @since Android 1.0 + */ +public abstract class Graphics2D extends Graphics { + + /** + * Instantiates a new Graphics2D object. This constructor should never be + * called directly. + */ + protected Graphics2D() { + super(); + } + + /** + * Adds preferences for the rendering algorithms. The preferences are + * arbitrary and specified by Map objects. All specified by Map object + * preferences can be modified. + * + * @param hints + * the rendering hints. + */ + public abstract void addRenderingHints(Map hints); + + /** + * Intersects the current clipping area with the specified Shape and the + * result becomes a new clipping area. If current clipping area is not + * defined, the Shape becomes the new clipping area. No rendering operations + * are allowed outside the clipping area. + * + * @param s + * the specified Shape object which will be intersected with + * current clipping area. + */ + public abstract void clip(Shape s); + + /** + * Draws the outline of the specified Shape. + * + * @param s + * the Shape which outline is drawn. + */ + public abstract void draw(Shape s); + + /** + * Draws the specified GlyphVector object's text at the point x, y. + * + * @param g + * the GlyphVector object to be drawn. + * @param x + * the X position where the GlyphVector's text should be + * rendered. + * @param y + * the Y position where the GlyphVector's text should be + * rendered. + */ + public abstract void drawGlyphVector(GlyphVector g, float x, float y); + + /** + * Draws the BufferedImage -- modified according to the operation + * BufferedImageOp -- at the point x, y. + * + * @param img + * the BufferedImage to be rendered. + * @param op + * the filter to be applied to the image before rendering. + * @param x + * the X coordinate of the point where the image's upper left + * corner will be placed. + * @param y + * the Y coordinate of the point where the image's upper left + * corner will be placed. + */ + public abstract void drawImage(BufferedImage img, BufferedImageOp op, int x, int y); + + /** + * Draws BufferedImage transformed from image space into user space + * according to the AffineTransform xform and notifies the ImageObserver. + * + * @param img + * the BufferedImage to be rendered. + * @param xform + * the affine transformation from the image to the user space. + * @param obs + * the ImageObserver to be notified about the image conversion. + * @return true, if the image is successfully loaded and rendered, or it's + * null, otherwise false. + */ + public abstract boolean drawImage(Image img, AffineTransform xform, ImageObserver obs); + + /** + * Draws a RenderableImage which is transformed from image space into user + * according to the AffineTransform xform. + * + * @param img + * the RenderableImage to be rendered. + * @param xform + * the affine transformation from image to user space. + */ + public abstract void drawRenderableImage(RenderableImage img, AffineTransform xform); + + /** + * Draws a RenderedImage which is transformed from image space into user + * according to the AffineTransform xform. + * + * @param img + * the RenderedImage to be rendered. + * @param xform + * the affine transformation from image to user space. + */ + public abstract void drawRenderedImage(RenderedImage img, AffineTransform xform); + + /** + * Draws the string specified by the AttributedCharacterIterator. The first + * character's position is specified by the X, Y parameters. + * + * @param iterator + * whose text is drawn. + * @param x + * the X position where the first character is drawn. + * @param y + * the Y position where the first character is drawn. + */ + public abstract void drawString(AttributedCharacterIterator iterator, float x, float y); + + /** + * Draws the string specified by the AttributedCharacterIterator. The first + * character's position is specified by the X, Y parameters. + * + * @param iterator + * whose text is drawn. + * @param x + * the X position where the first character is drawn. + * @param y + * the Y position where the first character is drawn. + * @see java.awt.Graphics#drawString(AttributedCharacterIterator, int, int) + */ + @Override + public abstract void drawString(AttributedCharacterIterator iterator, int x, int y); + + /** + * Draws the String whose the first character position is specified by the + * parameters X, Y. + * + * @param s + * the String to be drawn. + * @param x + * the X position of the first character. + * @param y + * the Y position of the first character. + */ + public abstract void drawString(String s, float x, float y); + + /** + * Draws the String whose the first character coordinates are specified by + * the parameters X, Y. + * + * @param str + * the String to be drawn. + * @param x + * the X coordinate of the first character. + * @param y + * the Y coordinate of the first character. + * @see java.awt.Graphics#drawString(String, int, int) + */ + @Override + public abstract void drawString(String str, int x, int y); + + /** + * Fills the interior of the specified Shape. + * + * @param s + * the Shape to be filled. + */ + public abstract void fill(Shape s); + + /** + * Gets the background color. + * + * @return the current background color. + */ + public abstract Color getBackground(); + + /** + * Gets the current composite of the Graphics2D. + * + * @return the current composite which specifies the compositing style. + */ + public abstract Composite getComposite(); + + /** + * Gets the device configuration. + * + * @return the device configuration. + */ + public abstract GraphicsConfiguration getDeviceConfiguration(); + + /** + * Gets the rendering context of the Font. + * + * @return the FontRenderContext. + */ + public abstract FontRenderContext getFontRenderContext(); + + /** + * Gets the current Paint of Graphics2D. + * + * @return the current Paint of Graphics2D. + */ + public abstract Paint getPaint(); + + /** + * Gets the value of single preference for specified key. + * + * @param key + * the specified key of the rendering hint. + * @return the value of rendering hint for specified key. + */ + public abstract Object getRenderingHint(RenderingHints.Key key); + + /** + * Gets the set of the rendering preferences as a collection of key/value + * pairs. + * + * @return the RenderingHints which contains the rendering preferences. + */ + public abstract RenderingHints getRenderingHints(); + + /** + * Gets current stroke of the Graphics2D. + * + * @return current stroke of the Graphics2D. + */ + public abstract Stroke getStroke(); + + /** + * Gets current affine transform of the Graphics2D. + * + * @return current AffineTransform of the Graphics2D. + */ + public abstract AffineTransform getTransform(); + + /** + * Determines whether or not the specified Shape intersects the specified + * Rectangle. If the onStroke parameter is true, this method checks whether + * or not the specified Shape outline intersects the specified Rectangle, + * otherwise this method checks whether or not the specified Shape's + * interior intersects the specified Rectangle. + * + * @param rect + * the specified Rectangle. + * @param s + * the Shape to check for intersection. + * @param onStroke + * the parameter determines whether or not this method checks for + * intersection of the Shape outline or of the Shape interior + * with the Rectangle. + * @return true, if there is a hit, false otherwise. + */ + public abstract boolean hit(Rectangle rect, Shape s, boolean onStroke); + + /** + * Performs a rotation transform relative to current Graphics2D Transform. + * The coordinate system is rotated by the specified angle in radians + * relative to current origin. + * + * @param theta + * the angle of rotation in radians. + */ + public abstract void rotate(double theta); + + /** + * Performs a translated rotation transform relative to current Graphics2D + * Transform. The coordinate system is rotated by the specified angle in + * radians relative to current origin and then moved to point (x, y). Is + * this right? + * + * @param theta + * the angle of rotation in radians. + * @param x + * the X coordinate. + * @param y + * the Y coordinate. + */ + public abstract void rotate(double theta, double x, double y); + + /** + * Performs a linear scale transform relative to current Graphics2D + * Transform. The coordinate system is rescaled vertically and horizontally + * by the specified parameters. + * + * @param sx + * the scaling factor by which the X coordinate is multiplied. + * @param sy + * the scaling factor by which the Y coordinate is multiplied. + */ + public abstract void scale(double sx, double sy); + + /** + * Sets a new background color for clearing rectangular areas. The clearRect + * method uses the current background color. + * + * @param color + * the new background color. + */ + public abstract void setBackground(Color color); + + /** + * Sets the current composite for Graphics2D. + * + * @param comp + * the Composite object. + */ + public abstract void setComposite(Composite comp); + + /** + * Sets the paint for Graphics2D. + * + * @param paint + * the Paint object. + */ + public abstract void setPaint(Paint paint); + + /** + * Sets a key-value pair in the current RenderingHints map. + * + * @param key + * the key of the rendering hint to set. + * @param value + * the value to set for the rendering hint. + */ + public abstract void setRenderingHint(RenderingHints.Key key, Object value); + + /** + * Replaces the current rendering hints with the specified rendering + * preferences. + * + * @param hints + * the new Map of rendering hints. + */ + public abstract void setRenderingHints(Map hints); + + /** + * Sets the stroke for the Graphics2D. + * + * @param s + * the Stroke object. + */ + public abstract void setStroke(Stroke s); + + /** + * Overwrite the current Transform of the Graphics2D. The specified + * Transform should be received from the getTransform() method and should be + * used only for restoring the original Graphics2D transform after calling + * draw or fill methods. + * + * @param Tx + * the specified Transform. + */ + public abstract void setTransform(AffineTransform Tx); + + /** + * Performs a shear transform relative to current Graphics2D Transform. The + * coordinate system is shifted by the specified multipliers relative to + * current position. + * + * @param shx + * the multiplier by which the X coordinates shift position along + * X axis as a function of Y coordinates. + * @param shy + * the multiplier by which the Y coordinates shift position along + * Y axis as a function of X coordinates. + */ + public abstract void shear(double shx, double shy); + + /** + * Concatenates the AffineTransform object with current Transform of this + * Graphics2D. The transforms are applied in reverse order with the last + * specified transform applied first and the next transformation applied to + * the result of previous transformation. More precisely, if Cx is the + * current Graphics2D transform, the transform method's result with Tx as + * the parameter is the transformation Rx, where Rx(p) = Cx(Tx(p)), for p - + * a point in current coordinate system. Rx becomes the current Transform + * for this Graphics2D. + * + * @param Tx + * the AffineTransform object to be concatenated with current + * Transform. + */ + public abstract void transform(AffineTransform Tx); + + /** + * Performs a translate transform relative to current Graphics2D Transform. + * The coordinate system is moved by the specified distance relative to + * current position. + * + * @param tx + * the translation distance along the X axis. + * @param ty + * the translation distance along the Y axis. + */ + public abstract void translate(double tx, double ty); + + /** + * Moves the origin Graphics2D Transform to the point with x, y coordinates + * in current coordinate system. The new origin of coordinate system is + * moved to the (x, y) point accordingly. All rendering and transform + * operations are performed relative to this new origin. + * + * @param x + * the X coordinate. + * @param y + * the Y coordinate. + * @see java.awt.Graphics#translate(int, int) + */ + @Override + public abstract void translate(int x, int y); + + /** + * Fills a 3D rectangle with the current color. The rectangle is specified + * by its width, height, and top left corner coordinates. + * + * @param x + * the X coordinate of the rectangle's top left corner. + * @param y + * the Y coordinate of the rectangle's top left corner. + * @param width + * the width of rectangle. + * @param height + * the height of rectangle. + * @param raised + * a boolean value that determines whether the rectangle is drawn + * as raised or indented. + * @see java.awt.Graphics#fill3DRect(int, int, int, int, boolean) + */ + @Override + public void fill3DRect(int x, int y, int width, int height, boolean raised) { + // According to the spec, color should be used instead of paint, + // so Graphics.fill3DRect resets paint and + // it should be restored after the call + Paint savedPaint = getPaint(); + super.fill3DRect(x, y, width, height, raised); + setPaint(savedPaint); + } + + /** + * Draws the highlighted outline of a rectangle. + * + * @param x + * the X coordinate of the rectangle's top left corner. + * @param y + * the Y coordinate of the rectangle's top left corner. + * @param width + * the width of rectangle. + * @param height + * the height of rectangle. + * @param raised + * a boolean value that determines whether the rectangle is drawn + * as raised or indented. + * @see java.awt.Graphics#draw3DRect(int, int, int, int, boolean) + */ + @Override + public void draw3DRect(int x, int y, int width, int height, boolean raised) { + // According to the spec, color should be used instead of paint, + // so Graphics.draw3DRect resets paint and + // it should be restored after the call + Paint savedPaint = getPaint(); + super.draw3DRect(x, y, width, height, raised); + setPaint(savedPaint); + } +} \ No newline at end of file diff --git a/awt/java/awt/GraphicsConfiguration.java b/awt/java/awt/GraphicsConfiguration.java new file mode 100644 index 000000000..d59e896ab --- /dev/null +++ b/awt/java/awt/GraphicsConfiguration.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.VolatileImage; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The GraphicsConfiguration class contains the characteristics of graphics + * devices such as a printer or monitor, and represents device's capabilities + * and modes. Many GraphicsConfiguration objects can be associated with single + * graphics device. + * + * @since Android 1.0 + */ +public abstract class GraphicsConfiguration { + + /** + * Constructor could not be used directly and should be obtained in extended + * classes. + */ + protected GraphicsConfiguration() { + } + + /** + * Creates BufferedImage image object with a data layout and color model + * compatible with this GraphicsConfiguration with specified width and + * height parameters. + * + * @param width + * the width of BufferedImage. + * @param height + * the height of BufferedImage. + * @return the BufferedImage object with specified width and height + * parameters. + */ + public abstract BufferedImage createCompatibleImage(int width, int height); + + /** + * Creates a BufferedImage that has the specified width, height, + * transparency and has a data layout and color model compatible with this + * GraphicsConfiguration. + * + * @param width + * the width of image. + * @param height + * the height of image. + * @param transparency + * the transparency mode. + * @return the BufferedImage object. + */ + public abstract BufferedImage createCompatibleImage(int width, int height, int transparency); + + /** + * Creates a VolatileImage that has the specified width and height and has a + * data layout and color model compatible with this GraphicsConfiguration. + * + * @param width + * the width of image. + * @param height + * the height of image. + * @return the VolatileImage object. + */ + public abstract VolatileImage createCompatibleVolatileImage(int width, int height); + + /** + * Creates a VolatileImage that supports the specified width, height, + * transparency and has a data layout and color model compatible with this + * GraphicsConfiguration. + * + * @param width + * the width of image. + * @param height + * the height of image. + * @param transparency + * the transparency mode. + * @return the VolatileImage object. + */ + public abstract VolatileImage createCompatibleVolatileImage(int width, int height, + int transparency); + + /** + * Gets the bounds of area covered by the GraphicsConfiguration in the + * device coordinates space. + * + * @return the Rectangle of GraphicsConfiguration's bounds. + */ + public abstract Rectangle getBounds(); + + /** + * Gets the ColorModel of the GraphicsConfiguration. + * + * @return the ColorModel object of the GraphicsConfiguration. + */ + public abstract ColorModel getColorModel(); + + /** + * Gets the ColorModel of the GraphicsConfiguration which supports specified + * Transparency. + * + * @param transparency + * the Transparency mode: OPAQUE, BITMASK, or TRANSLUCENT. + * @return the ColorModel of the GraphicsConfiguration which supports + * specified Transparency. + */ + public abstract ColorModel getColorModel(int transparency); + + /** + * Gets the default AffineTransform of the GraphicsConfiguration. This + * method translates user coordinates to device coordinates. + * + * @return the default AffineTransform of the GraphicsConfiguration. + */ + public abstract AffineTransform getDefaultTransform(); + + /** + * Gets the GraphicsDevice of the GraphicsConfiguration. + * + * @return the GraphicsDevice of the GraphicsConfiguration. + */ + public abstract GraphicsDevice getDevice(); + + /** + * Gets the normalizing AffineTransform of the GraphicsConfiguration. + * + * @return the normalizing AffineTransform of the GraphicsConfiguration. + */ + public abstract AffineTransform getNormalizingTransform(); + + /** + * Creates VolatileImage with specified width, height, ImageCapabilities; a + * data layout and color model compatible with this GraphicsConfiguration. + * + * @param width + * the width of image. + * @param height + * the height of image. + * @param caps + * the ImageCapabilities object. + * @return the VolatileImage which data layout and color model compatible + * with this GraphicsConfiguration. + * @throws AWTException + * if ImageCapabilities is not supported by the + * GraphicsConfiguration. + */ + public VolatileImage createCompatibleVolatileImage(int width, int height, ImageCapabilities caps) + throws AWTException { + VolatileImage res = createCompatibleVolatileImage(width, height); + if (!res.getCapabilities().equals(caps)) { + // awt.14A=Can not create VolatileImage with specified capabilities + throw new AWTException(Messages.getString("awt.14A")); //$NON-NLS-1$ + } + return res; + } + + /** + * Creates a VolatileImage with specified width, height, transparency and + * ImageCapabilities; a data layout and color model compatible with this + * GraphicsConfiguration. + * + * @param width + * the width of image. + * @param height + * the height of image. + * @param caps + * the ImageCapabilities object. + * @param transparency + * the Transparency mode: OPAQUE, BITMASK, or TRANSLUCENT. + * @return the VolatileImage which data layout and color model compatible + * with this GraphicsConfiguration. + * @throws AWTException + * if ImageCapabilities is not supported by the + * GraphicsConfiguration. + */ + public VolatileImage createCompatibleVolatileImage(int width, int height, + ImageCapabilities caps, int transparency) throws AWTException { + VolatileImage res = createCompatibleVolatileImage(width, height, transparency); + if (!res.getCapabilities().equals(caps)) { + // awt.14A=Can not create VolatileImage with specified capabilities + throw new AWTException(Messages.getString("awt.14A")); //$NON-NLS-1$ + } + return res; + } + + /** + * Gets the buffering capabilities of the GraphicsConfiguration. + * + * @return the BufferCapabilities object. + */ + public BufferCapabilities getBufferCapabilities() { + return new BufferCapabilities(new ImageCapabilities(false), new ImageCapabilities(false), + BufferCapabilities.FlipContents.UNDEFINED); + } + + /** + * Gets the image capabilities of the GraphicsConfiguration. + * + * @return the ImageCapabilities object. + */ + public ImageCapabilities getImageCapabilities() { + return new ImageCapabilities(false); + } +} diff --git a/awt/java/awt/GraphicsDevice.java b/awt/java/awt/GraphicsDevice.java new file mode 100644 index 000000000..9eda4e0ed --- /dev/null +++ b/awt/java/awt/GraphicsDevice.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The GraphicsDevice class describes the graphics devices (such as screens or + * printers) which are available in a particular graphics environment. Many + * GraphicsDevice instances can be associated with a single GraphicsEnvironment. + * Each GraphicsDevice has one or more GraphicsConfiguration objects which + * specify the different configurations and modes of GraphicsDevice. + * + * @since Android 1.0 + */ +public abstract class GraphicsDevice { + + /** + * The display mode. + */ + private DisplayMode displayMode; + + // ???AWT + // private Window fullScreenWindow = null; + + /** + * The Constant TYPE_IMAGE_BUFFER indicates a image buffer device. + */ + + public static final int TYPE_IMAGE_BUFFER = 2; + + /** + * The Constant TYPE_PRINTER indicates a printer device. + */ + public static final int TYPE_PRINTER = 1; + + /** + * The Constant TYPE_RASTER_SCREEN indicates a raster screen device. + */ + public static final int TYPE_RASTER_SCREEN = 0; + + /** + * Constructor is not to be used directly as this class is abstract. + */ + protected GraphicsDevice() { + displayMode = new DisplayMode(0, 0, DisplayMode.BIT_DEPTH_MULTI, + DisplayMode.REFRESH_RATE_UNKNOWN); + } + + /** + * Returns an array of GraphicsConfiguration objects associated with the + * GraphicsDevice. + * + * @return an array of GraphicsConfiguration objects associated with the + * GraphicsDevice. + */ + public abstract GraphicsConfiguration[] getConfigurations(); + + /** + * Gets the default configuration for the GraphicsDevice. + * + * @return the default GraphicsConfiguration object for the GraphicsDevice. + */ + public abstract GraphicsConfiguration getDefaultConfiguration(); + + /** + * Gets the String identifier which associated with the GraphicsDevice in + * the GraphicsEnvironment. + * + * @return the String identifier of the GraphicsDevice in the + * GraphicsEnvironment. + */ + public abstract String getIDstring(); + + /** + * Gets the type of this GraphicsDevice: TYPE_IMAGE_BUFFER, TYPE_PRINTER or + * TYPE_RASTER_SCREEN. + * + * @return the type of this GraphicsDevice: TYPE_IMAGE_BUFFER, TYPE_PRINTER + * or TYPE_RASTER_SCREEN. + */ + public abstract int getType(); + + /** + * Returns the number of bytes available in accelerated memory on this + * device. + * + * @return the number of bytes available accelerated memory. + */ + public int getAvailableAcceleratedMemory() { + return 0; + } + + /* + * ???AWT public GraphicsConfiguration + * getBestConfiguration(GraphicsConfigTemplate gct) { return + * gct.getBestConfiguration(getConfigurations()); } + */ + + /** + * Gets the current display mode of the GraphicsDevice. + * + * @return the current display mode of the GraphicsDevice. + */ + public DisplayMode getDisplayMode() { + return displayMode; + } + + /** + * Gets an array of display modes available in this GraphicsDevice. + * + * @return an array of display modes available in this GraphicsDevice. + */ + public DisplayMode[] getDisplayModes() { + DisplayMode[] dms = { + displayMode + }; + return dms; + } + + /* + * ???AWT public Window getFullScreenWindow() { return fullScreenWindow; } + */ + + /** + * Returns true if this GraphicsDevice supports low-level display changes. + * + * @return true, if this GraphicsDevice supports low-level display changes; + * false otherwise. + */ + public boolean isDisplayChangeSupported() { + return false; + } + + /** + * Returns true if this GraphicsDevice supports full screen mode. + * + * @return true, if this GraphicsDevice supports full screen mode, false + * otherwise. + */ + public boolean isFullScreenSupported() { + return false; + } + + // an array of display modes available in this GraphicsDevice. + + /** + * Sets the display mode of this GraphicsDevice. + * + * @param dm + * the new display mode of this GraphicsDevice. + */ + public void setDisplayMode(DisplayMode dm) { + if (!isDisplayChangeSupported()) { + // awt.122=Does not support display mode changes + throw new UnsupportedOperationException(Messages.getString("awt.122")); //$NON-NLS-1$ + } + + DisplayMode[] dms = getDisplayModes(); + for (DisplayMode element : dms) { + if (element.equals(dm)) { + displayMode = dm; + return; + } + } + // awt.123=Unsupported display mode: {0} + throw new IllegalArgumentException(Messages.getString("awt.123", dm)); //$NON-NLS-1$ + } + + /* + * ???AWT public void setFullScreenWindow(Window w) { if (w == null) { + * fullScreenWindow = null; return; } fullScreenWindow = w; if + * (isFullScreenSupported()) { w.enableInputMethods(false); } else { + * w.setSize(displayMode.getWidth(), displayMode.getHeight()); + * w.setLocation(0, 0); } w.setVisible(true); w.setAlwaysOnTop(true); } + */ +} diff --git a/awt/java/awt/GraphicsEnvironment.java b/awt/java/awt/GraphicsEnvironment.java new file mode 100644 index 000000000..d527417f7 --- /dev/null +++ b/awt/java/awt/GraphicsEnvironment.java @@ -0,0 +1,212 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package java.awt; + +import java.awt.image.BufferedImage; +import java.util.Locale; + +import org.apache.harmony.awt.ContextStorage; +import org.apache.harmony.awt.gl.CommonGraphics2DFactory; + +/** + * The GraphicsEnvironment class defines a collection of GraphicsDevice objects + * and Font objects which are available for Java application on current + * platform. + * + * @since Android 1.0 + */ +public abstract class GraphicsEnvironment { + + /** + * Constructor could not be used directly and should be obtained in extended + * classes. + */ + protected GraphicsEnvironment() { + } + + /** + * Gets the local GraphicsEnvironment. + * + * @return the local GraphicsEnvironment. + */ + public static GraphicsEnvironment getLocalGraphicsEnvironment() { + synchronized (ContextStorage.getContextLock()) { + if (ContextStorage.getGraphicsEnvironment() == null) { + if (isHeadless()) { + ContextStorage.setGraphicsEnvironment(new HeadlessGraphicsEnvironment()); + } else { + CommonGraphics2DFactory g2df = (CommonGraphics2DFactory)Toolkit + .getDefaultToolkit().getGraphicsFactory(); + + ContextStorage.setGraphicsEnvironment(g2df + .createGraphicsEnvironment(ContextStorage.getWindowFactory())); + } + } + + return ContextStorage.getGraphicsEnvironment(); + } + } + + /** + * Returns whether or not a display, keyboard, and mouse are supported in + * this graphics environment. + * + * @return true, if HeadlessException will be thrown from areas of the + * graphics environment that are dependent on a display, keyboard, + * or mouse, false otherwise. + */ + public boolean isHeadlessInstance() { + return false; + } + + /** + * Checks whether or not a display, keyboard, and mouse are supported in + * this environment. + * + * @return true, if a HeadlessException is thrown from areas of the Toolkit + * and GraphicsEnvironment that are dependent on a display, + * keyboard, or mouse, false otherwise. + */ + public static boolean isHeadless() { + return "true".equals(System.getProperty("java.awt.headless")); + } + + /** + * Gets the maximum bounds of system centered windows. + * + * @return the maximum bounds of system centered windows. + * @throws HeadlessException + * if isHeadless() method returns true. + */ + public Rectangle getMaximumWindowBounds() throws HeadlessException { + return getDefaultScreenDevice().getDefaultConfiguration().getBounds(); + } + + /** + * Gets the Point which should defines the center of system window. + * + * @return the Point where the system window should be centered. + * @throws HeadlessException + * if isHeadless() method returns true. + */ + public Point getCenterPoint() throws HeadlessException { + Rectangle mwb = getMaximumWindowBounds(); + return new Point(mwb.width >> 1, mwb.height >> 1); + } + + /** + * Indicates that the primary font should be used. Primary font is specified + * by initial system locale or default encoding). + */ + public void preferLocaleFonts() { + // Note: API specification says following: + // "The actual change in font rendering behavior resulting + // from a call to this method is implementation dependent; + // it may have no effect at all." So, doing nothing is an + // acceptable behavior for this method. + + // For now FontManager uses 1.4 font.properties scheme for font mapping, + // so + // this method doesn't make any sense. The implementation of this method + // which will influence font mapping is postponed until + // 1.5 mapping scheme not implemented. + + // todo - Implement non-default behavior with 1.5 font mapping scheme + } + + /** + * Indicates that a proportional preference of the font should be used. + */ + public void preferProportionalFonts() { + // Note: API specification says following: + // "The actual change in font rendering behavior resulting + // from a call to this method is implementation dependent; + // it may have no effect at all." So, doing nothing is an + // acceptable behavior for this method. + + // For now FontManager uses 1.4 font.properties scheme for font mapping, + // so + // this method doesn't make any sense. The implementation of this method + // which will influence font mapping is postponed until + // 1.5 mapping scheme not implemented. + + // todo - Implement non-default behavior with 1.5 font mapping scheme + } + + /** + * Creates the Graphics2D object for rendering to the specified + * BufferedImage. + * + * @param bufferedImage + * the BufferedImage object. + * @return the Graphics2D object which allows to render to the specified + * BufferedImage. + */ + public abstract Graphics2D createGraphics(BufferedImage bufferedImage); + + /** + * Gets the array of all available fonts instances in this + * GraphicsEnviroments. + * + * @return the array of all available fonts instances in this + * GraphicsEnviroments. + */ + public abstract Font[] getAllFonts(); + + /** + * Gets the array of all available font family names. + * + * @return the array of all available font family names. + */ + public abstract String[] getAvailableFontFamilyNames(); + + /** + * Gets the array of all available font family names for the specified + * locale. + * + * @param locale + * the Locale object which represents geographical region. The + * default locale is used if locale is null. + * @return the array of available font family names for the specified + * locale. + */ + public abstract String[] getAvailableFontFamilyNames(Locale locale); + + /** + * Gets the default screen device as GraphicDevice object. + * + * @return the GraphicDevice object which represents default screen device. + * @throws HeadlessException + * if isHeadless() returns true. + */ + public abstract GraphicsDevice getDefaultScreenDevice() throws HeadlessException; + + /** + * Gets an array of all available screen devices. + * + * @return the array of GraphicsDevice objects which represents all + * available screen devices. + * @throws HeadlessException + * if isHeadless() returns true. + */ + public abstract GraphicsDevice[] getScreenDevices() throws HeadlessException; +} diff --git a/awt/java/awt/HeadlessException.java b/awt/java/awt/HeadlessException.java new file mode 100644 index 000000000..ec111f1e4 --- /dev/null +++ b/awt/java/awt/HeadlessException.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +/** + * The HeadlessException class provides notifications and error messages when + * code that is dependent on a keyboard, display, or mouse is called in an + * environment that does not support a keyboard, display, or mouse. + * + * @since Android 1.0 + */ +public class HeadlessException extends UnsupportedOperationException { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 167183644944358563L; + + /** + * Instantiates a new headless exception. + */ + public HeadlessException() { + super(); + } + + /** + * Instantiates a new headless exception with the specified message. + * + * @param msg + * the String which represents error message. + */ + public HeadlessException(String msg) { + super(msg); + } +} diff --git a/awt/java/awt/HeadlessGraphicsEnvironment.java b/awt/java/awt/HeadlessGraphicsEnvironment.java new file mode 100644 index 000000000..306393f33 --- /dev/null +++ b/awt/java/awt/HeadlessGraphicsEnvironment.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.awt; + +import java.awt.GraphicsDevice; +import java.awt.HeadlessException; + +import org.apache.harmony.awt.gl.CommonGraphicsEnvironment; + +/** + * The HeadlessGraphicsEnvironment class is the CommonGraphicsEnvironment + * implementation to use in the case where the environment lacks display, + * keyboard, and mouse support. + * + * @since Android 1.0 + */ +public class HeadlessGraphicsEnvironment extends CommonGraphicsEnvironment { + + /** + * Returns whether or not a display, keyboard, and mouse are supported in + * this graphics environment. + * + * @return true, if HeadlessException will be thrown from areas of the + * graphics environment that are dependent on a display, keyboard, + * or mouse, false otherwise. + */ + @Override + public boolean isHeadlessInstance() { + return true; + } + + /** + * Gets the default screen device as GraphicDevice object. + * + * @return the GraphicDevice object which represents default screen device. + * @throws HeadlessException + * if isHeadless() returns true. + */ + @Override + public GraphicsDevice getDefaultScreenDevice() throws HeadlessException { + throw new HeadlessException(); + } + + /** + * Gets an array of all available screen devices. + * + * @return the array of GraphicsDevice objects which represents all + * available screen devices. + * @throws HeadlessException + * if isHeadless() returns true. + */ + @Override + public GraphicsDevice[] getScreenDevices() throws HeadlessException { + throw new HeadlessException(); + } + +} diff --git a/awt/java/awt/HeadlessToolkit.java b/awt/java/awt/HeadlessToolkit.java new file mode 100644 index 000000000..c64a85a61 --- /dev/null +++ b/awt/java/awt/HeadlessToolkit.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.awt; + +//???AWT +//import java.awt.datatransfer.Clipboard; +//import java.awt.dnd.DragGestureEvent; +//import java.awt.dnd.DragGestureListener; +//import java.awt.dnd.DragGestureRecognizer; +//import java.awt.dnd.DragSource; +//import java.awt.dnd.InvalidDnDOperationException; +//import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.im.InputMethodHighlight; +import java.awt.image.ColorModel; //import java.awt.peer.*; +//import java.beans.PropertyChangeSupport; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.apache.harmony.awt.ComponentInternals; //import org.apache.harmony.awt.datatransfer.DTK; +import org.apache.harmony.awt.wtk.GraphicsFactory; +import org.apache.harmony.awt.wtk.NativeEventQueue; +import org.apache.harmony.awt.wtk.WindowFactory; + +/** + * The HeadlessToolkit class is a subclass of ToolkitImpl to be used for + * graphical environments that lack keyboard and mouse capabilities. + * + * @since Android 1.0 + */ +public final class HeadlessToolkit extends ToolkitImpl { + + // ???AWT + /* + * @Override protected ButtonPeer createButton(Button a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected CheckboxPeer createCheckbox(Checkbox a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected CheckboxMenuItemPeer + * createCheckboxMenuItem(CheckboxMenuItem a0) throws HeadlessException { + * throw new HeadlessException(); } + * @Override protected ChoicePeer createChoice(Choice a0) throws + * HeadlessException { throw new HeadlessException(); } public Cursor + * createCustomCursor(Image img, Point hotSpot, String name) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected DialogPeer createDialog(Dialog a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override public T + * createDragGestureRecognizer( Class recognizerAbstractClass, DragSource + * ds, Component c, int srcActions, DragGestureListener dgl) { return null; + * } + * @Override public DragSourceContextPeer + * createDragSourceContextPeer(DragGestureEvent dge) throws + * InvalidDnDOperationException { throw new InvalidDnDOperationException(); + * } + * @Override protected FileDialogPeer createFileDialog(FileDialog a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected FramePeer createFrame(Frame a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected LabelPeer createLabel(Label a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected ListPeer createList(List a0) throws HeadlessException + * { throw new HeadlessException(); } + * @Override protected MenuPeer createMenu(Menu a0) throws HeadlessException + * { throw new HeadlessException(); } + * @Override protected MenuBarPeer createMenuBar(MenuBar a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected MenuItemPeer createMenuItem(MenuItem a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected PopupMenuPeer createPopupMenu(PopupMenu a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected ScrollbarPeer createScrollbar(Scrollbar a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected ScrollPanePeer createScrollPane(ScrollPane a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected TextAreaPeer createTextArea(TextArea a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected TextFieldPeer createTextField(TextField a0) throws + * HeadlessException { throw new HeadlessException(); } + * @Override protected WindowPeer createWindow(Window a0) throws + * HeadlessException { throw new HeadlessException(); } + */ + + @Override + public Dimension getBestCursorSize(int prefWidth, int prefHeight) throws HeadlessException { + throw new HeadlessException(); + } + + @Override + public ColorModel getColorModel() throws HeadlessException { + throw new HeadlessException(); + } + + @Override + public GraphicsFactory getGraphicsFactory() throws HeadlessException { + throw new HeadlessException(); + } + + @Override + public boolean getLockingKeyState(int keyCode) throws UnsupportedOperationException { + throw new HeadlessException(); + } + + @Override + public int getMaximumCursorColors() throws HeadlessException { + throw new HeadlessException(); + } + + @Override + public int getMenuShortcutKeyMask() throws HeadlessException { + throw new HeadlessException(); + } + + // ???AWT + /* + * @Override NativeEventQueue getNativeEventQueue() throws HeadlessException + * { throw new HeadlessException(); } + * @Override public PrintJob getPrintJob(Frame frame, String jobtitle, + * JobAttributes jobAttributes, PageAttributes pageAttributes) throws + * IllegalArgumentException { throw new IllegalArgumentException(); } + * @Override public PrintJob getPrintJob(Frame frame, String jobtitle, + * Properties props) throws NullPointerException { throw new + * NullPointerException(); } + */ + + @Override + public Insets getScreenInsets(GraphicsConfiguration gc) throws HeadlessException { + throw new HeadlessException(); + } + + @Override + public int getScreenResolution() throws HeadlessException { + throw new HeadlessException(); + } + + @Override + public Dimension getScreenSize() throws HeadlessException { + throw new HeadlessException(); + } + + // ???AWT + /* + * @Override public Clipboard getSystemClipboard() throws HeadlessException + * { throw new HeadlessException(); } + * @Override public Clipboard getSystemSelection() throws HeadlessException + * { throw new HeadlessException(); } + * @Override WindowFactory getWindowFactory() throws HeadlessException { + * throw new HeadlessException(); } + */ + + @Override + protected void init() { + lockAWT(); + try { + ComponentInternals.setComponentInternals(new ComponentInternalsImpl()); + // ???AWT: new EventQueue(this); // create the system EventQueue + // ???AWT: dispatcher = new Dispatcher(this); + desktopProperties = new HashMap(); + // ???AWT: desktopPropsSupport = new PropertyChangeSupport(this); + // ???AWT: awtEventsManager = new AWTEventsManager(); + // ???AWT: dispatchThread = new HeadlessEventDispatchThread(this, + // dispatcher); + // ???AWT: dtk = DTK.getDTK(); + dispatchThread.start(); + } finally { + unlockAWT(); + } + } + + @Override + public boolean isDynamicLayoutActive() throws HeadlessException { + throw new HeadlessException(); + } + + @Override + protected boolean isDynamicLayoutSet() throws HeadlessException { + throw new HeadlessException(); + } + + @Override + public boolean isFrameStateSupported(int state) throws HeadlessException { + throw new HeadlessException(); + } + + @Override + protected void loadSystemColors(int[] systemColors) throws HeadlessException { + throw new HeadlessException(); + } + + @Override + public Map mapInputMethodHighlight( + InputMethodHighlight highlight) throws HeadlessException { + throw new HeadlessException(); + } + + @Override + Map mapInputMethodHighlightImpl(InputMethodHighlight highlight) + throws HeadlessException { + throw new HeadlessException(); + } + + @Override + public void setDynamicLayout(boolean dynamic) throws HeadlessException { + throw new HeadlessException(); + } + + @Override + public void setLockingKeyState(int keyCode, boolean on) throws UnsupportedOperationException { + throw new HeadlessException(); + } +} diff --git a/awt/java/awt/IllegalComponentStateException.java b/awt/java/awt/IllegalComponentStateException.java new file mode 100644 index 000000000..bed172998 --- /dev/null +++ b/awt/java/awt/IllegalComponentStateException.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ + +package java.awt; + +/** + * The IllegalComponentStateException class is used to provide notification that + * AWT component is not in an appropriate state for the requested operation. + * + * @since Android 1.0 + */ +public class IllegalComponentStateException extends IllegalStateException { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -1889339587208144238L; + + /** + * Instantiates a new IllegalComponentStateException with the specified + * message. + * + * @param s + * the String message which describes the exception. + */ + public IllegalComponentStateException(String s) { + super(s); + } + + /** + * Instantiates a new IllegalComponentStateException without detailed + * message. + */ + public IllegalComponentStateException() { + } + +} diff --git a/awt/java/awt/Image.java b/awt/java/awt/Image.java new file mode 100644 index 000000000..7ae3ed883 --- /dev/null +++ b/awt/java/awt/Image.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt; + +import java.awt.image.AreaAveragingScaleFilter; +import java.awt.image.FilteredImageSource; +import java.awt.image.ImageFilter; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.ReplicateScaleFilter; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Image abstract class represents the graphic images. + * + * @since Android 1.0 + */ +public abstract class Image { + + /** + * The UndefinedProperty object should be returned if property is not + * defined for a particular image. + */ + public static final Object UndefinedProperty = new Object(); // $NON-LOCK-1$ + + /** + * The Constant SCALE_DEFAULT indicates the default image scaling algorithm. + */ + public static final int SCALE_DEFAULT = 1; + + /** + * The Constant SCALE_FAST indicates an image scaling algorithm which places + * a higher priority on scaling speed than on the image's smoothness. + */ + public static final int SCALE_FAST = 2; + + /** + * The Constant SCALE_SMOOTH indicates an image scaling algorithm which + * places a higher priority on image smoothness than on scaling speed. + */ + public static final int SCALE_SMOOTH = 4; + + /** + * The Constant SCALE_REPLICATE indicates the image scaling algorithm in the + * ReplicateScaleFilter class. + */ + public static final int SCALE_REPLICATE = 8; + + /** + * The Constant SCALE_AREA_AVERAGING indicates the area averaging image + * scaling algorithm. + */ + public static final int SCALE_AREA_AVERAGING = 16; + + /** + * The acceleration priority indicates image acceleration. + */ + protected float accelerationPriority = 0.5f; + + /** + * The Constant capabilities. + */ + private static final ImageCapabilities capabilities = new ImageCapabilities(false); + + /** + * Gets the image property with the specified name. The UndefinedProperty + * object should be return if the property is not specified for this image. + * The return value should be null if the property is currently unknown yet + * and the specified ImageObserver is to be notified later. + * + * @param name + * the name of image's property. + * @param observer + * the ImageObserver. + * @return the Object which represents value of the specified property. + */ + public abstract Object getProperty(String name, ImageObserver observer); + + /** + * Gets the ImageProducer object which represents data of this Image. + * + * @return the ImageProducer object which represents data of this Image. + */ + public abstract ImageProducer getSource(); + + /** + * Gets the width of this image. The specified ImageObserver object is + * notified when the width of this image is available. + * + * @param observer + * the ImageObserver object which is is notified when the width + * of this image is available. + * @return the width of image, or -1 if the width of this image is not + * available. + */ + public abstract int getWidth(ImageObserver observer); + + /** + * Gets the height of this image. The specified ImageObserver object is + * notified when the height of this image is available. + * + * @param observer + * the ImageObserver object which is is notified when the height + * of this image is available. + * @return the height of image, or -1 if the height of this image is not + * available. + */ + public abstract int getHeight(ImageObserver observer); + + /** + * Gets the scaled instance of this Image. This method returns an Image + * object constructed from the source of this image with the specified + * width, height, and applied scaling algorithm. + * + * @param width + * the width of scaled Image. + * @param height + * the height of scaled Image. + * @param hints + * the constant which indicates scaling algorithm. + * @return the scaled Image. + */ + public Image getScaledInstance(int width, int height, int hints) { + ImageFilter filter; + if ((hints & (SCALE_SMOOTH | SCALE_AREA_AVERAGING)) != 0) { + filter = new AreaAveragingScaleFilter(width, height); + } else { + filter = new ReplicateScaleFilter(width, height); + } + ImageProducer producer = new FilteredImageSource(getSource(), filter); + return Toolkit.getDefaultToolkit().createImage(producer); + } + + /** + * Gets a Graphics object for rendering this image. This method can be used + * for off-screen images. + * + * @return a Graphics object for rendering to this image. + */ + public abstract Graphics getGraphics(); + + /** + * Flushes resources which are used by this Image object. This method resets + * the image to the reconstructed state from the image's source. + */ + public abstract void flush(); + + /** + * Gets the acceleration priority of this image. + * + * @return the acceleration priority of this image. + */ + public float getAccelerationPriority() { + return accelerationPriority; + } + + /** + * Sets the acceleration priority for this image. + * + * @param priority + * the new acceleration priority (value in the range 0-1). + */ + public void setAccelerationPriority(float priority) { + if (priority < 0 || priority > 1) { + // awt.10A=Priority must be a value between 0 and 1, inclusive + throw new IllegalArgumentException(Messages.getString("awt.10A")); //$NON-NLS-1$ + } + accelerationPriority = priority; + } + + /** + * Gets an ImageCapabilities object of this Image object for the specified + * GraphicsConfiguration. + * + * @param gc + * the specified GraphicsConfiguration object (null value means + * default GraphicsConfiguration). + * @return an ImageCapabilities object of this Image object for the + * specified GraphicsConfiguration. + */ + public ImageCapabilities getCapabilities(GraphicsConfiguration gc) { + // Note: common image is not accelerated. + return capabilities; + } +} diff --git a/awt/java/awt/ImageCapabilities.java b/awt/java/awt/ImageCapabilities.java new file mode 100644 index 000000000..c6d59462a --- /dev/null +++ b/awt/java/awt/ImageCapabilities.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +/** + * The ImageCapabilities class gives information about an image's capabilities. + * + * @since Android 1.0 + */ +public class ImageCapabilities implements Cloneable { + + /** + * The accelerated. + */ + private final boolean accelerated; + + /** + * Instantiates a new ImageCapabilities with the specified acceleration flag + * which indicates whether acceleration is desired or not. + * + * @param accelerated + * the accelerated flag. + */ + public ImageCapabilities(boolean accelerated) { + this.accelerated = accelerated; + } + + /** + * Returns a copy of this ImageCapabilities object. + * + * @return the copy of this ImageCapabilities object. + */ + @Override + public Object clone() { + return new ImageCapabilities(accelerated); + } + + /** + * Returns true if the Image of this ImageCapabilities is or can be + * accelerated. + * + * @return true, if the Image of this ImageCapabilities is or can be + * accelerated, false otherwise. + */ + public boolean isAccelerated() { + return accelerated; + } + + /** + * Returns true if this ImageCapabilities applies to the VolatileImage which + * can lose its surfaces. + * + * @return true if this ImageCapabilities applies to the VolatileImage which + * can lose its surfaces, false otherwise. + */ + public boolean isTrueVolatile() { + return true; + } +} diff --git a/awt/java/awt/Insets.java b/awt/java/awt/Insets.java new file mode 100644 index 000000000..04f198c61 --- /dev/null +++ b/awt/java/awt/Insets.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ + +package java.awt; + +import java.io.Serializable; + +import org.apache.harmony.misc.HashCode; + +/** + * The Insets class represents the borders of a container. This class describes + * the space that a container should leave at each edge: the top, the bottom, + * the right side, and the left side. The space can be filled with a border, a + * blank space, or a title. + * + * @since Android 1.0 + */ +public class Insets implements Cloneable, Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -2272572637695466749L; + + /** + * The top inset indicates the size of the space added to the top of the + * rectangle. + */ + public int top; + + /** + * The left inset indicates the size of the space added to the left side of + * the rectangle. + */ + public int left; + + /** + * The bottom inset indicates the size of the space subtracted from the + * bottom of the rectangle. + */ + public int bottom; + + /** + * The right inset indicates the size of the space subtracted from the right + * side of the rectangle. + */ + public int right; + + /** + * Instantiates a new Inset object with the specified top, left, bottom, + * right parameters. + * + * @param top + * the top inset. + * @param left + * the left inset. + * @param bottom + * the bottom inset. + * @param right + * the right inset. + */ + public Insets(int top, int left, int bottom, int right) { + setValues(top, left, bottom, right); + } + + /** + * Returns a hash code of the Insets object. + * + * @return a hash code of the Insets object. + */ + @Override + public int hashCode() { + int hashCode = HashCode.EMPTY_HASH_CODE; + hashCode = HashCode.combine(hashCode, top); + hashCode = HashCode.combine(hashCode, left); + hashCode = HashCode.combine(hashCode, bottom); + hashCode = HashCode.combine(hashCode, right); + return hashCode; + } + + /** + * Returns a copy of this Insets object. + * + * @return a copy of this Insets object. + */ + @Override + public Object clone() { + return new Insets(top, left, bottom, right); + } + + /** + * Checks if this Insets object is equal to the specified object. + * + * @param o + * the Object to be compared. + * @return true, if the object is an Insets object whose data values are + * equal to those of this object, false otherwise. + */ + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof Insets) { + Insets i = (Insets)o; + return ((i.left == left) && (i.bottom == bottom) && (i.right == right) && (i.top == top)); + } + return false; + } + + /** + * Returns a String representation of this Insets object. + * + * @return a String representation of this Insets object. + */ + @Override + public String toString() { + /* + * The format is based on 1.5 release behavior which can be revealed by + * the following code: System.out.println(new Insets(1, 2, 3, 4)); + */ + + return (getClass().getName() + "[left=" + left + ",top=" + top + //$NON-NLS-1$ //$NON-NLS-2$ + ",right=" + right + ",bottom=" + bottom + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + /** + * Sets top, left, bottom, and right insets to the specified values. + * + * @param top + * the top inset. + * @param left + * the left inset. + * @param bottom + * the bottom inset. + * @param right + * the right inset. + */ + public void set(int top, int left, int bottom, int right) { + setValues(top, left, bottom, right); + } + + /** + * Sets the values. + * + * @param top + * the top. + * @param left + * the left. + * @param bottom + * the bottom. + * @param right + * the right. + */ + private void setValues(int top, int left, int bottom, int right) { + this.top = top; + this.left = left; + this.bottom = bottom; + this.right = right; + } +} diff --git a/awt/java/awt/ItemSelectable.java b/awt/java/awt/ItemSelectable.java new file mode 100644 index 000000000..212cf709e --- /dev/null +++ b/awt/java/awt/ItemSelectable.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ + +package java.awt; + +import java.awt.event.ItemListener; + +/** + * The ItemSelectable interface represents a set of items which can be selected. + * + * @since Android 1.0 + */ +public interface ItemSelectable { + + /** + * Adds an ItemListener for receiving item events when the state of an item + * is changed by the user. + * + * @param l + * the ItemListener. + */ + public void addItemListener(ItemListener l); + + /** + * Gets an array of the selected objects or null if there is no selected + * object. + * + * @return an array of the selected objects or null if there is no selected + * object. + */ + public Object[] getSelectedObjects(); + + /** + * Removes the specified ItemListener. + * + * @param l + * the ItemListener which will be removed. + */ + public void removeItemListener(ItemListener l); + +} diff --git a/awt/java/awt/MenuComponent.java b/awt/java/awt/MenuComponent.java new file mode 100644 index 000000000..9c1b120e6 --- /dev/null +++ b/awt/java/awt/MenuComponent.java @@ -0,0 +1,783 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.awt; + +import java.awt.event.FocusListener; +import java.awt.event.MouseEvent; +import java.awt.peer.MenuComponentPeer; +import java.io.Serializable; +import java.util.Locale; //import javax.accessibility.Accessible; +//import javax.accessibility.AccessibleComponent; +//import javax.accessibility.AccessibleContext; +//import javax.accessibility.AccessibleRole; +//import javax.accessibility.AccessibleSelection; +//import javax.accessibility.AccessibleStateSet; +import org.apache.harmony.awt.gl.MultiRectArea; +import org.apache.harmony.awt.state.MenuItemState; +import org.apache.harmony.awt.state.MenuState; +import org.apache.harmony.luni.util.NotImplementedException; + +/** + * The MenuComponent abstract class is the superclass for menu components. Menu + * components receive and process AWT events. + * + * @since Android 1.0 + */ +public abstract class MenuComponent implements Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -4536902356223894379L; + + /** + * The name. + */ + private String name; + + /** + * The font. + */ + private Font font; + + /** + * The parent. + */ + MenuContainer parent; + + /** + * The deprecated event handler. + */ + boolean deprecatedEventHandler = true; + + /** + * The selected item index. + */ + private int selectedItemIndex; + + // ???AWT: private AccessibleContext accessibleContext; + + /** + * The toolkit. + */ + final Toolkit toolkit = Toolkit.getDefaultToolkit(); + + // ???AWT + /* + * protected abstract class AccessibleAWTMenuComponent extends + * AccessibleContext implements Serializable, AccessibleComponent, + * AccessibleSelection { private static final long serialVersionUID = + * -4269533416223798698L; public void addFocusListener(FocusListener + * listener) { } public boolean contains(Point pt) { return false; } public + * Accessible getAccessibleAt(Point pt) { return null; } public Color + * getBackground() { return null; } public Rectangle getBounds() { return + * null; } public Cursor getCursor() { return null; } public Font getFont() + * { return MenuComponent.this.getFont(); } public FontMetrics + * getFontMetrics(Font font) { return null; } public Color getForeground() { + * return null; } public Point getLocation() { return null; } public Point + * getLocationOnScreen() { return null; } public Dimension getSize() { + * return null; } public boolean isEnabled() { return true; // always + * enabled } public boolean isFocusTraversable() { return true; // always + * focus traversable } public boolean isShowing() { return true;// always + * showing } public boolean isVisible() { return true; // always visible } + * public void removeFocusListener(FocusListener listener) { } public void + * requestFocus() { } public void setBackground(Color color) { } public void + * setBounds(Rectangle rect) { } public void setCursor(Cursor cursor) { } + * public void setEnabled(boolean enabled) { } public void setFont(Font + * font) { MenuComponent.this.setFont(font); } public void + * setForeground(Color color) { } public void setLocation(Point pt) { } + * public void setSize(Dimension pt) { } public void setVisible(boolean + * visible) { } public void addAccessibleSelection(int index) { } public + * void clearAccessibleSelection() { } public Accessible + * getAccessibleSelection(int index) { return null; } public int + * getAccessibleSelectionCount() { return 0; } public boolean + * isAccessibleChildSelected(int index) { return false; } public void + * removeAccessibleSelection(int index) { } public void + * selectAllAccessibleSelection() { } + * @Override public Accessible getAccessibleChild(int index) { return null; + * } + * @Override public int getAccessibleChildrenCount() { return 0; } + * @Override public AccessibleComponent getAccessibleComponent() { return + * this; } + * @Override public String getAccessibleDescription() { return + * super.getAccessibleDescription(); } + * @Override public int getAccessibleIndexInParent() { toolkit.lockAWT(); + * try { Accessible aParent = getAccessibleParent(); int aIndex = -1; if + * (aParent instanceof MenuComponent) { MenuComponent parent = + * (MenuComponent) aParent; int count = parent.getItemCount(); for (int i = + * 0; i < count; i++) { MenuComponent comp = parent.getItem(i); if (comp + * instanceof Accessible) { aIndex++; if (comp == MenuComponent.this) { + * return aIndex; } } } } return -1; } finally { toolkit.unlockAWT(); } } + * @Override public String getAccessibleName() { return + * super.getAccessibleName(); } + * @Override public Accessible getAccessibleParent() { toolkit.lockAWT(); + * try { Accessible aParent = super.getAccessibleParent(); if (aParent != + * null) { return aParent; } MenuContainer parent = getParent(); if (parent + * instanceof Accessible) { aParent = (Accessible) parent; } return aParent; + * } finally { toolkit.unlockAWT(); } } + * @Override public AccessibleRole getAccessibleRole() { return + * AccessibleRole.AWT_COMPONENT; } + * @Override public AccessibleSelection getAccessibleSelection() { return + * this; } + * @Override public AccessibleStateSet getAccessibleStateSet() { return new + * AccessibleStateSet(); } + * @Override public Locale getLocale() { return Locale.getDefault(); } } + */ + + /** + * The accessor to MenuComponent internal state, utilized by the visual + * theme. + * + * @throws HeadlessException + * the headless exception. + */ + // ???AWT + /* + * class State implements MenuState { Dimension size; Dimension getSize() { + * if (size == null) { calculate(); } return size; } public int getWidth() { + * return getSize().width; } public int getHeight() { return + * getSize().height; } public Font getFont() { return + * MenuComponent.this.getFont(); } public int getItemCount() { return + * MenuComponent.this.getItemCount(); } public int getSelectedItemIndex() { + * return MenuComponent.this.getSelectedItemIndex(); } public boolean + * isFontSet() { return MenuComponent.this.isFontSet(); } + * @SuppressWarnings("deprecation") public FontMetrics getFontMetrics(Font + * f) { return MenuComponent.this.toolkit.getFontMetrics(f); } public Point + * getLocation() { return MenuComponent.this.getLocation(); } public + * MenuItemState getItem(int index) { MenuItem item = + * MenuComponent.this.getItem(index); return item.itemState; } public void + * setSize(int w, int h) { this.size = new Dimension(w, h); } void + * calculate() { size = new Dimension(); + * size.setSize(toolkit.theme.calculateMenuSize(this)); } void reset() { for + * (int i = 0; i < getItemCount(); i++) { ((MenuItem.State) + * getItem(i)).reset(); } } } + */ + + /** + * Pop-up box for menu. It transfers the paint events, keyboard and mouse + * events to the menu component itself. + */ + // ???AWT + /* + * class MenuPopupBox extends PopupBox { private final Point lastMousePos = + * new Point(); + * @Override boolean isMenu() { return true; } + * @Override void paint(Graphics gr) { MenuComponent.this.paint(gr); } + * @Override void onKeyEvent(int eventId, int vKey, long when, int + * modifiers) { MenuComponent.this.onKeyEvent(eventId, vKey, when, + * modifiers); } + * @Override void onMouseEvent(int eventId, Point where, int mouseButton, + * long when, int modifiers, int wheelRotation) { // prevent conflict of + * mouse and keyboard // when sub-menu drops down due to keyboard navigation + * if (lastMousePos.equals(where) && (eventId == MouseEvent.MOUSE_MOVED || + * eventId == MouseEvent.MOUSE_ENTERED)) { return; } + * lastMousePos.setLocation(where); MenuComponent.this.onMouseEvent(eventId, + * where, mouseButton, when, modifiers); } } + */ + + /** + * Instantiates a new MenuComponent object. + * + * @throws HeadlessException + * if the graphical interface environment can't support + * MenuComponents. + */ + public MenuComponent() throws HeadlessException { + toolkit.lockAWT(); + try { + Toolkit.checkHeadless(); + name = autoName(); + selectedItemIndex = -1; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the name of the MenuComponent object. + * + * @return the name of the MenuComponent object. + */ + public String getName() { + toolkit.lockAWT(); + try { + return name; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Returns a String representation of the MenuComponent object. + * + * @return a String representation of the MenuComponent object. + */ + @Override + public String toString() { + toolkit.lockAWT(); + try { + return getClass().getName() + "[" + paramString() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Gets the parent menu container. + * + * @return the parent. + */ + public MenuContainer getParent() { + toolkit.lockAWT(); + try { + return parent; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the name of the MenuComponent to the specified string. + * + * @param name + * the new name of the MenuComponent object. + */ + public void setName(String name) { + toolkit.lockAWT(); + try { + this.name = name; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Dispatches AWT event. + * + * @param event + * the AWTEvent. + */ + public final void dispatchEvent(AWTEvent event) { + toolkit.lockAWT(); + try { + processEvent(event); + if (deprecatedEventHandler) { + postDeprecatedEvent(event); + } + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Post deprecated event. + * + * @param event + * the event. + */ + void postDeprecatedEvent(AWTEvent event) { + Event evt = event.getEvent(); + if (evt != null) { + postEvent(evt); + } + } + + /** + * Gets the peer of the MenuComponent; an application must not use this + * method directly. + * + * @return the MenuComponentPeer object. + * @throws NotImplementedException + * if this method is not implemented by a subclass. + * @deprecated an application must not use this method directly. + */ + @Deprecated + public MenuComponentPeer getPeer() throws org.apache.harmony.luni.util.NotImplementedException { + toolkit.lockAWT(); + try { + } finally { + toolkit.unlockAWT(); + } + if (true) { + throw new RuntimeException("Method is not implemented"); //TODO: implement //$NON-NLS-1$ + } + return null; + } + + /** + * Gets the locking object of this MenuComponent. + * + * @return the locking object of this MenuComponent. + */ + protected final Object getTreeLock() { + return toolkit.awtTreeLock; + } + + /** + * Posts the Event to the MenuComponent. + * + * @param e + * the Event. + * @return true, if the event is posted successfully, false otherwise. + * @deprecated Replaced dispatchEvent method. + */ + @SuppressWarnings("deprecation") + @Deprecated + public boolean postEvent(Event e) { + toolkit.lockAWT(); + try { + if (parent != null) { + return parent.postEvent(e); + } + return false; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Returns the string representation of the MenuComponent state. + * + * @return returns the string representation of the MenuComponent state. + */ + protected String paramString() { + toolkit.lockAWT(); + try { + return getName(); + } finally { + toolkit.unlockAWT(); + } + } + + // ???AWT + /* + * public AccessibleContext getAccessibleContext() { toolkit.lockAWT(); try + * { if (accessibleContext == null) { accessibleContext = + * createAccessibleContext(); } return accessibleContext; } finally { + * toolkit.unlockAWT(); } } + */ + + /** + * Gets the font of the MenuComponent object. + * + * @return the Font of the MenuComponent object. + */ + public Font getFont() { + toolkit.lockAWT(); + try { + if (font == null && hasDefaultFont()) { + return toolkit.getDefaultFont(); + } + if (font == null && parent != null) { + return parent.getFont(); + } + return font; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Checks if is font set. + * + * @return true, if is font set + */ + boolean isFontSet() { + return font != null + || ((parent instanceof MenuComponent) && ((MenuComponent)parent).isFontSet()); + } + + /** + * Checks for default font. + * + * @return true, if successful. + */ + boolean hasDefaultFont() { + return false; + } + + /** + * Processes an AWTEevent on this menu component. + * + * @param event + * the AWTEvent. + */ + protected void processEvent(AWTEvent event) { + toolkit.lockAWT(); + try { + // do nothing + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Removes the peer of the MenuComponent. + */ + public void removeNotify() { + toolkit.lockAWT(); + try { + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the Font for this MenuComponent object. + * + * @param font + * the new Font to be used for this MenuComponent. + */ + public void setFont(Font font) { + toolkit.lockAWT(); + try { + this.font = font; + } finally { + toolkit.unlockAWT(); + } + } + + /** + * Sets the parent. + * + * @param parent + * the new parent. + */ + void setParent(MenuContainer parent) { + this.parent = parent; + } + + /** + * Gets the location. + * + * @return the location. + */ + Point getLocation() { + // to be overridden + return new Point(0, 0); + } + + /** + * Gets the width. + * + * @return the width. + */ + int getWidth() { + // to be overridden + return 1; + } + + /** + * Gets the height. + * + * @return the height. + */ + int getHeight() { + // to be overridden + return 1; + } + + /** + * Recursively find the menu item for a menu shortcut. + * + * @param gr + * the gr. + * @return the menu item; or null if the item is not available for this + * shortcut. + */ + // ???AWT + /* + * MenuItem getShortcutMenuItemImpl(MenuShortcut ms) { if (ms == null) { + * return null; } for (int i = 0; i < getItemCount(); i++) { MenuItem mi = + * getItem(i); if (mi instanceof Menu) { mi = ((Menu) + * mi).getShortcutMenuItemImpl(ms); if (mi != null) { return mi; } } else if + * (ms.equals(mi.getShortcut())) { return mi; } } return null; } + */ + + void paint(Graphics gr) { + gr.setColor(Color.LIGHT_GRAY); + gr.fillRect(0, 0, getWidth(), getHeight()); + gr.setColor(Color.BLACK); + } + + /** + * Mouse events handler. + * + * @param eventId + * one of the MouseEvent.MOUSE_* constants. + * @param where + * mouse location. + * @param mouseButton + * mouse button that was pressed or released. + * @param when + * event time. + * @param modifiers + * input event modifiers. + */ + void onMouseEvent(int eventId, Point where, int mouseButton, long when, int modifiers) { + // to be overridden + } + + /** + * Keyboard event handler. + * + * @param eventId + * one of the KeyEvent.KEY_* constants. + * @param vKey + * the key code. + * @param when + * event time. + * @param modifiers + * input event modifiers. + */ + void onKeyEvent(int eventId, int vKey, long when, int modifiers) { + // to be overridden + } + + /** + * Post the ActionEvent or ItemEvent, depending on type of the menu item. + * + * @param index + * the index. + * @return the item rect. + */ + // ???AWT + /* + * void fireItemAction(int item, long when, int modifiers) { MenuItem mi = + * getItem(item); mi.itemSelected(when, modifiers); } MenuItem getItem(int + * index) { // to be overridden return null; } int getItemCount() { return + * 0; } + */ + + /** + * @return The sub-menu of currently selecetd item, or null if such a + * sub-menu is not available. + */ + // ???AWT + /* + * Menu getSelectedSubmenu() { if (selectedItemIndex < 0) { return null; } + * MenuItem item = getItem(selectedItemIndex); return (item instanceof Menu) + * ? (Menu) item : null; } + */ + + /** + * Convenience method for selectItem(index, true). + */ + // ???AWT + /* + * void selectItem(int index) { selectItem(index, true); } + */ + + /** + * Change the selection in the menu. + * + * @param index + * new selecetd item's index. + * @param showSubMenu + * if new selected item has a sub-menu, should that sub-menu be + * displayed. + */ + // ???AWT + /* + * void selectItem(int index, boolean showSubMenu) { if (selectedItemIndex + * == index) { return; } if (selectedItemIndex >= 0 && + * getItem(selectedItemIndex) instanceof Menu) { ((Menu) + * getItem(selectedItemIndex)).hide(); } MultiRectArea clip = + * getUpdateClip(index, selectedItemIndex); selectedItemIndex = index; + * Graphics gr = getGraphics(clip); if (gr != null) { paint(gr); } if + * (showSubMenu) { showSubMenu(selectedItemIndex); } } + */ + + /** + * Change the selected item to the next one in the requested direction + * moving cyclically, skipping separators + * + * @param forward + * the direction to move the selection. + * @param showSubMenu + * if new selected item has a sub-menu, should that sub-menu be + * displayed. + */ + // ???AWT + /* + * void selectNextItem(boolean forward, boolean showSubMenu) { int selected + * = getSelectedItemIndex(); int count = getItemCount(); if (count == 0) { + * return; } if (selected < 0) { selected = (forward ? count - 1 : 0); } int + * i = selected; do { i = (forward ? (i + 1) : (i + count - 1)) % count; i + * %= count; MenuItem item = getItem(i); if (!"-".equals(item.getLabel())) { + * //$NON-NLS-1$ selectItem(i, showSubMenu); return; } } while (i != + * selected); } void showSubMenu(int index) { if ((index < 0) || + * !isActive()) { return; } MenuItem item = getItem(index); if (item + * instanceof Menu) { Menu menu = ((Menu) getItem(index)); if + * (menu.getItemCount() == 0) { return; } Point location = + * getSubmenuLocation(index); menu.show(location.x, location.y, false); } } + */ + + /** + * @return the menu bar which is the root of current menu's hierarchy; or + * null if the hierarchy root is not a menu bar. + */ + // ???AWT + /* + * MenuBar getMenuBar() { if (parent instanceof MenuBar) { return (MenuBar) + * parent; } if (parent instanceof MenuComponent) { return ((MenuComponent) + * parent).getMenuBar(); } return null; } PopupBox getPopupBox() { return + * null; } + */ + + Rectangle getItemRect(int index) { + // to be overridden + return null; + } + + /** + * Determine the clip region when menu selection is changed from index1 to + * index2. + * + * @param index1 + * old selected item. + * @param index2 + * new selected item. + * @return the region to repaint. + */ + final MultiRectArea getUpdateClip(int index1, int index2) { + MultiRectArea clip = new MultiRectArea(); + if (index1 >= 0) { + clip.add(getItemRect(index1)); + } + if (index2 >= 0) { + clip.add(getItemRect(index2)); + } + return clip; + } + + /** + * Gets the submenu location. + * + * @param index + * the index. + * @return the submenu location. + */ + Point getSubmenuLocation(int index) { + // to be overridden + return new Point(0, 0); + } + + /** + * Gets the selected item index. + * + * @return the selected item index. + */ + int getSelectedItemIndex() { + return selectedItemIndex; + } + + /** + * Hide. + */ + void hide() { + selectedItemIndex = -1; + if (parent instanceof MenuComponent) { + ((MenuComponent)parent).itemHidden(this); + } + } + + /** + * Item hidden. + * + * @param mc + * the mc. + */ + void itemHidden(MenuComponent mc) { + // to be overridden + } + + /** + * Checks if is visible. + * + * @return true, if is visible. + */ + boolean isVisible() { + return true; + } + + /** + * Checks if is active. + * + * @return true, if is active. + */ + boolean isActive() { + return true; + } + + /** + * Hide all menu hierarchy. + */ + void endMenu() { + // ???AWT: toolkit.dispatcher.popupDispatcher.deactivateAll(); + } + + /** + * Handle the mouse click or Enter key event on a menu's item. + * + * @param when + * the event time. + * @param modifiers + * input event modifiers. + */ + void itemSelected(long when, int modifiers) { + endMenu(); + } + + /** + * Auto name. + * + * @return the string. + */ + String autoName() { + String name = getClass().getName(); + if (name.indexOf("$") != -1) { //$NON-NLS-1$ + return null; + } + // ???AWT: int number = toolkit.autoNumber.nextMenuComponent++; + int number = 0; + name = name.substring(name.lastIndexOf(".") + 1) + Integer.toString(number); //$NON-NLS-1$ + return name; + } + + /** + * Creates the Graphics object for the pop-up box of this menu component. + * + * @param clip + * the clip to set on this Graphics. + * @return the created Graphics object, or null if such object is not + * available. + */ + Graphics getGraphics(MultiRectArea clip) { + // to be overridden + return null; + } + + /** + * @return accessible context specific for particular menu component. + */ + // ???AWT + /* + * AccessibleContext createAccessibleContext() { return null; } + */ +} diff --git a/awt/java/awt/MenuContainer.java b/awt/java/awt/MenuContainer.java new file mode 100644 index 000000000..e509a1b1b --- /dev/null +++ b/awt/java/awt/MenuContainer.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ + +package java.awt; + +/** + * The MenuContainer interface represents all menu containers. + * + * @since Android 1.0 + */ +public interface MenuContainer { + + /** + * Removes the specified MenuComponent from the MenuContainer. + * + * @param c + * the MenuComponent. + */ + public void remove(MenuComponent c); + + /** + * Gets the Font of the MenuContainer. + * + * @return the font of the MenuContainer. + */ + public Font getFont(); + + /** + * Posts an Event. + * + * @param e + * the Event. + * @return true if the event is posted successfully, false otherwise. + * @deprecated Replaced by dispatchEvent method. + */ + @Deprecated + public boolean postEvent(Event e); + +} diff --git a/awt/java/awt/ModalContext.java b/awt/java/awt/ModalContext.java new file mode 100644 index 000000000..32a591200 --- /dev/null +++ b/awt/java/awt/ModalContext.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt; + +/** + * + * The context for nested event loop. It can be dialog, popup menu etc. + */ +class ModalContext { + + private boolean running = false; + + private final Toolkit toolkit; + + ModalContext() { + toolkit = Toolkit.getDefaultToolkit(); + } + + /** + * Set up and run modal loop in this context + * + */ + void runModalLoop() { + running = true; + toolkit.dispatchThread.runModalLoop(this); + } + + /** + * Leave the modal loop running in this context + * This method doesn't stops the loop immediately, + * it just sets the flag that says the modal loop to stop + * + */ + void endModalLoop() { + running = false; + } + + /** + * + * @return modal loop is currently running in this context + */ + boolean isModalLoopRunning() { + return running; + } + +} diff --git a/awt/java/awt/MouseDispatcher.java b/awt/java/awt/MouseDispatcher.java new file mode 100644 index 000000000..df48f9d61 --- /dev/null +++ b/awt/java/awt/MouseDispatcher.java @@ -0,0 +1,418 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev, Michael Danilov, Pavel Dolgov + * @version $Revision$ + */ +package java.awt; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.Dispatcher.MouseGrabManager; +import java.util.EventListener; + +import org.apache.harmony.awt.wtk.NativeEvent; +import org.apache.harmony.awt.wtk.NativeWindow; + + +class MouseDispatcher { + + // Fields for synthetic mouse click events generation + private static final int clickDelta = 5; + private final long[] lastPressTime = new long[] {0l, 0l, 0l}; + private final Point[] lastPressPos = new Point[] {null, null, null}; + private final boolean[] buttonPressed = new boolean[] {false, false, false}; + private final int[] clickCount = new int[] {0, 0, 0}; + + // Fields for mouse entered/exited support + private Component lastUnderPointer = null; + private final Point lastScreenPos = new Point(-1, -1); + + // Fields for redundant mouse moved/dragged filtering + private Component lastUnderMotion = null; + private Point lastLocalPos = new Point(-1, -1); + + private final MouseGrabManager mouseGrabManager; + private final Toolkit toolkit; + + static Point convertPoint(Component src, int x, int y, Component dest) { + Point srcPoint = getAbsLocation(src); + Point destPoint = getAbsLocation(dest); + + return new Point(x + (srcPoint.x - destPoint.x), + y + (srcPoint.y - destPoint.y)); + } + + static Point convertPoint(Component src, Point p, Component dst) { + return convertPoint(src, p.x, p.y, dst); + } + + private static Point getAbsLocation(Component comp) { + Point location = new Point(0, 0); +// BEGIN android-changed: AWT components not supported +// for (Component parent = comp; parent != null; parent = parent.parent) { +// Point parentPos = (parent instanceof EmbeddedWindow ? +// parent.getNativeWindow().getScreenPos() : +// parent.getLocation()); +// +// location.translate(parentPos.x, parentPos.y); +// +// if (parent instanceof Window) { +// break; +// } +// } +// END android-changed + + return location; + } + + MouseDispatcher(MouseGrabManager mouseGrabManager, + Toolkit toolkit) { + this.mouseGrabManager = mouseGrabManager; + this.toolkit = toolkit; + } + + Point getPointerPos() { + return lastScreenPos; + } + + boolean dispatch(Component src, NativeEvent event) { + int id = event.getEventId(); + + lastScreenPos.setLocation(event.getScreenPos()); + checkMouseEnterExit(event.getInputModifiers(), event.getTime()); + + if (id == MouseEvent.MOUSE_WHEEL) { +// BEGIN android-changed: AWT components not supported +// dispatchWheelEvent(src, event); +// END android-changed + } else if ((id != MouseEvent.MOUSE_ENTERED) && + (id != MouseEvent.MOUSE_EXITED)) { + PointerInfo info = new PointerInfo(src, event.getLocalPos()); + + mouseGrabManager.preprocessEvent(event); + findEventSource(info); + if ((id == MouseEvent.MOUSE_PRESSED) || + (id == MouseEvent.MOUSE_RELEASED)) { + + dispatchButtonEvent(info, event); + } else if ((id == MouseEvent.MOUSE_MOVED) || + (id == MouseEvent.MOUSE_DRAGGED)) { + + dispatchMotionEvent(info, event); + } + } + + return false; + } + + private void checkMouseEnterExit(int modifiers, long when) { +// BEGIN android-changed: AWT components not supported +// PointerInfo info = findComponentUnderPointer(); +// Component curUnderPointer = +// propagateEvent(info, AWTEvent.MOUSE_EVENT_MASK, +// MouseListener.class, false).src; +// +// if (curUnderPointer != lastUnderPointer) { +// Point pos = info.position; +// if ((lastUnderPointer != null) && +// lastUnderPointer.isMouseExitedExpected()) { +// +// Point exitPos = convertPoint(null, lastScreenPos.x, +// lastScreenPos.y, lastUnderPointer); +// +// postMouseEnterExit(MouseEvent.MOUSE_EXITED, modifiers, when, +// exitPos.x, exitPos.y, lastUnderPointer); +// } +// setCursor(curUnderPointer); +// if (curUnderPointer != null) { +// postMouseEnterExit(MouseEvent.MOUSE_ENTERED, modifiers, when, +// pos.x, pos.y, curUnderPointer); +// } +// lastUnderPointer = curUnderPointer; +// } +// END android-changed + } + + private void setCursor(Component comp) { + if (comp == null) { + return; + } + Component grabOwner = mouseGrabManager.getSyntheticGrabOwner(); + Component cursorComp = ((grabOwner != null) && + grabOwner.isShowing() ? grabOwner : comp); + cursorComp.setCursor(); + } + + private void postMouseEnterExit(int id, int mod, long when, + int x, int y, Component comp) { + if (comp.isIndirectlyEnabled()) { + toolkit.getSystemEventQueueImpl().postEvent( + new MouseEvent(comp, id, when, mod, x, y, 0, false)); + comp.setMouseExitedExpected(id == MouseEvent.MOUSE_ENTERED); + } else { + comp.setMouseExitedExpected(false); + } + } + + // BEGIN android-changed: AWT components not supported +// private PointerInfo findComponentUnderPointer() { +// NativeWindow nativeWindow = toolkit.getWindowFactory(). +// getWindowFromPoint(lastScreenPos); +// +// if (nativeWindow != null) { +// Component comp = toolkit.getComponentById(nativeWindow.getId()); +// +// if (comp != null) { +// Window window = comp.getWindowAncestor(); +// Point pointerPos = convertPoint(null, lastScreenPos.x, +// lastScreenPos.y, window); +// +// if (window.getClient().contains(pointerPos)) { +// PointerInfo info = new PointerInfo(window, pointerPos); +// +// fall2Child(info); +// +// return info; +// } +// } +// } +// +// return new PointerInfo(null, null); +// } +// END android-changed + + private void findEventSource(PointerInfo info) { + Component grabOwner = mouseGrabManager.getSyntheticGrabOwner(); + + if (grabOwner != null && grabOwner.isShowing()) { + info.position = convertPoint(info.src, info.position, grabOwner); + info.src = grabOwner; + } else { + //???AWT: rise2TopLevel(info); + //???AWT: fall2Child(info); + } + } + + // BEGIN android-changed: AWT components not supported +// private void rise2TopLevel(PointerInfo info) { +// while (!(info.src instanceof Window)) { +// info.position.translate(info.src.x, info.src.y); +// info.src = info.src.parent; +// } +// } +// +// private void fall2Child(PointerInfo info) { +// Insets insets = info.src.getInsets(); +// +// final Point pos = info.position; +// final int x = pos.x; +// final int y = pos.y; +// if ((x >= insets.left) && (y >= insets.top) && +// (x < (info.src.w - insets.right)) && +// (y < (info.src.h - insets.bottom))) +// { +// Component[] children = ((Container) info.src).getComponents(); +// +// for (Component child : children) { +// if (child.isShowing()) { +// if (child.contains(x - child.getX(), +// y - child.getY())) +// { +// info.src = child; +// pos.translate(-child.x, -child.y); +// +// if (child instanceof Container) { +// fall2Child(info); +// } +// +// return; +// } +// } +// } +// } +// } +// END android-changed + + private void dispatchButtonEvent(PointerInfo info, NativeEvent event) { + int button = event.getMouseButton(); + long time = event.getTime(); + int id = event.getEventId(); + int index = button - 1; + boolean clickRequired = false; + + propagateEvent(info, AWTEvent.MOUSE_EVENT_MASK, + MouseListener.class, false); + if (id == MouseEvent.MOUSE_PRESSED) { + int clickInterval = toolkit.dispatcher.clickInterval; + mouseGrabManager.onMousePressed(info.src); + buttonPressed[index] = true; + clickCount[index] = (!deltaExceeded(index, info) && + ((time - lastPressTime[index]) <= clickInterval)) ? + clickCount[index] + 1 : 1; + lastPressTime[index] = time; + lastPressPos[index] = info.position; + } else { + mouseGrabManager.onMouseReleased(info.src); + // set cursor back on synthetic mouse grab end: +// BEGIN android-changed: AWT components not supported +// setCursor(findComponentUnderPointer().src); +// END android-changed + if (buttonPressed[index]) { + buttonPressed[index] = false; + clickRequired = !deltaExceeded(index, info); + } else { + clickCount[index] = 0; + } + } + if (info.src.isIndirectlyEnabled()) { + final Point pos = info.position; + final int mod = event.getInputModifiers(); + toolkit.getSystemEventQueueImpl().postEvent( + new MouseEvent(info.src, id, time, mod, pos.x, + pos.y, clickCount[index], + event.getTrigger(), button)); + if (clickRequired) { + toolkit.getSystemEventQueueImpl().postEvent( + new MouseEvent(info.src, + MouseEvent.MOUSE_CLICKED, + time, mod, pos.x, pos.y, + clickCount[index], false, + button)); + } + } + } + + private boolean deltaExceeded(int index, PointerInfo info) { + final Point lastPos = lastPressPos[index]; + if (lastPos == null) { + return true; + } + return ((Math.abs(lastPos.x - info.position.x) > clickDelta) || + (Math.abs(lastPos.y - info.position.y) > clickDelta)); + } + + private void dispatchMotionEvent(PointerInfo info, NativeEvent event) { + propagateEvent(info, AWTEvent.MOUSE_MOTION_EVENT_MASK, + MouseMotionListener.class, false); + final Point pos = info.position; + if ((lastUnderMotion != info.src) || + !lastLocalPos.equals(pos)) { + + lastUnderMotion = info.src; + lastLocalPos = pos; + + if (info.src.isIndirectlyEnabled()) { + toolkit.getSystemEventQueueImpl().postEvent( + new MouseEvent(info.src, event.getEventId(), + event.getTime(), + event.getInputModifiers(), + pos.x, pos.y, 0, false)); + } + } + } + + MouseWheelEvent createWheelEvent(Component src, NativeEvent event, + Point where) { + + Integer scrollAmountProperty = + (Integer)toolkit.getDesktopProperty("awt.wheelScrollingSize"); //$NON-NLS-1$ + int amount = 1; + int type = MouseWheelEvent.WHEEL_UNIT_SCROLL; + + if (scrollAmountProperty != null) { + amount = scrollAmountProperty.intValue(); + if (amount == -1) { + type = MouseWheelEvent.WHEEL_BLOCK_SCROLL; + amount = 1; + } + } + return new MouseWheelEvent(src, event.getEventId(), + event.getTime(), event.getInputModifiers(), + where.x, where.y, 0, false, type, amount, + event.getWheelRotation()); + } + +// BEGIN android-changed: AWT components not supported +// private void dispatchWheelEvent(Component src, NativeEvent event) { +// PointerInfo info = findComponentUnderPointer(); +// +// if (info.src == null) { +// info.src = src; +// info.position = event.getLocalPos(); +// } +// +// propagateEvent(info, AWTEvent.MOUSE_WHEEL_EVENT_MASK, +// MouseWheelListener.class, true); +// if ((info.src != null) && info.src.isIndirectlyEnabled()) { +// toolkit.getSystemEventQueueImpl().postEvent( +// createWheelEvent(info.src, event, info.position)); +// } +// } +// END android-changed + + private PointerInfo propagateEvent(PointerInfo info, long mask, + Class type, boolean pierceHW) { + Component src = info.src; + while ((src != null) && + (src.isLightweight() || pierceHW) && + !(src.isMouseEventEnabled(mask) || + (src.getListeners(type).length > 0))) { + + info.position.translate(src.x, src.y); +// BEGIN android-changed: AWT components not supported +// src = src.parent; +// END android-changed + info.src = src; + } + + return info; + } + +// BEGIN android-changed: AWT components not supported +// Window findWindowAt(Point p) { +// NativeWindow nativeWindow = +// toolkit.getWindowFactory().getWindowFromPoint(p); +// +// Window window = null; +// if (nativeWindow != null) { +// Component comp = toolkit.getComponentById(nativeWindow.getId()); +// +// if (comp != null) { +// window = comp.getWindowAncestor(); +// } +// } +// return window; +// } +// END android-changed + + private class PointerInfo { + + Component src; + Point position; + + PointerInfo(Component src, Point position) { + this.src = src; + this.position = position; + } + + } + +} diff --git a/awt/java/awt/Paint.java b/awt/java/awt/Paint.java new file mode 100644 index 000000000..dfea3a74b --- /dev/null +++ b/awt/java/awt/Paint.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; + +/** + * The Paint interface provides possibility of generating color patterns in + * device space for fill, draw, or stroke operations in a Graphics2D. + * + * @since Android 1.0 + */ +public interface Paint extends Transparency { + + /** + * Creates the PaintContext which is used to generate color patterns for + * rendering operations of Graphics2D. + * + * @param cm + * the ColorModel object, or null. + * @param deviceBounds + * the Rectangle represents the bounding box of device space for + * the graphics rendering operations. + * @param userBounds + * the Rectangle represents bounding box of user space for the + * graphics rendering operations. + * @param xform + * the AffineTransform for translation from user space to device + * space. + * @param hints + * the RenderingHints preferences. + * @return the PaintContext for generating color patterns. + */ + PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, + AffineTransform xform, RenderingHints hints); +} diff --git a/awt/java/awt/PaintContext.java b/awt/java/awt/PaintContext.java new file mode 100644 index 000000000..966b6ca99 --- /dev/null +++ b/awt/java/awt/PaintContext.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +import java.awt.image.ColorModel; +import java.awt.image.Raster; + +/** + * The PaintContext interface determines the specific environment for generating + * color patterns in device space for fill, draw, or stroke rendering operations + * using Graphics2D. This interface provides colors through the Raster object + * associated with the specific ColorModel for Graphics2D rendering operations. + * + * @since Android 1.0 + */ +public interface PaintContext { + + /** + * Releases the resources allocated for the operation. + */ + void dispose(); + + /** + * Gets the color model. + * + * @return the ColorModel object. + */ + ColorModel getColorModel(); + + /** + * Gets the Raster which defines the colors of the specified rectangular + * area for Graphics2D rendering operations. + * + * @param x + * the X coordinate of the device space area for which colors are + * generated. + * @param y + * the Y coordinate of the device space area for which colors are + * generated. + * @param w + * the width of the device space area for which colors are + * generated. + * @param h + * the height of the device space area for which colors are + * generated. + * @return the Raster object which contains the colors of the specified + * rectangular area for Graphics2D rendering operations. + */ + Raster getRaster(int x, int y, int w, int h); +} diff --git a/awt/java/awt/Point.java b/awt/java/awt/Point.java new file mode 100644 index 000000000..8ec424121 --- /dev/null +++ b/awt/java/awt/Point.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt; + +import java.awt.geom.Point2D; +import java.io.Serializable; + +/** + * The Point class represents a point location with coordinates X, Y in current + * coordinate system. + * + * @since Android 1.0 + */ +public class Point extends Point2D implements Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -5276940640259749850L; + + /** + * The X coordinate of Point. + */ + public int x; + + /** + * The Y coordinate of Point. + */ + public int y; + + /** + * Instantiates a new point with (0, O) coordinates, the origin of + * coordinate system. + */ + public Point() { + setLocation(0, 0); + } + + /** + * Instantiates a new point with (x, y) coordinates. + * + * @param x + * the X coordinate of Point. + * @param y + * the Y coordinate of Point. + */ + public Point(int x, int y) { + setLocation(x, y); + } + + /** + * Instantiates a new point, giving it the same location as the parameter p. + * + * @param p + * the Point object giving the coordinates of the new point. + */ + public Point(Point p) { + setLocation(p.x, p.y); + } + + /** + * Compares current Point with the specified object. + * + * @param obj + * the Object to be compared. + * @return true, if the Object being compared is a Point whose coordinates + * are equal to the coordinates of this Point, false otherwise. + * @see java.awt.geom.Point2D#equals(Object) + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Point) { + Point p = (Point)obj; + return x == p.x && y == p.y; + } + return false; + } + + /** + * Returns string representation of the current Point object. + * + * @return a string representation of the current Point object. + */ + @Override + public String toString() { + return getClass().getName() + "[x=" + x + ",y=" + y + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + /** + * Gets X coordinate of Point as a double. + * + * @return X coordinate of the point as a double. + * @see java.awt.geom.Point2D#getX() + */ + @Override + public double getX() { + return x; + } + + /** + * Gets Y coordinate of Point as a double. + * + * @return Y coordinate of the point as a double. + * @see java.awt.geom.Point2D#getY() + */ + @Override + public double getY() { + return y; + } + + /** + * Gets the location of the Point as a new Point object. + * + * @return a copy of the Point. + */ + public Point getLocation() { + return new Point(x, y); + } + + /** + * Sets the location of the Point to the same coordinates as p. + * + * @param p + * the Point that gives the new location. + */ + public void setLocation(Point p) { + setLocation(p.x, p.y); + } + + /** + * Sets the location of the Point to the coordinates X, Y. + * + * @param x + * the X coordinate of the Point's new location. + * @param y + * the Y coordinate of the Point's new location. + */ + public void setLocation(int x, int y) { + this.x = x; + this.y = y; + } + + /** + * Sets the location of Point to the specified double coordinates. + * + * @param x + * the X the Point's new location. + * @param y + * the Y the Point's new location. + * @see java.awt.geom.Point2D#setLocation(double, double) + */ + @Override + public void setLocation(double x, double y) { + x = x < Integer.MIN_VALUE ? Integer.MIN_VALUE : x > Integer.MAX_VALUE ? Integer.MAX_VALUE + : x; + y = y < Integer.MIN_VALUE ? Integer.MIN_VALUE : y > Integer.MAX_VALUE ? Integer.MAX_VALUE + : y; + setLocation((int)Math.round(x), (int)Math.round(y)); + } + + /** + * Moves the Point to the specified (x, y) location. + * + * @param x + * the X coordinate of the new location. + * @param y + * the Y coordinate of the new location. + */ + public void move(int x, int y) { + setLocation(x, y); + } + + /** + * Translates current Point moving it from the position (x, y) to the new + * position given by (x+dx, x+dy) coordinates. + * + * @param dx + * the horizontal delta - the Point is moved to this distance + * along X axis. + * @param dy + * the vertical delta - the Point is moved to this distance along + * Y axis. + */ + public void translate(int dx, int dy) { + x += dx; + y += dy; + } + +} diff --git a/awt/java/awt/Polygon.java b/awt/java/awt/Polygon.java new file mode 100644 index 000000000..de31eb989 --- /dev/null +++ b/awt/java/awt/Polygon.java @@ -0,0 +1,515 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt; + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.gl.*; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Polygon class defines an closed area specified by n vertices and n edges. + * The coordinates of the vertices are specified by x, y arrays. The edges are + * the line segments from the point (x[i], y[i]) to the point (x[i+1], y[i+1]), + * for -1 < i < (n-1) plus the line segment from the point (x[n-1], y[n-1]) to + * the point (x[0], y[0]) point. The Polygon is empty if the number of vertices + * is zero. + * + * @since Android 1.0 + */ +public class Polygon implements Shape, Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -6460061437900069969L; + + /** + * The points buffer capacity. + */ + private static final int BUFFER_CAPACITY = 4; + + /** + * The number of Polygon vertices. + */ + public int npoints; + + /** + * The array of X coordinates of the vertices. + */ + public int[] xpoints; + + /** + * The array of Y coordinates of the vertices. + */ + public int[] ypoints; + + /** + * The smallest Rectangle that completely contains this Polygon. + */ + protected Rectangle bounds; + + /* + * Polygon path iterator + */ + /** + * The internal Class Iterator. + */ + class Iterator implements PathIterator { + + /** + * The source Polygon object. + */ + public Polygon p; + + /** + * The path iterator transformation. + */ + public AffineTransform t; + + /** + * The current segment index. + */ + public int index; + + /** + * Constructs a new Polygon.Iterator for the given polygon and + * transformation + * + * @param at + * the AffineTransform object to apply rectangle path. + * @param p + * the p. + */ + public Iterator(AffineTransform at, Polygon p) { + this.p = p; + this.t = at; + if (p.npoints == 0) { + index = 1; + } + } + + public int getWindingRule() { + return WIND_EVEN_ODD; + } + + public boolean isDone() { + return index > p.npoints; + } + + public void next() { + index++; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + // awt.110=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.110")); //$NON-NLS-1$ + } + if (index == p.npoints) { + return SEG_CLOSE; + } + coords[0] = p.xpoints[index]; + coords[1] = p.ypoints[index]; + if (t != null) { + t.transform(coords, 0, coords, 0, 1); + } + return index == 0 ? SEG_MOVETO : SEG_LINETO; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + // awt.110=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.110")); //$NON-NLS-1$ + } + if (index == p.npoints) { + return SEG_CLOSE; + } + coords[0] = p.xpoints[index]; + coords[1] = p.ypoints[index]; + if (t != null) { + t.transform(coords, 0, coords, 0, 1); + } + return index == 0 ? SEG_MOVETO : SEG_LINETO; + } + } + + /** + * Instantiates a new empty polygon. + */ + public Polygon() { + xpoints = new int[BUFFER_CAPACITY]; + ypoints = new int[BUFFER_CAPACITY]; + } + + /** + * Instantiates a new polygon with the specified number of vertices, and the + * given arrays of x, y vertex coordinates. The length of each coordinate + * array may not be less than the specified number of vertices but may be + * greater. Only the first n elements are used from each coordinate array. + * + * @param xpoints + * the array of X vertex coordinates. + * @param ypoints + * the array of Y vertex coordinates. + * @param npoints + * the number vertices of the polygon. + * @throws IndexOutOfBoundsException + * if the length of xpoints or ypoints is less than n. + * @throws NegativeArraySizeException + * if n is negative. + */ + public Polygon(int[] xpoints, int[] ypoints, int npoints) { + if (npoints > xpoints.length || npoints > ypoints.length) { + // awt.111=Parameter npoints is greater than array length + throw new IndexOutOfBoundsException(Messages.getString("awt.111")); //$NON-NLS-1$ + } + if (npoints < 0) { + // awt.112=Negative number of points + throw new NegativeArraySizeException(Messages.getString("awt.112")); //$NON-NLS-1$ + } + this.npoints = npoints; + this.xpoints = new int[npoints]; + this.ypoints = new int[npoints]; + System.arraycopy(xpoints, 0, this.xpoints, 0, npoints); + System.arraycopy(ypoints, 0, this.ypoints, 0, npoints); + } + + /** + * Resets the current Polygon to an empty Polygon. More precisely, the + * number of Polygon vertices is set to zero, but x, y coordinates arrays + * are not affected. + */ + public void reset() { + npoints = 0; + bounds = null; + } + + /** + * Invalidates the data that depends on the vertex coordinates. This method + * should be called after direct manipulations of the x, y vertex + * coordinates arrays to avoid unpredictable results of methods which rely + * on the bounding box. + */ + public void invalidate() { + bounds = null; + } + + /** + * Adds the point to the Polygon and updates the bounding box accordingly. + * + * @param px + * the X coordinate of the added vertex. + * @param py + * the Y coordinate of the added vertex. + */ + public void addPoint(int px, int py) { + if (npoints == xpoints.length) { + int[] tmp; + + tmp = new int[xpoints.length + BUFFER_CAPACITY]; + System.arraycopy(xpoints, 0, tmp, 0, xpoints.length); + xpoints = tmp; + + tmp = new int[ypoints.length + BUFFER_CAPACITY]; + System.arraycopy(ypoints, 0, tmp, 0, ypoints.length); + ypoints = tmp; + } + + xpoints[npoints] = px; + ypoints[npoints] = py; + npoints++; + + if (bounds != null) { + bounds.setFrameFromDiagonal(Math.min(bounds.getMinX(), px), Math.min(bounds.getMinY(), + py), Math.max(bounds.getMaxX(), px), Math.max(bounds.getMaxY(), py)); + } + } + + /** + * Gets the bounding rectangle of the Polygon. The bounding rectangle is the + * smallest rectangle which contains the Polygon. + * + * @return the bounding rectangle of the Polygon. + * @see java.awt.Shape#getBounds() + */ + public Rectangle getBounds() { + if (bounds != null) { + return bounds; + } + if (npoints == 0) { + return new Rectangle(); + } + + int bx1 = xpoints[0]; + int by1 = ypoints[0]; + int bx2 = bx1; + int by2 = by1; + + for (int i = 1; i < npoints; i++) { + int x = xpoints[i]; + int y = ypoints[i]; + if (x < bx1) { + bx1 = x; + } else if (x > bx2) { + bx2 = x; + } + if (y < by1) { + by1 = y; + } else if (y > by2) { + by2 = y; + } + } + + return bounds = new Rectangle(bx1, by1, bx2 - bx1, by2 - by1); + } + + /** + * Gets the bounding rectangle of the Polygon. The bounding rectangle is the + * smallest rectangle which contains the Polygon. + * + * @return the bounding rectangle of the Polygon. + * @deprecated Use getBounds() method. + */ + @Deprecated + public Rectangle getBoundingBox() { + return getBounds(); + } + + /** + * Gets the Rectangle2D which represents Polygon bounds. The bounding + * rectangle is the smallest rectangle which contains the Polygon. + * + * @return the bounding rectangle of the Polygon. + * @see java.awt.Shape#getBounds2D() + */ + public Rectangle2D getBounds2D() { + return getBounds().getBounds2D(); + } + + /** + * Translates all vertices of Polygon the specified distances along X, Y + * axis. + * + * @param mx + * the distance to translate horizontally. + * @param my + * the distance to translate vertically. + */ + public void translate(int mx, int my) { + for (int i = 0; i < npoints; i++) { + xpoints[i] += mx; + ypoints[i] += my; + } + if (bounds != null) { + bounds.translate(mx, my); + } + } + + /** + * Checks whether or not the point given by the coordinates x, y lies inside + * the Polygon. + * + * @param x + * the X coordinate of the point to check. + * @param y + * the Y coordinate of the point to check. + * @return true, if the specified point lies inside the Polygon, false + * otherwise. + * @deprecated Use contains(int, int) method. + */ + @Deprecated + public boolean inside(int x, int y) { + return contains((double)x, (double)y); + } + + /** + * Checks whether or not the point given by the coordinates x, y lies inside + * the Polygon. + * + * @param x + * the X coordinate of the point to check. + * @param y + * the Y coordinate of the point to check. + * @return true, if the specified point lies inside the Polygon, false + * otherwise. + */ + public boolean contains(int x, int y) { + return contains((double)x, (double)y); + } + + /** + * Checks whether or not the point with specified double coordinates lies + * inside the Polygon. + * + * @param x + * the X coordinate of the point to check. + * @param y + * the Y coordinate of the point to check. + * @return true, if the point given by the double coordinates lies inside + * the Polygon, false otherwise. + * @see java.awt.Shape#contains(double, double) + */ + public boolean contains(double x, double y) { + return Crossing.isInsideEvenOdd(Crossing.crossShape(this, x, y)); + } + + /** + * Checks whether or not the rectangle determined by the parameters [x, y, + * width, height] lies inside the Polygon. + * + * @param x + * the X coordinate of the rectangles's left upper corner as a + * double. + * @param y + * the Y coordinate of the rectangles's left upper corner as a + * double. + * @param width + * the width of rectangle as a double. + * @param height + * the height of rectangle as a double. + * @return true, if the specified rectangle lies inside the Polygon, false + * otherwise. + * @see java.awt.Shape#contains(double, double, double, double) + */ + public boolean contains(double x, double y, double width, double height) { + int cross = Crossing.intersectShape(this, x, y, width, height); + return cross != Crossing.CROSSING && Crossing.isInsideEvenOdd(cross); + } + + /** + * Checks whether or not the rectangle determined by the parameters [x, y, + * width, height] intersects the interior of the Polygon. + * + * @param x + * the X coordinate of the rectangles's left upper corner as a + * double. + * @param y + * the Y coordinate of the rectangles's left upper corner as a + * double. + * @param width + * the width of rectangle as a double. + * @param height + * the height of rectangle as a double. + * @return true, if the specified rectangle intersects the interior of the + * Polygon, false otherwise. + * @see java.awt.Shape#intersects(double, double, double, double) + */ + public boolean intersects(double x, double y, double width, double height) { + int cross = Crossing.intersectShape(this, x, y, width, height); + return cross == Crossing.CROSSING || Crossing.isInsideEvenOdd(cross); + } + + /** + * Checks whether or not the specified rectangle lies inside the Polygon. + * + * @param rect + * the Rectangle2D object. + * @return true, if the specified rectangle lies inside the Polygon, false + * otherwise. + * @see java.awt.Shape#contains(java.awt.geom.Rectangle2D) + */ + public boolean contains(Rectangle2D rect) { + return contains(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + } + + /** + * Checks whether or not the specified Point lies inside the Polygon. + * + * @param point + * the Point object. + * @return true, if the specified Point lies inside the Polygon, false + * otherwise. + */ + public boolean contains(Point point) { + return contains(point.getX(), point.getY()); + } + + /** + * Checks whether or not the specified Point2D lies inside the Polygon. + * + * @param point + * the Point2D object. + * @return true, if the specified Point2D lies inside the Polygon, false + * otherwise. + * @see java.awt.Shape#contains(java.awt.geom.Point2D) + */ + public boolean contains(Point2D point) { + return contains(point.getX(), point.getY()); + } + + /** + * Checks whether or not the interior of rectangle specified by the + * Rectangle2D object intersects the interior of the Polygon. + * + * @param rect + * the Rectangle2D object. + * @return true, if the Rectangle2D intersects the interior of the Polygon, + * false otherwise. + * @see java.awt.Shape#intersects(java.awt.geom.Rectangle2D) + */ + public boolean intersects(Rectangle2D rect) { + return intersects(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + } + + /** + * Gets the PathIterator object which gives the coordinates of the polygon, + * transformed according to the specified AffineTransform. + * + * @param t + * the specified AffineTransform object or null. + * @return PathIterator object for the Polygon. + * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform) + */ + public PathIterator getPathIterator(AffineTransform t) { + return new Iterator(t, this); + } + + /** + * Gets the PathIterator object which gives the coordinates of the polygon, + * transformed according to the specified AffineTransform. The flatness + * parameter is ignored. + * + * @param t + * the specified AffineTransform object or null. + * @param flatness + * the maximum number of the control points for a given curve + * which varies from colinear before a subdivided curve is + * replaced by a straight line connecting the endpoints. This + * parameter is ignored for the Polygon class. + * @return PathIterator object for the Polygon. + * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform, + * double) + */ + public PathIterator getPathIterator(AffineTransform t, double flatness) { + return new Iterator(t, this); + } + +} diff --git a/awt/java/awt/Rectangle.java b/awt/java/awt/Rectangle.java new file mode 100644 index 000000000..d8ebb3ad1 --- /dev/null +++ b/awt/java/awt/Rectangle.java @@ -0,0 +1,723 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt; + +import java.awt.geom.Rectangle2D; +import java.io.Serializable; + +/** + * The Rectangle class defines the rectangular area in terms of its upper left + * corner coordinates [x,y], its width, and its height. A Rectangle specified by + * [x, y, width, height] parameters has an outline path with corners at [x, y], + * [x + width,y], [x + width,y + height], and [x, y + height].
+ *
+ * The rectangle is empty if the width or height is negative or zero. In this + * case the isEmpty method returns true. + * + * @since Android 1.0 + */ +public class Rectangle extends Rectangle2D implements Shape, Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -4345857070255674764L; + + /** + * The X coordinate of the rectangle's left upper corner. + */ + public int x; + + /** + * The Y coordinate of the rectangle's left upper corner. + */ + public int y; + + /** + * The width of rectangle. + */ + public int width; + + /** + * The height of rectangle. + */ + public int height; + + /** + * Instantiates a new rectangle with [0, 0] upper left corner coordinates, + * the width and the height are zero. + */ + public Rectangle() { + setBounds(0, 0, 0, 0); + } + + /** + * Instantiates a new rectangle whose upper left corner coordinates are + * given by the Point object (p.X and p.Y), and the width and the height are + * zero. + * + * @param p + * the Point specifies the upper left corner coordinates of the + * rectangle. + */ + public Rectangle(Point p) { + setBounds(p.x, p.y, 0, 0); + } + + /** + * Instantiates a new rectangle whose upper left corner coordinates are + * given by the Point object (p.X and p.Y), and the width and the height are + * given by Dimension object (d.width and d.height). + * + * @param p + * the point specifies the upper left corner coordinates of the + * rectangle. + * @param d + * the dimension specifies the width and the height of the + * rectangle. + */ + public Rectangle(Point p, Dimension d) { + setBounds(p.x, p.y, d.width, d.height); + } + + /** + * Instantiates a new rectangle determined by the upper left corner + * coordinates (x, y), width and height. + * + * @param x + * the X upper left corner coordinate of the rectangle. + * @param y + * the Y upper left corner coordinate of the rectangle. + * @param width + * the width of rectangle. + * @param height + * the height of rectangle. + */ + public Rectangle(int x, int y, int width, int height) { + setBounds(x, y, width, height); + } + + /** + * Instantiates a new rectangle with [0, 0] as its upper left corner + * coordinates and the specified width and height. + * + * @param width + * the width of rectangle. + * @param height + * the height of rectangle. + */ + public Rectangle(int width, int height) { + setBounds(0, 0, width, height); + } + + /** + * Instantiates a new rectangle with the same coordinates as the given + * source rectangle. + * + * @param r + * the Rectangle object which parameters will be used for + * instantiating a new Rectangle. + */ + public Rectangle(Rectangle r) { + setBounds(r.x, r.y, r.width, r.height); + } + + /* + * public Rectangle(Dimension d) { setBounds(0, 0, d.width, d.height); } + */ + /** + * Gets the X coordinate of bound as a double. + * + * @return the X coordinate of bound as a double. + * @see java.awt.geom.RectangularShape#getX() + */ + @Override + public double getX() { + return x; + } + + /** + * Gets the Y coordinate of bound as a double. + * + * @return the Y coordinate of bound as a double. + * @see java.awt.geom.RectangularShape#getY() + */ + @Override + public double getY() { + return y; + } + + /** + * Gets the height of the rectangle as a double. + * + * @return the height of the rectangle as a double. + * @see java.awt.geom.RectangularShape#getHeight() + */ + @Override + public double getHeight() { + return height; + } + + /** + * Gets the width of the rectangle as a double. + * + * @return the width of the rectangle as a double. + * @see java.awt.geom.RectangularShape#getWidth() + */ + @Override + public double getWidth() { + return width; + } + + /** + * Determines whether or not the rectangle is empty. The rectangle is empty + * if its width or height is negative or zero. + * + * @return true, if the rectangle is empty, otherwise false. + * @see java.awt.geom.RectangularShape#isEmpty() + */ + @Override + public boolean isEmpty() { + return width <= 0 || height <= 0; + } + + /** + * Gets the size of a Rectangle as Dimension object. + * + * @return a Dimension object which represents size of the rectangle. + */ + public Dimension getSize() { + return new Dimension(width, height); + } + + /** + * Sets the size of the Rectangle. + * + * @param width + * the new width of the rectangle. + * @param height + * the new height of the rectangle. + */ + public void setSize(int width, int height) { + this.width = width; + this.height = height; + } + + /** + * Sets the size of a Rectangle specified as Dimension object. + * + * @param d + * a Dimension object which represents new size of a rectangle. + */ + public void setSize(Dimension d) { + setSize(d.width, d.height); + } + + /** + * Gets the location of a rectangle's upper left corner as a Point object. + * + * @return the Point object with coordinates equal to the upper left corner + * of the rectangle. + */ + public Point getLocation() { + return new Point(x, y); + } + + /** + * Sets the location of the rectangle in terms of its upper left corner + * coordinates X and Y. + * + * @param x + * the X coordinate of the rectangle's upper left corner. + * @param y + * the Y coordinate of the rectangle's upper left corner. + */ + public void setLocation(int x, int y) { + this.x = x; + this.y = y; + } + + /** + * Sets the location of a rectangle using a Point object to give the + * coordinates of the upper left corner. + * + * @param p + * the Point object which represents the new upper left corner + * coordinates of rectangle. + */ + public void setLocation(Point p) { + setLocation(p.x, p.y); + } + + /** + * Moves a rectangle to the new location by moving its upper left corner to + * the point with coordinates X and Y. + * + * @param x + * the new X coordinate of the rectangle's upper left corner. + * @param y + * the new Y coordinate of the rectangle's upper left corner. + * @deprecated Use setLocation(int, int) method. + */ + @Deprecated + public void move(int x, int y) { + setLocation(x, y); + } + + /** + * Sets the rectangle to be the nearest rectangle with integer coordinates + * bounding the rectangle defined by the double-valued parameters. + * + * @param x + * the X coordinate of the upper left corner of the double-valued + * rectangle to be bounded. + * @param y + * the Y coordinate of the upper left corner of the double-valued + * rectangle to be bounded. + * @param width + * the width of the rectangle to be bounded. + * @param height + * the height of the rectangle to be bounded. + * @see java.awt.geom.Rectangle2D#setRect(double, double, double, double) + */ + @Override + public void setRect(double x, double y, double width, double height) { + int x1 = (int)Math.floor(x); + int y1 = (int)Math.floor(y); + int x2 = (int)Math.ceil(x + width); + int y2 = (int)Math.ceil(y + height); + setBounds(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Sets a new size for the rectangle. + * + * @param width + * the rectangle's new width. + * @param height + * the rectangle's new height. + * @deprecated use the setSize(int, int) method. + */ + @Deprecated + public void resize(int width, int height) { + setBounds(x, y, width, height); + } + + /** + * Resets the bounds of a rectangle to the specified x, y, width and height + * parameters. + * + * @param x + * the new X coordinate of the upper left corner. + * @param y + * the new Y coordinate of the upper left corner. + * @param width + * the new width of rectangle. + * @param height + * the new height of rectangle. + * @deprecated use setBounds(int, int, int, int) method + */ + @Deprecated + public void reshape(int x, int y, int width, int height) { + setBounds(x, y, width, height); + } + + /** + * Gets bounds of the rectangle as a new Rectangle object. + * + * @return the Rectangle object with the same bounds as the original + * rectangle. + * @see java.awt.geom.RectangularShape#getBounds() + */ + @Override + public Rectangle getBounds() { + return new Rectangle(x, y, width, height); + } + + /** + * Gets the bounds of the original rectangle as a Rectangle2D object. + * + * @return the Rectangle2D object which represents the bounds of the + * original rectangle. + * @see java.awt.geom.Rectangle2D#getBounds2D() + */ + @Override + public Rectangle2D getBounds2D() { + return getBounds(); + } + + /** + * Sets the bounds of a rectangle to the specified x, y, width, and height + * parameters. + * + * @param x + * the X coordinate of the upper left corner. + * @param y + * the Y coordinate of the upper left corner. + * @param width + * the width of rectangle. + * @param height + * the height of rectangle. + */ + public void setBounds(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.height = height; + this.width = width; + } + + /** + * Sets the bounds of the rectangle to match the bounds of the Rectangle + * object sent as a parameter. + * + * @param r + * the Rectangle object which specifies the new bounds. + */ + public void setBounds(Rectangle r) { + setBounds(r.x, r.y, r.width, r.height); + } + + /** + * Enlarges the rectangle by moving each corner outward from the center by a + * distance of dx horizonally and a distance of dy vertically. Specifically, + * changes a rectangle with [x, y, width, height] parameters to a rectangle + * with [x-dx, y-dy, width+2*dx, height+2*dy] parameters. + * + * @param dx + * the horizontal distance to move each corner coordinate. + * @param dy + * the vertical distance to move each corner coordinate. + */ + public void grow(int dx, int dy) { + x -= dx; + y -= dy; + width += dx + dx; + height += dy + dy; + } + + /** + * Moves a rectangle a distance of mx along the x coordinate axis and a + * distance of my along y coordinate axis. + * + * @param mx + * the horizontal translation increment. + * @param my + * the vertical translation increment. + */ + public void translate(int mx, int my) { + x += mx; + y += my; + } + + /** + * Enlarges the rectangle to cover the specified point. + * + * @param px + * the X coordinate of the new point to be covered by the + * rectangle. + * @param py + * the Y coordinate of the new point to be covered by the + * rectangle. + */ + public void add(int px, int py) { + int x1 = Math.min(x, px); + int x2 = Math.max(x + width, px); + int y1 = Math.min(y, py); + int y2 = Math.max(y + height, py); + setBounds(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Enlarges the rectangle to cover the specified point with the new point + * given as a Point object. + * + * @param p + * the Point object that specifies the new point to be covered by + * the rectangle. + */ + public void add(Point p) { + add(p.x, p.y); + } + + /** + * Adds a new rectangle to the original rectangle, the result is an union of + * the specified specified rectangle and original rectangle. + * + * @param r + * the Rectangle which is added to the original rectangle. + */ + public void add(Rectangle r) { + int x1 = Math.min(x, r.x); + int x2 = Math.max(x + width, r.x + r.width); + int y1 = Math.min(y, r.y); + int y2 = Math.max(y + height, r.y + r.height); + setBounds(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Determines whether or not the point with specified coordinates [px, py] + * is within the bounds of the rectangle. + * + * @param px + * the X coordinate of point. + * @param py + * the Y coordinate of point. + * @return true, if the point with specified coordinates [px, py] is within + * the bounds of the rectangle, false otherwise. + */ + public boolean contains(int px, int py) { + if (isEmpty()) { + return false; + } + if (px < x || py < y) { + return false; + } + px -= x; + py -= y; + return px < width && py < height; + } + + /** + * Determines whether or not the point given as a Point object is within the + * bounds of the rectangle. + * + * @param p + * the Point object + * @return true, if the point p is within the bounds of the rectangle, + * otherwise false. + */ + public boolean contains(Point p) { + return contains(p.x, p.y); + } + + /** + * Determines whether or not the rectangle specified by [rx, ry, rw, rh] + * parameters is located inside the original rectangle. + * + * @param rx + * the X coordinate of the rectangle to compare. + * @param ry + * the Y coordinate of the rectangle to compare. + * @param rw + * the width of the rectangle to compare. + * @param rh + * the height of the rectangle to compare. + * @return true, if a rectangle with [rx, ry, rw, rh] parameters is entirely + * contained in the original rectangle, false otherwise. + */ + public boolean contains(int rx, int ry, int rw, int rh) { + return contains(rx, ry) && contains(rx + rw - 1, ry + rh - 1); + } + + /** + * Compares whether or not the rectangle specified by the Rectangle object + * is located inside the original rectangle. + * + * @param r + * the Rectangle object. + * @return true, if the rectangle specified by Rectangle object is entirely + * contained in the original rectangle, false otherwise. + */ + public boolean contains(Rectangle r) { + return contains(r.x, r.y, r.width, r.height); + } + + /** + * Compares whether or not a point with specified coordinates [px, py] + * belongs to a rectangle. + * + * @param px + * the X coordinate of a point. + * @param py + * the Y coordinate of a point. + * @return true, if a point with specified coordinates [px, py] belongs to a + * rectangle, otherwise false. + * @deprecated use contains(int, int) method. + */ + @Deprecated + public boolean inside(int px, int py) { + return contains(px, py); + } + + /** + * Returns the intersection of the original rectangle with the specified + * Rectangle2D. + * + * @param r + * the Rectangle2D object. + * @return the Rectangle2D object that is the result of intersecting the + * original rectangle with the specified Rectangle2D. + * @see java.awt.geom.Rectangle2D#createIntersection(java.awt.geom.Rectangle2D) + */ + @Override + public Rectangle2D createIntersection(Rectangle2D r) { + if (r instanceof Rectangle) { + return intersection((Rectangle)r); + } + Rectangle2D dst = new Rectangle2D.Double(); + Rectangle2D.intersect(this, r, dst); + return dst; + } + + /** + * Returns the intersection of the original rectangle with the specified + * rectangle. An empty rectangle is returned if there is no intersection. + * + * @param r + * the Rectangle object. + * @return the Rectangle object is result of the original rectangle with the + * specified rectangle. + */ + public Rectangle intersection(Rectangle r) { + int x1 = Math.max(x, r.x); + int y1 = Math.max(y, r.y); + int x2 = Math.min(x + width, r.x + r.width); + int y2 = Math.min(y + height, r.y + r.height); + return new Rectangle(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Determines whether or not the original rectangle intersects the specified + * rectangle. + * + * @param r + * the Rectangle object. + * @return true, if the two rectangles overlap, false otherwise. + */ + public boolean intersects(Rectangle r) { + return !intersection(r).isEmpty(); + } + + /** + * Determines where the specified Point is located with respect to the + * rectangle. This method computes whether the point is to the right or to + * the left of the rectangle and whether it is above or below the rectangle, + * and packs the result into an integer by using a binary OR operation with + * the following masks: + *
    + *
  • Rectangle2D.OUT_LEFT
  • + *
  • Rectangle2D.OUT_TOP
  • + *
  • Rectangle2D.OUT_RIGHT
  • + *
  • Rectangle2D.OUT_BOTTOM
  • + *
+ * If the rectangle is empty, all masks are set, and if the point is inside + * the rectangle, none are set. + * + * @param px + * the X coordinate of the specified point. + * @param py + * the Y coordinate of the specified point. + * @return the location of the Point relative to the rectangle as the result + * of logical OR operation with all out masks. + * @see java.awt.geom.Rectangle2D#outcode(double, double) + */ + @Override + public int outcode(double px, double py) { + int code = 0; + + if (width <= 0) { + code |= OUT_LEFT | OUT_RIGHT; + } else if (px < x) { + code |= OUT_LEFT; + } else if (px > x + width) { + code |= OUT_RIGHT; + } + + if (height <= 0) { + code |= OUT_TOP | OUT_BOTTOM; + } else if (py < y) { + code |= OUT_TOP; + } else if (py > y + height) { + code |= OUT_BOTTOM; + } + + return code; + } + + /** + * Enlarges the rectangle to cover the specified Rectangle2D. + * + * @param r + * the Rectangle2D object. + * @return the union of the original and the specified Rectangle2D. + * @see java.awt.geom.Rectangle2D#createUnion(java.awt.geom.Rectangle2D) + */ + @Override + public Rectangle2D createUnion(Rectangle2D r) { + if (r instanceof Rectangle) { + return union((Rectangle)r); + } + Rectangle2D dst = new Rectangle2D.Double(); + Rectangle2D.union(this, r, dst); + return dst; + } + + /** + * Enlarges the rectangle to cover the specified rectangle. + * + * @param r + * the Rectangle. + * @return the union of the original and the specified rectangle. + */ + public Rectangle union(Rectangle r) { + Rectangle dst = new Rectangle(this); + dst.add(r); + return dst; + } + + /** + * Compares the original Rectangle with the specified object. + * + * @param obj + * the specified Object for comparison. + * @return true, if the specified Object is a rectangle with the same + * dimensions as the original rectangle, false otherwise. + * @see java.awt.geom.Rectangle2D#equals(Object) + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Rectangle) { + Rectangle r = (Rectangle)obj; + return r.x == x && r.y == y && r.width == width && r.height == height; + } + return false; + } + + /** + * Returns a string representation of the rectangle; the string contains [x, + * y, width, height] parameters of the rectangle. + * + * @return the string representation of the rectangle. + */ + @Override + public String toString() { + // The output format based on 1.5 release behaviour. It could be + // obtained in the following way + // System.out.println(new Rectangle().toString()) + return getClass().getName() + "[x=" + x + ",y=" + y + //$NON-NLS-1$ //$NON-NLS-2$ + ",width=" + width + ",height=" + height + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + +} diff --git a/awt/java/awt/RenderingHints.java b/awt/java/awt/RenderingHints.java new file mode 100644 index 000000000..acf6fa15d --- /dev/null +++ b/awt/java/awt/RenderingHints.java @@ -0,0 +1,606 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * The RenderingHints class represents preferences for the rendering algorithms. + * The preferences are arbitrary and can be specified by Map objects or by + * key-value pairs. + * + * @since Android 1.0 + */ +public class RenderingHints implements Map, Cloneable { + + /** + * The Constant KEY_ALPHA_INTERPOLATION - alpha interpolation rendering hint + * key. + */ + public static final Key KEY_ALPHA_INTERPOLATION = new KeyImpl(1); + + /** + * The Constant VALUE_ALPHA_INTERPOLATION_DEFAULT - alpha interpolation + * rendering hint value. + */ + public static final Object VALUE_ALPHA_INTERPOLATION_DEFAULT = new KeyValue( + KEY_ALPHA_INTERPOLATION); + + /** + * The Constant VALUE_ALPHA_INTERPOLATION_SPEED - alpha interpolation + * rendering hint value. + */ + public static final Object VALUE_ALPHA_INTERPOLATION_SPEED = new KeyValue( + KEY_ALPHA_INTERPOLATION); + + /** + * The Constant VALUE_ALPHA_INTERPOLATION_QUALITY - alpha interpolation + * rendering hint value. + */ + public static final Object VALUE_ALPHA_INTERPOLATION_QUALITY = new KeyValue( + KEY_ALPHA_INTERPOLATION); + + /** + * The Constant KEY_ANTIALIASING - antialiasing rendering hint key. + */ + public static final Key KEY_ANTIALIASING = new KeyImpl(2); + + /** + * The Constant VALUE_ANTIALIAS_DEFAULT - antialiasing rendering hint value. + */ + public static final Object VALUE_ANTIALIAS_DEFAULT = new KeyValue(KEY_ANTIALIASING); + + /** + * The Constant VALUE_ANTIALIAS_ON - antialiasing rendering hint value. + */ + public static final Object VALUE_ANTIALIAS_ON = new KeyValue(KEY_ANTIALIASING); + + /** + * The Constant VALUE_ANTIALIAS_OFF - antialiasing rendering hint value. + */ + public static final Object VALUE_ANTIALIAS_OFF = new KeyValue(KEY_ANTIALIASING); + + /** + * The Constant KEY_COLOR_RENDERING - color rendering hint key. + */ + public static final Key KEY_COLOR_RENDERING = new KeyImpl(3); + + /** + * The Constant VALUE_COLOR_RENDER_DEFAULT - color rendering hint value. + */ + public static final Object VALUE_COLOR_RENDER_DEFAULT = new KeyValue(KEY_COLOR_RENDERING); + + /** + * The Constant VALUE_COLOR_RENDER_SPEED - color rendering hint value. + */ + public static final Object VALUE_COLOR_RENDER_SPEED = new KeyValue(KEY_COLOR_RENDERING); + + /** + * The Constant VALUE_COLOR_RENDER_QUALITY - color rendering hint value. + */ + public static final Object VALUE_COLOR_RENDER_QUALITY = new KeyValue(KEY_COLOR_RENDERING); + + /** + * The Constant KEY_DITHERING - dithering rendering hint key. + */ + public static final Key KEY_DITHERING = new KeyImpl(4); + + /** + * The Constant VALUE_DITHER_DEFAULT - dithering rendering hint value. + */ + public static final Object VALUE_DITHER_DEFAULT = new KeyValue(KEY_DITHERING); + + /** + * The Constant VALUE_DITHER_DISABLE - dithering rendering hint value. + */ + public static final Object VALUE_DITHER_DISABLE = new KeyValue(KEY_DITHERING); + + /** + * The Constant VALUE_DITHER_DISABLE - dithering rendering hint value. + */ + public static final Object VALUE_DITHER_ENABLE = new KeyValue(KEY_DITHERING); + + /** + * The Constant KEY_FRACTIONALMETRICS - fractional metrics rendering hint + * key. + */ + public static final Key KEY_FRACTIONALMETRICS = new KeyImpl(5); + + /** + * The Constant VALUE_FRACTIONALMETRICS_DEFAULT - fractional metrics + * rendering hint value. + */ + public static final Object VALUE_FRACTIONALMETRICS_DEFAULT = new KeyValue(KEY_FRACTIONALMETRICS); + + /** + * The Constant VALUE_FRACTIONALMETRICS_ON - fractional metrics rendering + * hint value. + */ + public static final Object VALUE_FRACTIONALMETRICS_ON = new KeyValue(KEY_FRACTIONALMETRICS); + + /** + * The Constant VALUE_FRACTIONALMETRICS_OFF - fractional metrics rendering + * hint value. + */ + public static final Object VALUE_FRACTIONALMETRICS_OFF = new KeyValue(KEY_FRACTIONALMETRICS); + + /** + * The Constant KEY_INTERPOLATION - interpolation rendering hint key. + */ + public static final Key KEY_INTERPOLATION = new KeyImpl(6); + + /** + * The Constant VALUE_INTERPOLATION_BICUBIC - interpolation rendering hint + * value. + */ + public static final Object VALUE_INTERPOLATION_BICUBIC = new KeyValue(KEY_INTERPOLATION); + + /** + * The Constant VALUE_INTERPOLATION_BILINEAR - interpolation rendering hint + * value. + */ + public static final Object VALUE_INTERPOLATION_BILINEAR = new KeyValue(KEY_INTERPOLATION); + + /** + * The Constant VALUE_INTERPOLATION_NEAREST_NEIGHBOR - interpolation + * rendering hint value. + */ + public static final Object VALUE_INTERPOLATION_NEAREST_NEIGHBOR = new KeyValue( + KEY_INTERPOLATION); + + /** + * The Constant KEY_RENDERING - rendering hint key. + */ + public static final Key KEY_RENDERING = new KeyImpl(7); + + /** + * The Constant VALUE_RENDER_DEFAULT - rendering hint value. + */ + public static final Object VALUE_RENDER_DEFAULT = new KeyValue(KEY_RENDERING); + + /** + * The Constant VALUE_RENDER_SPEED - rendering hint value. + */ + public static final Object VALUE_RENDER_SPEED = new KeyValue(KEY_RENDERING); + + /** + * The Constant VALUE_RENDER_QUALITY - rendering hint value. + */ + public static final Object VALUE_RENDER_QUALITY = new KeyValue(KEY_RENDERING); + + /** + * The Constant KEY_STROKE_CONTROL - stroke control hint key. + */ + public static final Key KEY_STROKE_CONTROL = new KeyImpl(8); + + /** + * The Constant VALUE_STROKE_DEFAULT - stroke hint value. + */ + public static final Object VALUE_STROKE_DEFAULT = new KeyValue(KEY_STROKE_CONTROL); + + /** + * The Constant VALUE_STROKE_NORMALIZE - stroke hint value. + */ + public static final Object VALUE_STROKE_NORMALIZE = new KeyValue(KEY_STROKE_CONTROL); + + /** + * The Constant VALUE_STROKE_PURE - stroke hint value. + */ + public static final Object VALUE_STROKE_PURE = new KeyValue(KEY_STROKE_CONTROL); + + /** + * The Constant KEY_TEXT_ANTIALIASING - text antialiasing hint key. + */ + public static final Key KEY_TEXT_ANTIALIASING = new KeyImpl(9); + + /** + * The Constant VALUE_TEXT_ANTIALIAS_DEFAULT - text antialiasing hint key. + */ + public static final Object VALUE_TEXT_ANTIALIAS_DEFAULT = new KeyValue(KEY_TEXT_ANTIALIASING); + + /** + * The Constant VALUE_TEXT_ANTIALIAS_ON - text antialiasing hint key. + */ + public static final Object VALUE_TEXT_ANTIALIAS_ON = new KeyValue(KEY_TEXT_ANTIALIASING); + + /** + * The Constant VALUE_TEXT_ANTIALIAS_OFF - text antialiasing hint key. + */ + public static final Object VALUE_TEXT_ANTIALIAS_OFF = new KeyValue(KEY_TEXT_ANTIALIASING); + + /** The map. */ + private HashMap map = new HashMap(); + + /** + * Instantiates a new rendering hints object from specified Map object with + * defined key/value pairs or null for empty RenderingHints. + * + * @param map + * the Map object with defined key/value pairs or null for empty + * RenderingHints. + */ + public RenderingHints(Map map) { + super(); + if (map != null) { + putAll(map); + } + } + + /** + * Instantiates a new rendering hints object with the specified key/value + * pair. + * + * @param key + * the key of hint property. + * @param value + * the value of hint property. + */ + public RenderingHints(Key key, Object value) { + super(); + put(key, value); + } + + /** + * Adds the properties represented by key/value pairs from the specified + * RenderingHints object to current object. + * + * @param hints + * the RenderingHints to be added. + */ + public void add(RenderingHints hints) { + map.putAll(hints.map); + } + + /** + * Puts the specified value to the specified key. Neither the key nor the + * value can be null. + * + * @param key + * the rendering hint key. + * @param value + * the rendering hint value. + * @return the previous rendering hint value assigned to the key or null. + */ + public Object put(Object key, Object value) { + if (!((Key)key).isCompatibleValue(value)) { + throw new IllegalArgumentException(); + } + + return map.put(key, value); + } + + /** + * Removes the specified key and corresponding value from the RenderingHints + * object. + * + * @param key + * the specified hint key to be removed. + * @return the object of previous rendering hint value which is assigned to + * the specified key, or null. + */ + public Object remove(Object key) { + return map.remove(key); + } + + /** + * Gets the value assigned to the specified key. + * + * @param key + * the rendering hint key. + * @return the object assigned to the specified key. + */ + public Object get(Object key) { + return map.get(key); + } + + /** + * Returns a set of rendering hints keys for current RenderingHints object. + * + * @return the set of rendering hints keys. + */ + public Set keySet() { + return map.keySet(); + } + + /** + * Returns a set of Map.Entry objects which contain current RenderingHint + * key/value pairs. + * + * @return the a set of mapped RenderingHint key/value pairs. + */ + public Set> entrySet() { + return map.entrySet(); + } + + /** + * Puts all of the preferences from the specified Map into the current + * RenderingHints object. These mappings replace all existing preferences. + * + * @param m + * the specified Map of preferences. + */ + public void putAll(Map m) { + if (m instanceof RenderingHints) { + map.putAll(((RenderingHints)m).map); + } else { + Set entries = m.entrySet(); + + if (entries != null) { + Iterator it = entries.iterator(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry)it.next(); + Key key = (Key)entry.getKey(); + Object val = entry.getValue(); + put(key, val); + } + } + } + } + + /** + * Returns a Collection of values contained in current RenderingHints + * object. + * + * @return the Collection of RenderingHints's values. + */ + public Collection values() { + return map.values(); + } + + /** + * Checks whether or not current RenderingHints object contains at least one + * the value which is equal to the specified Object. + * + * @param value + * the specified Object. + * @return true, if the specified object is assigned to at least one + * RenderingHint's key, false otherwise. + */ + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + /** + * Checks whether or not current RenderingHints object contains the key + * which is equal to the specified Object. + * + * @param key + * the specified Object. + * @return true, if the RenderingHints object contains the specified Object + * as a key, false otherwise. + */ + public boolean containsKey(Object key) { + if (key == null) { + throw new NullPointerException(); + } + + return map.containsKey(key); + } + + /** + * Checks whether or not the RenderingHints object contains any key/value + * pairs. + * + * @return true, if the RenderingHints object is empty, false otherwise. + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Clears the RenderingHints of all key/value pairs. + */ + public void clear() { + map.clear(); + } + + /** + * Returns the number of key/value pairs in the RenderingHints. + * + * @return the number of key/value pairs. + */ + public int size() { + return map.size(); + } + + /** + * Compares the RenderingHints object with the specified object. + * + * @param o + * the specified Object to be compared. + * @return true, if the Object is a Map whose key/value pairs match this + * RenderingHints' key/value pairs, false otherwise. + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Map)) { + return false; + } + + Map m = (Map)o; + Set keys = keySet(); + if (!keys.equals(m.keySet())) { + return false; + } + + Iterator it = keys.iterator(); + while (it.hasNext()) { + Key key = (Key)it.next(); + Object v1 = get(key); + Object v2 = m.get(key); + if (!(v1 == null ? v2 == null : v1.equals(v2))) { + return false; + } + } + return true; + } + + /** + * Returns the hash code for this RenderingHints object. + * + * @return the hash code for this RenderingHints object. + */ + @Override + public int hashCode() { + return map.hashCode(); + } + + /** + * Returns the clone of the RenderingHints object with the same contents. + * + * @return the clone of the RenderingHints instance. + */ + @SuppressWarnings("unchecked") + @Override + public Object clone() { + RenderingHints clone = new RenderingHints(null); + clone.map = (HashMap)this.map.clone(); + return clone; + } + + /** + * Returns the string representation of the RenderingHints object. + * + * @return the String object which represents RenderingHints object. + */ + @Override + public String toString() { + return "RenderingHints[" + map.toString() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * The RenderingHints.Key class is abstract and defines a base type for all + * RenderingHints keys. + * + * @since Android 1.0 + */ + public abstract static class Key { + + /** The key. */ + private final int key; + + /** + * Instantiates a new key with unique integer identifier. No two objects + * of the same subclass with the same integer key can be instantiated. + * + * @param key + * the unique key. + */ + protected Key(int key) { + this.key = key; + } + + /** + * Compares the Key object with the specified object. + * + * @param o + * the specified Object to be compared. + * @return true, if the Key is equal to the specified object, false + * otherwise. + */ + @Override + public final boolean equals(Object o) { + return this == o; + } + + /** + * Returns the hash code for this Key object. + * + * @return the hash code for this Key object. + */ + @Override + public final int hashCode() { + return System.identityHashCode(this); + } + + /** + * Returns integer unique key with which this Key object has been + * instantiated. + * + * @return the integer unique key with which this Key object has been + * instantiated. + */ + protected final int intKey() { + return key; + } + + /** + * Checks whether or not specified value is compatible with the Key. + * + * @param val + * the Object. + * @return true, if the specified value is compatible with the Key, + * false otherwise. + */ + public abstract boolean isCompatibleValue(Object val); + } + + /** + * Private implementation of Key class. + */ + private static class KeyImpl extends Key { + + /** + * Instantiates a new key implementation. + * + * @param key + * the key. + */ + protected KeyImpl(int key) { + super(key); + } + + @Override + public boolean isCompatibleValue(Object val) { + if (!(val instanceof KeyValue)) { + return false; + } + + return ((KeyValue)val).key == this; + } + } + + /** + * Private class KeyValue is used as value for Key class instance. + */ + private static class KeyValue { + + /** + * The key. + */ + private final Key key; + + /** + * Instantiates a new key value. + * + * @param key + * the key. + */ + protected KeyValue(Key key) { + this.key = key; + } + } +} diff --git a/awt/java/awt/Shape.java b/awt/java/awt/Shape.java new file mode 100644 index 000000000..59bc623b8 --- /dev/null +++ b/awt/java/awt/Shape.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * The Shape interface defines a geometric shape defined by a boundary (outline) + * path. The path outline can be accessed through a PathIterator object. The + * Shape interface provides methods for obtaining the bounding box (which is the + * smallest rectangle containing the shape and for obtaining a PathIterator + * object for current Shape, as well as utility methods which determine if the + * Shape contains or intersects a Rectangle or contains a Point. + * + * @since Android 1.0 + */ +public interface Shape { + + /** + * Checks whether or not the point with specified coordinates lies inside + * the Shape. + * + * @param x + * the X coordinate. + * @param y + * the Y coordinate. + * @return true, if the specified coordinates lie inside the Shape, false + * otherwise. + */ + public boolean contains(double x, double y); + + /** + * Checks whether or not the rectangle with specified [x, y, width, height] + * parameters lies inside the Shape. + * + * @param x + * the X double coordinate of the rectangle's upper left corner. + * @param y + * the Y double coordinate of the rectangle's upper left corner. + * @param w + * the width of rectangle. + * @param h + * the height of rectangle. + * @return true, if the specified rectangle lies inside the Shape, false + * otherwise. + */ + public boolean contains(double x, double y, double w, double h); + + /** + * Checks whether or not the specified Point2D lies inside the Shape. + * + * @param point + * the Point2D object. + * @return true, if the specified Point2D lies inside the Shape, false + * otherwise. + */ + public boolean contains(Point2D point); + + /** + * Checks whether or not the specified rectangle lies inside the Shape. + * + * @param r + * the Rectangle2D object. + * @return true, if the specified rectangle lies inside the Shape, false + * otherwise. + */ + public boolean contains(Rectangle2D r); + + /** + * Gets the bounding rectangle of the Shape. The bounding rectangle is the + * smallest rectangle which contains the Shape. + * + * @return the bounding rectangle of the Shape. + */ + public Rectangle getBounds(); + + /** + * Gets the Rectangle2D which represents Shape bounds. The bounding + * rectangle is the smallest rectangle which contains the Shape. + * + * @return the bounding rectangle of the Shape. + */ + public Rectangle2D getBounds2D(); + + /** + * Gets the PathIterator object of the Shape which provides access to the + * shape's boundary modified by the specified AffineTransform. + * + * @param at + * the specified AffineTransform object or null. + * @return PathIterator object for the Shape. + */ + public PathIterator getPathIterator(AffineTransform at); + + /** + * Gets the PathIterator object of the Shape which provides access to the + * coordinates of the shapes boundary modified by the specified + * AffineTransform. The flatness parameter defines the amount of subdivision + * of the curved segments and specifies the maximum distance which every + * point on the unflattened transformed curve can deviate from the returned + * flattened path segments. + * + * @param at + * the specified AffineTransform object or null. + * @param flatness + * the maximum number of the control points for a given curve + * which varies from colinear before a subdivided curve is + * replaced by a straight line connecting the endpoints. + * @return PathIterator object for the Shape. + */ + public PathIterator getPathIterator(AffineTransform at, double flatness); + + /** + * Checks whether or not the interior of rectangular specified by [x, y, + * width, height] parameters intersects the interior of the Shape. + * + * @param x + * the X double coordinate of the rectangle's upper left corner. + * @param y + * the Y double coordinate of the rectangle's upper left corner. + * @param w + * the width of rectangle. + * @param h + * the height of rectangle. + * @return true, if the rectangle specified by [x, y, width, height] + * parameters intersects the interior of the Shape, false otherwise. + */ + public boolean intersects(double x, double y, double w, double h); + + /** + * Checks whether or not the interior of rectangle specified by Rectangle2D + * object intersects the interior of the Shape. + * + * @param r + * the Rectangle2D object. + * @return true, if the Rectangle2D intersects the interior of the Shape, + * otherwise false. + */ + public boolean intersects(Rectangle2D r); +} diff --git a/awt/java/awt/Stroke.java b/awt/java/awt/Stroke.java new file mode 100644 index 000000000..6d17a23bd --- /dev/null +++ b/awt/java/awt/Stroke.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt; + +/** + * The Stroke interface gives a pen style to be used by the Graphics2D + * interface. It provides a means for getting a stroked version of a shape, + * which is the version that is suitable for drawing via the Graphics2D + * interface. Stroking a shape gives the shape's outline a width or drawing + * style. + *

+ * The Draw methods from Graphics2D interface should use the Stroke object for + * rendering the shape's outline. The stroke should be set by + * setStroke(java.awt.Stroke) method of the Graphics2D interface. + * + * @see java.awt.Graphics2D#setStroke(java.awt.Stroke) + * @since Android 1.0 + */ +public interface Stroke { + + /** + * Creates the stroked shape, which is the version that is suitable for + * drawing via the Graphics2D interface. Stroking a shape gives the shape's + * outline a width or drawing style. + * + * @param p + * the original shape. + * @return the stroked shape. + */ + public Shape createStrokedShape(Shape p); +} diff --git a/awt/java/awt/Toolkit.java b/awt/java/awt/Toolkit.java new file mode 100644 index 000000000..e38d52402 --- /dev/null +++ b/awt/java/awt/Toolkit.java @@ -0,0 +1,1444 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.awt; + +import java.awt.event.AWTEventListener; +import java.awt.event.AWTEventListenerProxy; +import java.awt.event.InputEvent; +import java.awt.im.InputMethodHighlight; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.peer.FontPeer; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.EventListener; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.Properties; +import java.util.ResourceBundle; + +import org.apache.harmony.awt.ChoiceStyle; +import org.apache.harmony.awt.ComponentInternals; +import org.apache.harmony.awt.ContextStorage; +import org.apache.harmony.awt.ReadOnlyIterator; +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.awt.wtk.CreationParams; +import org.apache.harmony.awt.wtk.GraphicsFactory; +import org.apache.harmony.awt.wtk.NativeCursor; + +import org.apache.harmony.awt.wtk.NativeEventQueue; +import org.apache.harmony.awt.wtk.NativeEventThread; +import org.apache.harmony.awt.wtk.ShutdownWatchdog; +import org.apache.harmony.awt.wtk.Synchronizer; +import org.apache.harmony.awt.wtk.WTK; +import org.apache.harmony.luni.util.NotImplementedException; + +/** + * The Toolkit class is the representation of the platform-specific Abstract + * Window Toolkit implementation. Toolkit's subclasses are used to bind the + * various components to particular native toolkit implementations. + * + * @since Android 1.0 + */ +public abstract class Toolkit { + + /** + * The Constant RECOURCE_PATH. + */ + private static final String RECOURCE_PATH = "org.apache.harmony.awt.resources.AWTProperties"; //$NON-NLS-1$ + + /** + * The Constant properties. + */ + private static final ResourceBundle properties = loadResources(RECOURCE_PATH); + + /** + * The dispatcher. + */ + Dispatcher dispatcher; + + /** + * The system event queue core. + */ + private EventQueueCore systemEventQueueCore; + + /** + * The dispatch thread. + */ + EventDispatchThread dispatchThread; + + /** + * The native thread. + */ + NativeEventThread nativeThread; + + /** + * The AWT events manager. + */ + protected AWTEventsManager awtEventsManager; + + /** + * The Class AWTTreeLock. + */ + private class AWTTreeLock { + } + + /** + * The AWT tree lock. + */ + final Object awtTreeLock = new AWTTreeLock(); + + /** + * The synchronizer. + */ + private final Synchronizer synchronizer = ContextStorage.getSynchronizer(); + + /** + * The shutdown watchdog. + */ + final ShutdownWatchdog shutdownWatchdog = new ShutdownWatchdog(); + + /** + * The auto number. + */ + final AutoNumber autoNumber = new AutoNumber(); + + /** + * The event type lookup. + */ + final AWTEvent.EventTypeLookup eventTypeLookup = new AWTEvent.EventTypeLookup(); + + /** + * The b dynamic layout set. + */ + private boolean bDynamicLayoutSet = true; + + /** + * The set of desktop properties that user set directly. + */ + private final HashSet userPropSet = new HashSet(); + + /** + * The desktop properties. + */ + protected Map desktopProperties; + + /** + * The desktop props support. + */ + protected PropertyChangeSupport desktopPropsSupport; + + /** + * For this component the native window is being created It is used in the + * callback-driven window creation (e.g. on Windows in the handler of + * WM_CREATE event) to establish the connection between this component and + * its native window. + */ + private Object recentNativeWindowComponent; + + /** + * The wtk. + */ + private WTK wtk; + + /** + * The Class ComponentInternalsImpl. + * + * @since Android 1.0 + */ + protected final class ComponentInternalsImpl extends ComponentInternals { + + /** + * Shutdown. + */ + @Override + public void shutdown() { + dispatchThread.shutdown(); + } + + /** + * Sets the desktop property to the specified value and fires a property + * change event. + * + * @param name + * the name of property. + * @param value + * the new value of property. + */ + @Override + public void setDesktopProperty(String name, Object value) { + Toolkit.this.setDesktopProperty(name, value); + } + } + + /** + * A lot of methods must throw HeadlessException if + * GraphicsEnvironment.isHeadless() returns true. + * + * @throws HeadlessException + * the headless exception. + */ + static void checkHeadless() throws HeadlessException { + if (GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance()) + throw new HeadlessException(); + } + + /** + * Lock AWT. + */ + final void lockAWT() { + synchronizer.lock(); + } + + /** + * Static lock AWT. + */ + static final void staticLockAWT() { + ContextStorage.getSynchronizer().lock(); + } + + /** + * Unlock AWT. + */ + final void unlockAWT() { + synchronizer.unlock(); + } + + /** + * Static unlock AWT. + */ + static final void staticUnlockAWT() { + ContextStorage.getSynchronizer().unlock(); + } + + /** + * InvokeAndWait under AWT lock. W/o this method system can hang up. Added + * to support modality (Dialog.show() & PopupMenu.show()) from not event + * dispatch thread. Use in other cases is not recommended. Still can be + * called only for whole API methods that cannot be called from other + * classes API methods. Examples: show() for modal dialogs - correct, only + * user can call it, directly or through setVisible(true) setBounds() for + * components - incorrect, setBounds() can be called from layoutContainer() + * for layout managers + * + * @param runnable + * the runnable. + * @throws InterruptedException + * the interrupted exception. + * @throws InvocationTargetException + * the invocation target exception. + */ + final void unsafeInvokeAndWait(Runnable runnable) throws InterruptedException, + InvocationTargetException { + synchronizer.storeStateAndFree(); + try { + EventQueue.invokeAndWait(runnable); + } finally { + synchronizer.lockAndRestoreState(); + } + } + + /** + * Gets the synchronizer. + * + * @return the synchronizer. + */ + final Synchronizer getSynchronizer() { + return synchronizer; + } + + /** + * Gets the wTK. + * + * @return the wTK. + */ + final WTK getWTK() { + return wtk; + } + + /** + * Gets the property with the specified key and default value. This method + * returns the defValue if the property is not found. + * + * @param propName + * the name of property. + * @param defVal + * the default value. + * @return the property value. + */ + public static String getProperty(String propName, String defVal) { + if (propName == null) { + // awt.7D=Property name is null + throw new NullPointerException(Messages.getString("awt.7D")); //$NON-NLS-1$ + } + staticLockAWT(); + try { + String retVal = null; + if (properties != null) { + try { + retVal = properties.getString(propName); + } catch (MissingResourceException e) { + } catch (ClassCastException e) { + } + } + return (retVal == null) ? defVal : retVal; + } finally { + staticUnlockAWT(); + } + } + + /** + * Gets the default Toolkit. + * + * @return the default Toolkit. + */ + public static Toolkit getDefaultToolkit() { + synchronized (ContextStorage.getContextLock()) { + if (ContextStorage.shutdownPending()) { + return null; + } + Toolkit defToolkit = ContextStorage.getDefaultToolkit(); + if (defToolkit != null) { + return defToolkit; + } + staticLockAWT(); + try { + defToolkit = GraphicsEnvironment.isHeadless() ? new HeadlessToolkit() + : new ToolkitImpl(); + ContextStorage.setDefaultToolkit(defToolkit); + return defToolkit; + } finally { + staticUnlockAWT(); + } + // TODO: read system property named awt.toolkit + // and create an instance of the specified class, + // by default use ToolkitImpl + } + } + + /** + * Gets the default Font. + * + * @return the default Font for Toolkit. + */ + Font getDefaultFont() { + return wtk.getSystemProperties().getDefaultFont(); + } + + /** + * Load resources. + * + * @param path + * the path. + * @return the resource bundle. + */ + private static ResourceBundle loadResources(String path) { + try { + return ResourceBundle.getBundle(path); + } catch (MissingResourceException e) { + return null; + } + } + + /** + * Gets the wTK class name. + * + * @return the wTK class name. + */ + private static String getWTKClassName() { + return "com.android.internal.awt.AndroidWTK"; + } + + /** + * Gets the component by id. + * + * @param id + * the id. + * @return the component by id. + */ + Component getComponentById(long id) { + if (id == 0) { + return null; + } + return null; + } + + /** + * Gets the GraphicsFactory. + * + * @return the GraphicsFactory object. + */ + public GraphicsFactory getGraphicsFactory() { + return wtk.getGraphicsFactory(); + } + + /** + * Instantiates a new toolkit. + */ + public Toolkit() { + init(); + } + + /** + * Initiates AWT. + */ + protected void init() { + lockAWT(); + try { + ComponentInternals.setComponentInternals(new ComponentInternalsImpl()); + new EventQueue(this); // create the system EventQueue + dispatcher = new Dispatcher(this); + final String className = getWTKClassName(); + desktopProperties = new HashMap(); + desktopPropsSupport = new PropertyChangeSupport(this); + awtEventsManager = new AWTEventsManager(); + dispatchThread = new EventDispatchThread(this, dispatcher); + nativeThread = new NativeEventThread(); + NativeEventThread.Init init = new NativeEventThread.Init() { + public WTK init() { + wtk = createWTK(className); + wtk.getNativeEventQueue().setShutdownWatchdog(shutdownWatchdog); + synchronizer.setEnvironment(wtk, dispatchThread); + ContextStorage.setWTK(wtk); + return wtk; + } + }; + nativeThread.start(init); + dispatchThread.start(); + wtk.getNativeEventQueue().awake(); + } finally { + unlockAWT(); + } + } + + /** + * Synchronizes this toolkit's graphics. + */ + public abstract void sync(); + + /** + * Returns the construction status of a specified image that is being + * created. + * + * @param a0 + * the image to be checked. + * @param a1 + * the width of scaled image for which the status is being + * checked or -1. + * @param a2 + * the height of scaled image for which the status is being + * checked or -1. + * @param a3 + * the ImageObserver object to be notified while the image is + * being prepared. + * @return the ImageObserver flags which give the current state of the image + * data. + */ + public abstract int checkImage(Image a0, int a1, int a2, ImageObserver a3); + + /** + * Creates the image with the specified ImageProducer. + * + * @param a0 + * the ImageProducer to be used for image creation. + * @return the image with the specified ImageProducer. + */ + public abstract Image createImage(ImageProducer a0); + + /** + * Creates the image from the specified byte array, offset and length. The + * byte array should contain data with image format supported by Toolkit + * such as JPEG, GIF, or PNG. + * + * @param a0 + * the byte array with the image data. + * @param a1 + * the offset of the beginning the image data in the byte array. + * @param a2 + * the length of the image data in the byte array. + * @return the created Image. + */ + public abstract Image createImage(byte[] a0, int a1, int a2); + + /** + * Creates the image using image data from the specified URL. + * + * @param a0 + * the URL for extracting image data. + * @return the Image. + */ + public abstract Image createImage(URL a0); + + /** + * Creates the image using image data from the specified file. + * + * @param a0 + * the file name which contains image data of supported format. + * @return the Image. + */ + public abstract Image createImage(String a0); + + /** + * Gets the color model. + * + * @return the ColorModel of Toolkit's screen. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + public abstract ColorModel getColorModel() throws HeadlessException; + + /** + * Gets the screen device metrics for the specified font. + * + * @param font + * the Font. + * @return the FontMetrics for the specified Font. + * @deprecated Use getLineMetrics method from Font class. + */ + + @Deprecated + public abstract FontMetrics getFontMetrics(Font font); + + /** + * Prepares the specified image for rendering on the screen with the + * specified size. + * + * @param a0 + * the Image to be prepared. + * @param a1 + * the width of the screen representation or -1 for the current + * screen. + * @param a2 + * the height of the screen representation or -1 for the current + * screen. + * @param a3 + * the ImageObserver object to be notified as soon as the image + * is prepared. + * @return true, if image is fully prepared, false otherwise. + */ + public abstract boolean prepareImage(Image a0, int a1, int a2, ImageObserver a3); + + /** + * Creates an audio beep. + */ + public abstract void beep(); + + /** + * Returns the array of font names which are available in this Toolkit. + * + * @return the array of font names which are available in this Toolkit. + * @deprecated use GraphicsEnvironment.getAvailableFontFamilyNames() method. + */ + @Deprecated + public abstract String[] getFontList(); + + /** + * Gets the the Font implementation using the specified peer interface. + * + * @param a0 + * the Font name to be implemented. + * @param a1 + * the the font style: PLAIN, BOLD, ITALIC. + * @return the FontPeer implementation of the specified Font. + * @deprecated use java.awt.GraphicsEnvironment.getAllFonts method. + */ + + @Deprecated + protected abstract FontPeer getFontPeer(String a0, int a1); + + /** + * Gets the image from the specified file which contains image data in a + * supported image format (such as JPEG, GIF, or PNG); this method should + * return the same Image for multiple calls of this method with the same + * image file name. + * + * @param a0 + * the file name which contains image data in a supported image + * format (such as JPEG, GIF, or PNG). + * @return the Image. + */ + public abstract Image getImage(String a0); + + /** + * Gets the image from the specified URL which contains image data in a + * supported image format (such as JPEG, GIF, or PNG); this method should + * return the same Image for multiple calls of this method with the same + * image URL. + * + * @param a0 + * the URL which contains image data in a supported image format + * (such as JPEG, GIF, or PNG). + * @return the Image. + */ + public abstract Image getImage(URL a0); + + /** + * Gets the screen resolution. + * + * @return the screen resolution. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + public abstract int getScreenResolution() throws HeadlessException; + + /** + * Gets the screen size. + * + * @return a Dimension object containing the width and height of the screen. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + public abstract Dimension getScreenSize() throws HeadlessException; + + /** + * Gets the EventQueue instance without checking access. + * + * @return the system EventQueue. + */ + protected abstract EventQueue getSystemEventQueueImpl(); + + /** + * Returns a map of text attributes for the abstract level description of + * the specified input method highlight, or null if no mapping is found. + * + * @param highlight + * the InputMethodHighlight. + * @return the Map. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + public abstract Map mapInputMethodHighlight( + InputMethodHighlight highlight) throws HeadlessException; + + /** + * Map input method highlight impl. + * + * @param highlight + * the highlight. + * @return the map. + * @throws HeadlessException + * the headless exception. + */ + Map mapInputMethodHighlightImpl(InputMethodHighlight highlight) + throws HeadlessException { + HashMap map = new HashMap(); + wtk.getSystemProperties().mapInputMethodHighlight(highlight, map); + return Collections. unmodifiableMap(map); + } + + /** + * Adds the specified PropertyChangeListener listener for the specified + * property. + * + * @param propName + * the property name for which the specified + * PropertyChangeListener will be added. + * @param l + * the PropertyChangeListener object. + */ + public void addPropertyChangeListener(String propName, PropertyChangeListener l) { + lockAWT(); + try { + if (desktopProperties.isEmpty()) { + initializeDesktopProperties(); + } + } finally { + unlockAWT(); + } + if (l != null) { // there is no guarantee that null listener will not be + // added + desktopPropsSupport.addPropertyChangeListener(propName, l); + } + } + + /** + * Returns an array of the property change listeners registered with this + * Toolkit. + * + * @return an array of the property change listeners registered with this + * Toolkit. + */ + public PropertyChangeListener[] getPropertyChangeListeners() { + return desktopPropsSupport.getPropertyChangeListeners(); + } + + /** + * Returns an array of the property change listeners registered with this + * Toolkit for notification regarding the specified property. + * + * @param propName + * the property name for which the PropertyChangeListener was + * registered. + * @return the array of PropertyChangeListeners registered for the specified + * property name. + */ + public PropertyChangeListener[] getPropertyChangeListeners(String propName) { + return desktopPropsSupport.getPropertyChangeListeners(propName); + } + + /** + * Removes the specified property change listener registered for the + * specified property name. + * + * @param propName + * the property name. + * @param l + * the PropertyChangeListener registered for the specified + * property name. + */ + public void removePropertyChangeListener(String propName, PropertyChangeListener l) { + desktopPropsSupport.removePropertyChangeListener(propName, l); + } + + /** + * Creates a custom cursor with the specified Image, hot spot, and cursor + * description. + * + * @param img + * the image of activated cursor. + * @param hotSpot + * the Point giving the coordinates of the cursor's hot spot. + * @param name + * the cursor description. + * @return the cursor with the specified Image, hot spot, and cursor + * description. + * @throws IndexOutOfBoundsException + * if the hot spot values are outside the bounds of the cursor. + * @throws HeadlessException + * if isHeadless() method of GraphicsEnvironment class returns + * true. + */ + public Cursor createCustomCursor(Image img, Point hotSpot, String name) + throws IndexOutOfBoundsException, HeadlessException { + lockAWT(); + try { + int w = img.getWidth(null), x = hotSpot.x; + int h = img.getHeight(null), y = hotSpot.y; + if (x < 0 || x >= w || y < 0 || y >= h) { + // awt.7E=invalid hotSpot + throw new IndexOutOfBoundsException(Messages.getString("awt.7E")); //$NON-NLS-1$ + } + return new Cursor(name, img, hotSpot); + } finally { + unlockAWT(); + } + } + + /** + * Returns the supported cursor dimension which is closest to the specified + * width and height. If the Toolkit only supports a single cursor size, this + * method should return the supported cursor size. If custom cursor is not + * supported, a dimension of 0, 0 should be returned. + * + * @param prefWidth + * the preferred cursor width. + * @param prefHeight + * the preferred cursor height. + * @return the supported cursor dimension which is closest to the specified + * width and height. + * @throws HeadlessException + * if GraphicsEnvironment.isHeadless() returns true. + */ + public Dimension getBestCursorSize(int prefWidth, int prefHeight) throws HeadlessException { + lockAWT(); + try { + return wtk.getCursorFactory().getBestCursorSize(prefWidth, prefHeight); + } finally { + unlockAWT(); + } + } + + /** + * Gets the value for the specified desktop property. + * + * @param propName + * the property name. + * @return the Object that is the property's value. + */ + public final Object getDesktopProperty(String propName) { + lockAWT(); + try { + if (desktopProperties.isEmpty()) { + initializeDesktopProperties(); + } + if (propName.equals("awt.dynamicLayoutSupported")) { //$NON-NLS-1$ + // dynamicLayoutSupported is special case + return Boolean.valueOf(isDynamicLayoutActive()); + } + Object val = desktopProperties.get(propName); + if (val == null) { + // try to lazily load prop value + // just for compatibility, our lazilyLoad is empty + val = lazilyLoadDesktopProperty(propName); + } + return val; + } finally { + unlockAWT(); + } + } + + /** + * Returns the locking key state for the specified key. + * + * @param a0 + * the key code: VK_CAPS_LOCK, VK_NUM_LOCK, VK_SCROLL_LOCK, or + * VK_KANA_LOCK. + * @return true if the specified key code is in the locked state, false + * otherwise. + * @throws UnsupportedOperationException + * if the state of this key can't be retrieved, or if the + * keyboard doesn't have this key. + * @throws NotImplementedException + * if this method is not implemented. + */ + public boolean getLockingKeyState(int a0) throws UnsupportedOperationException, + org.apache.harmony.luni.util.NotImplementedException { + lockAWT(); + try { + } finally { + unlockAWT(); + } + if (true) { + throw new RuntimeException("Method is not implemented"); //TODO: implement //$NON-NLS-1$ + } + return true; + } + + /** + * Returns the maximum number of colors which the Toolkit supports for + * custom cursor. + * + * @return the maximum cursor colors. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + public int getMaximumCursorColors() throws HeadlessException { + lockAWT(); + try { + return wtk.getCursorFactory().getMaximumCursorColors(); + } finally { + unlockAWT(); + } + } + + /** + * Gets the menu shortcut key mask. + * + * @return the menu shortcut key mask. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + public int getMenuShortcutKeyMask() throws HeadlessException { + lockAWT(); + try { + return InputEvent.CTRL_MASK; + } finally { + unlockAWT(); + } + } + + /** + * Gets the screen insets. + * + * @param gc + * the GraphicsConfiguration. + * @return the insets of this toolkit. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + public Insets getScreenInsets(GraphicsConfiguration gc) throws HeadlessException { + if (gc == null) { + throw new NullPointerException(); + } + lockAWT(); + try { + return new Insets(0, 0, 0, 0); // TODO: get real screen insets + } finally { + unlockAWT(); + } + } + + /** + * Gets the system EventQueue instance. If the default implementation of + * checkAwtEventQueueAccess is used, then this results of a call to the + * security manager's checkPermission method with an + * AWTPermission("accessEventQueue") permission. + * + * @return the system EventQueue instance. + */ + public final EventQueue getSystemEventQueue() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkAwtEventQueueAccess(); + } + return getSystemEventQueueImpl(); + } + + /** + * Gets the system event queue core. + * + * @return the system event queue core. + */ + EventQueueCore getSystemEventQueueCore() { + return systemEventQueueCore; + } + + /** + * Sets the system event queue core. + * + * @param core + * the new system event queue core. + */ + void setSystemEventQueueCore(EventQueueCore core) { + systemEventQueueCore = core; + } + + /** + * Initialize the desktop properties. + */ + protected void initializeDesktopProperties() { + lockAWT(); + try { + wtk.getSystemProperties().init(desktopProperties); + } finally { + unlockAWT(); + } + } + + /** + * Checks if dynamic layout of Containers is active or not. + * + * @return true, if is dynamic layout of Containers is active, false + * otherwise. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + public boolean isDynamicLayoutActive() throws HeadlessException { + lockAWT(); + try { + // always return true + return true; + } finally { + unlockAWT(); + } + } + + /** + * Returns if the layout of Containers is checked dynamically during + * resizing, or statically after resizing is completed. + * + * @return true, if if the layout of Containers is checked dynamically + * during resizing; false, if the layout of Containers is checked + * statically after resizing is completed. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + protected boolean isDynamicLayoutSet() throws HeadlessException { + lockAWT(); + try { + return bDynamicLayoutSet; + } finally { + unlockAWT(); + } + } + + /** + * Checks if the specified frame state is supported by Toolkit or not. + * + * @param state + * the frame state. + * @return true, if frame state is supported, false otherwise. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + public boolean isFrameStateSupported(int state) throws HeadlessException { + lockAWT(); + try { + return wtk.getWindowFactory().isWindowStateSupported(state); + } finally { + unlockAWT(); + } + } + + /** + * Loads the value of the desktop property with the specified property name. + * + * @param propName + * the property name. + * @return the desktop property values. + */ + protected Object lazilyLoadDesktopProperty(String propName) { + return null; + } + + /** + * Loads the current system color values to the specified array. + * + * @param colors + * the array where the current system color values are written by + * this method. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + protected void loadSystemColors(int[] colors) throws HeadlessException { + lockAWT(); + try { + } finally { + unlockAWT(); + } + } + + /** + * Sets the value of the desktop property with the specified name. + * + * @param propName + * the property's name. + * @param value + * the property's value. + */ + protected final void setDesktopProperty(String propName, Object value) { + Object oldVal; + lockAWT(); + try { + oldVal = getDesktopProperty(propName); + userPropSet.add(propName); + desktopProperties.put(propName, value); + } finally { + unlockAWT(); + } + desktopPropsSupport.firePropertyChange(propName, oldVal, value); + } + + /** + * Sets the layout state, whether the Container layout is checked + * dynamically during resizing, or statically after resizing is completed. + * + * @param dynamic + * the new dynamic layout state - if true the layout of + * Containers is checked dynamically during resizing, if false - + * statically after resizing is completed. + * @throws HeadlessException + * if the GraphicsEnvironment.isHeadless() method returns true. + */ + public void setDynamicLayout(boolean dynamic) throws HeadlessException { + lockAWT(); + try { + bDynamicLayoutSet = dynamic; + } finally { + unlockAWT(); + } + } + + /** + * Sets the locking key state for the specified key code. + * + * @param a0 + * the key code: VK_CAPS_LOCK, VK_NUM_LOCK, VK_SCROLL_LOCK, or + * VK_KANA_LOCK. + * @param a1 + * the state - true to set the specified key code to the locked + * state, false - to unlock it. + * @throws UnsupportedOperationException + * if the state of this key can't be set, or if the keyboard + * doesn't have this key. + * @throws NotImplementedException + * if this method is not implemented. + */ + public void setLockingKeyState(int a0, boolean a1) throws UnsupportedOperationException, + org.apache.harmony.luni.util.NotImplementedException { + lockAWT(); + try { + } finally { + unlockAWT(); + } + if (true) { + throw new RuntimeException("Method is not implemented"); //TODO: implement //$NON-NLS-1$ + } + return; + } + + /** + * On queue empty. + */ + void onQueueEmpty() { + throw new RuntimeException("Not implemented!"); + } + + /** + * Creates the wtk. + * + * @param clsName + * the cls name. + * @return the wTK. + */ + private WTK createWTK(String clsName) { + WTK newWTK = null; + try { + newWTK = (WTK)Class.forName(clsName).newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return newWTK; + } + + /** + * Connect the component to its native window + * + * @param winId + * the id of native window just created. + */ + boolean onWindowCreated(long winId) { + return false; + } + + /** + * Gets the native event queue. + * + * @return the native event queue. + */ + NativeEventQueue getNativeEventQueue() { + return wtk.getNativeEventQueue(); + } + + /** + * Returns a shared instance of implementation of + * org.apache.harmony.awt.wtk.NativeCursor for current platform for. + * + * @param type + * the Java Cursor type. + * @return new instance of implementation of NativeCursor. + */ + NativeCursor createNativeCursor(int type) { + return wtk.getCursorFactory().getCursor(type); + } + + /** + * Returns a shared instance of implementation of + * org.apache.harmony.awt.wtk.NativeCursor for current platform for custom + * cursor + * + * @param img + * the img. + * @param hotSpot + * the hot spot. + * @param name + * the name. + * @return new instance of implementation of NativeCursor. + */ + NativeCursor createCustomNativeCursor(Image img, Point hotSpot, String name) { + return wtk.getCursorFactory().createCustomCursor(img, hotSpot.x, hotSpot.y); + } + + /** + * Adds an AWTEventListener to the Toolkit to listen for events of types + * corresponding to bits in the specified event mask. Event masks are + * defined in AWTEvent class. + * + * @param listener + * the AWTEventListener. + * @param eventMask + * the bitmask of event types. + */ + public void addAWTEventListener(AWTEventListener listener, long eventMask) { + lockAWT(); + try { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(awtEventsManager.permission); + } + awtEventsManager.addAWTEventListener(listener, eventMask); + } finally { + unlockAWT(); + } + } + + /** + * Removes the specified AWT event listener. + * + * @param listener + * the AWTEventListener to be removed. + */ + public void removeAWTEventListener(AWTEventListener listener) { + lockAWT(); + try { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(awtEventsManager.permission); + } + awtEventsManager.removeAWTEventListener(listener); + } finally { + unlockAWT(); + } + } + + /** + * Gets the array of all AWT event listeners registered with this Toolkit. + * + * @return the array of all AWT event listeners registered with this + * Toolkit. + */ + public AWTEventListener[] getAWTEventListeners() { + lockAWT(); + try { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(awtEventsManager.permission); + } + return awtEventsManager.getAWTEventListeners(); + } finally { + unlockAWT(); + } + } + + /** + * Returns the array of the AWT event listeners registered with this Toolkit + * for the event types corresponding to the specified event mask. + * + * @param eventMask + * the bit mask of event type. + * @return the array of the AWT event listeners registered in this Toolkit + * for the event types corresponding to the specified event mask. + */ + public AWTEventListener[] getAWTEventListeners(long eventMask) { + lockAWT(); + try { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(awtEventsManager.permission); + } + return awtEventsManager.getAWTEventListeners(eventMask); + } finally { + unlockAWT(); + } + } + + /** + * Dispatch AWT event. + * + * @param event + * the event. + */ + void dispatchAWTEvent(AWTEvent event) { + awtEventsManager.dispatchAWTEvent(event); + } + + /** + * The Class AWTEventsManager. + */ + final class AWTEventsManager { + + /** + * The permission. + */ + AWTPermission permission = new AWTPermission("listenToAllAWTEvents"); //$NON-NLS-1$ + + /** + * The listeners. + */ + private final AWTListenerList listeners = new AWTListenerList(); + + /** + * Adds the AWT event listener. + * + * @param listener + * the listener. + * @param eventMask + * the event mask. + */ + void addAWTEventListener(AWTEventListener listener, long eventMask) { + if (listener != null) { + listeners.addUserListener(new AWTEventListenerProxy(eventMask, listener)); + } + } + + /** + * Removes the AWT event listener. + * + * @param listener + * the listener. + */ + void removeAWTEventListener(AWTEventListener listener) { + if (listener != null) { + for (AWTEventListenerProxy proxy : listeners.getUserListeners()) { + if (listener == proxy.getListener()) { + listeners.removeUserListener(proxy); + return; + } + } + } + } + + /** + * Gets the AWT event listeners. + * + * @return the AWT event listeners. + */ + AWTEventListener[] getAWTEventListeners() { + HashSet listenersSet = new HashSet(); + for (AWTEventListenerProxy proxy : listeners.getUserListeners()) { + listenersSet.add(proxy.getListener()); + } + return listenersSet.toArray(new AWTEventListener[listenersSet.size()]); + } + + /** + * Gets the AWT event listeners. + * + * @param eventMask + * the event mask. + * @return the AWT event listeners. + */ + AWTEventListener[] getAWTEventListeners(long eventMask) { + HashSet listenersSet = new HashSet(); + for (AWTEventListenerProxy proxy : listeners.getUserListeners()) { + if ((proxy.getEventMask() & eventMask) == eventMask) { + listenersSet.add(proxy.getListener()); + } + } + return listenersSet.toArray(new AWTEventListener[listenersSet.size()]); + } + + /** + * Dispatch AWT event. + * + * @param event + * the event. + */ + void dispatchAWTEvent(AWTEvent event) { + AWTEvent.EventDescriptor descriptor = eventTypeLookup.getEventDescriptor(event); + if (descriptor == null) { + return; + } + for (AWTEventListenerProxy proxy : listeners.getUserListeners()) { + if ((proxy.getEventMask() & descriptor.eventMask) != 0) { + proxy.eventDispatched(event); + } + } + } + } + + /** + * The Class AutoNumber. + */ + static final class AutoNumber { + + /** + * The next component. + */ + int nextComponent = 0; + + /** + * The next canvas. + */ + int nextCanvas = 0; + + /** + * The next panel. + */ + int nextPanel = 0; + + /** + * The next window. + */ + int nextWindow = 0; + + /** + * The next frame. + */ + int nextFrame = 0; + + /** + * The next dialog. + */ + int nextDialog = 0; + + /** + * The next button. + */ + int nextButton = 0; + + /** + * The next menu component. + */ + int nextMenuComponent = 0; + + /** + * The next label. + */ + int nextLabel = 0; + + /** + * The next check box. + */ + int nextCheckBox = 0; + + /** + * The next scrollbar. + */ + int nextScrollbar = 0; + + /** + * The next scroll pane. + */ + int nextScrollPane = 0; + + /** + * The next list. + */ + int nextList = 0; + + /** + * The next choice. + */ + int nextChoice = 0; + + /** + * The next file dialog. + */ + int nextFileDialog = 0; + + /** + * The next text area. + */ + int nextTextArea = 0; + + /** + * The next text field. + */ + int nextTextField = 0; + } + + private class Lock { + } + + /** + * The lock. + */ + private final Object lock = new Lock(); + +} diff --git a/awt/java/awt/ToolkitImpl.java b/awt/java/awt/ToolkitImpl.java new file mode 100644 index 000000000..5015aefed --- /dev/null +++ b/awt/java/awt/ToolkitImpl.java @@ -0,0 +1,255 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.awt; + +import java.awt.im.InputMethodHighlight; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.peer.*; +import java.io.Serializable; +import java.net.URL; +import java.util.Hashtable; +import java.util.Map; +import org.apache.harmony.awt.gl.image.*; +import org.apache.harmony.awt.wtk.GraphicsFactory; + +class ToolkitImpl extends Toolkit { + + static final Hashtable imageCache = new Hashtable(); + + @Override + public void sync() { + lockAWT(); + try { + } finally { + unlockAWT(); + } + } + + @Override + public int checkImage(Image image, int width, int height, ImageObserver observer) { + lockAWT(); + try { + if (width == 0 || height == 0) { + return ImageObserver.ALLBITS; + } + if (!(image instanceof OffscreenImage)) { + return ImageObserver.ALLBITS; + } + OffscreenImage oi = (OffscreenImage) image; + return oi.checkImage(observer); + } finally { + unlockAWT(); + } + } + + @Override + public Image createImage(ImageProducer producer) { + lockAWT(); + try { + return new OffscreenImage(producer); + } finally { + unlockAWT(); + } + } + + @Override + public Image createImage(byte[] imagedata, int imageoffset, int imagelength) { + lockAWT(); + try { + return new OffscreenImage(new ByteArrayDecodingImageSource(imagedata, imageoffset, + imagelength)); + } finally { + unlockAWT(); + } + } + + @Override + public Image createImage(URL url) { + lockAWT(); + try { + return new OffscreenImage(new URLDecodingImageSource(url)); + } finally { + unlockAWT(); + } + } + + @Override + public Image createImage(String filename) { + lockAWT(); + try { + return new OffscreenImage(new FileDecodingImageSource(filename)); + } finally { + unlockAWT(); + } + } + + @Override + public ColorModel getColorModel() { + lockAWT(); + try { + return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice() + .getDefaultConfiguration().getColorModel(); + } finally { + unlockAWT(); + } + } + + @SuppressWarnings("deprecation") + @Override + @Deprecated + public FontMetrics getFontMetrics(Font font) { + lockAWT(); + try { + GraphicsFactory gf = getGraphicsFactory(); + return gf.getFontMetrics(font); + } finally { + unlockAWT(); + } + } + + @Override + public boolean prepareImage(Image image, int width, int height, ImageObserver observer) { + lockAWT(); + try { + if (width == 0 || height == 0) { + return true; + } + if (!(image instanceof OffscreenImage)) { + return true; + } + OffscreenImage oi = (OffscreenImage) image; + return oi.prepareImage(observer); + } finally { + unlockAWT(); + } + } + + @Override + public void beep() { + lockAWT(); + try { + // ???AWT: is there nothing to be implemented here? + } finally { + unlockAWT(); + } + } + + @SuppressWarnings("deprecation") + @Override + @Deprecated + public String[] getFontList() { + lockAWT(); + try { + } finally { + unlockAWT(); + } + return null; + } + + @SuppressWarnings("deprecation") + @Override + @Deprecated + protected FontPeer getFontPeer(String a0, int a1) { + lockAWT(); + try { + return null; + } finally { + unlockAWT(); + } + } + + @Override + public Image getImage(String filename) { + return getImage(filename, this); + } + + static Image getImage(String filename, Toolkit toolkit) { + synchronized (imageCache) { + Image im = (filename == null ? null : imageCache.get(filename)); + + if (im == null) { + try { + im = toolkit.createImage(filename); + imageCache.put(filename, im); + } catch (Exception e) { + } + } + + return im; + } + } + + @Override + public Image getImage(URL url) { + return getImage(url, this); + } + + static Image getImage(URL url, Toolkit toolkit) { + synchronized (imageCache) { + Image im = imageCache.get(url); + if (im == null) { + try { + im = toolkit.createImage(url); + imageCache.put(url, im); + } catch (Exception e) { + } + } + return im; + } + } + + @Override + public int getScreenResolution() throws HeadlessException { + lockAWT(); + try { + return 62; + } finally { + unlockAWT(); + } + } + + @Override + public Dimension getScreenSize() { + lockAWT(); + try { + DisplayMode dm = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDisplayMode(); + return new Dimension(dm.getWidth(), dm.getHeight()); + } finally { + unlockAWT(); + } + } + + @Override + public Map mapInputMethodHighlight( + InputMethodHighlight highlight) throws HeadlessException { + lockAWT(); + try { + return mapInputMethodHighlightImpl(highlight); + } finally { + unlockAWT(); + } + } + + @Override + protected EventQueue getSystemEventQueueImpl() { + return getSystemEventQueueCore().getActiveEventQueue(); + } +} diff --git a/awt/java/awt/Transparency.java b/awt/java/awt/Transparency.java new file mode 100644 index 000000000..44a1e7f2e --- /dev/null +++ b/awt/java/awt/Transparency.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ + +package java.awt; + +/** + * The Transparency interface defines transparency's general modes. + * + * @since Android 1.0 + */ +public interface Transparency { + + /** + * The Constant OPAQUE represents completely opaque data, all pixels have an + * alpha value of 1.0. + */ + public static final int OPAQUE = 1; + + /** + * The Constant BITMASK represents data which can be either completely + * opaque, with an alpha value of 1.0, or completely transparent, with an + * alpha value of 0.0. + */ + public static final int BITMASK = 2; + + /** + * The Constant TRANSLUCENT represents data which alpha value can vary + * between and including 0.0 and 1.0. + */ + public static final int TRANSLUCENT = 3; + + /** + * Gets the transparency mode. + * + * @return the transparency mode: OPAQUE, BITMASK or TRANSLUCENT. + */ + public int getTransparency(); + +} diff --git a/awt/java/awt/color/CMMException.java b/awt/java/awt/color/CMMException.java new file mode 100644 index 000000000..18b9a7e56 --- /dev/null +++ b/awt/java/awt/color/CMMException.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +package java.awt.color; + +/** + * The CMMException is thrown as soon as a native CMM error occurs. + * + * @since Android 1.0 + */ +public class CMMException extends java.lang.RuntimeException { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 5775558044142994965L; + + /** + * Instantiates a new CMM exception with detail message. + * + * @param s + * the detail message of the exception. + */ + public CMMException (String s) { + super (s); + } +} diff --git a/awt/java/awt/color/ColorSpace.java b/awt/java/awt/color/ColorSpace.java new file mode 100644 index 000000000..44c491b74 --- /dev/null +++ b/awt/java/awt/color/ColorSpace.java @@ -0,0 +1,414 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +package java.awt.color; + +import java.io.Serializable; + +import org.apache.harmony.awt.gl.color.LUTColorConverter; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The ColorSpace class defines a color space type for a Color and provides + * methods for arrays of color component operations. + * + * @since Android 1.0 + */ +public abstract class ColorSpace implements Serializable { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = -409452704308689724L; + + /** + * The Constant TYPE_XYZ indicates XYZ color space type. + */ + public static final int TYPE_XYZ = 0; + + /** + * The Constant TYPE_Lab indicates Lab color space type. + */ + public static final int TYPE_Lab = 1; + + /** + * The Constant TYPE_Luv indicates Luv color space type. + */ + public static final int TYPE_Luv = 2; + + /** + * The Constant TYPE_YCbCr indicates YCbCr color space type. + */ + public static final int TYPE_YCbCr = 3; + + /** + * The Constant TYPE_Yxy indicates Yxy color space type. + */ + public static final int TYPE_Yxy = 4; + + /** + * The Constant TYPE_RGB indicates RGB color space type. + */ + public static final int TYPE_RGB = 5; + + /** + * The Constant TYPE_GRAY indicates Gray color space type. + */ + public static final int TYPE_GRAY = 6; + + /** + * The Constant TYPE_HSV indicates HSV color space type. + */ + public static final int TYPE_HSV = 7; + + /** + * The Constant TYPE_HLS indicates HLS color space type. + */ + public static final int TYPE_HLS = 8; + + /** + * The Constant TYPE_CMYK indicates CMYK color space type. + */ + public static final int TYPE_CMYK = 9; + + /** + * The Constant TYPE_CMY indicates CMY color space type. + */ + public static final int TYPE_CMY = 11; + + /** + * The Constant TYPE_2CLR indicates color spaces with 2 components. + */ + public static final int TYPE_2CLR = 12; + + /** + * The Constant TYPE_3CLR indicates color spaces with 3 components. + */ + public static final int TYPE_3CLR = 13; + + /** + * The Constant TYPE_4CLR indicates color spaces with 4 components. + */ + public static final int TYPE_4CLR = 14; + + /** + * The Constant TYPE_5CLR indicates color spaces with 5 components. + */ + public static final int TYPE_5CLR = 15; + + /** + * The Constant TYPE_6CLR indicates color spaces with 6 components. + */ + public static final int TYPE_6CLR = 16; + + /** + * The Constant TYPE_7CLR indicates color spaces with 7 components. + */ + public static final int TYPE_7CLR = 17; + + /** + * The Constant TYPE_8CLR indicates color spaces with 8 components. + */ + public static final int TYPE_8CLR = 18; + + /** + * The Constant TYPE_9CLR indicates color spaces with 9 components. + */ + public static final int TYPE_9CLR = 19; + + /** + * The Constant TYPE_ACLR indicates color spaces with 10 components. + */ + public static final int TYPE_ACLR = 20; + + /** + * The Constant TYPE_BCLR indicates color spaces with 11 components. + */ + public static final int TYPE_BCLR = 21; + + /** + * The Constant TYPE_CCLR indicates color spaces with 12 components. + */ + public static final int TYPE_CCLR = 22; + + /** + * The Constant TYPE_DCLR indicates color spaces with 13 components. + */ + public static final int TYPE_DCLR = 23; + + /** + * The Constant TYPE_ECLR indicates color spaces with 14 components. + */ + public static final int TYPE_ECLR = 24; + + /** + * The Constant TYPE_FCLR indicates color spaces with 15 components. + */ + public static final int TYPE_FCLR = 25; + + /** + * The Constant CS_sRGB indicates standard RGB color space. + */ + public static final int CS_sRGB = 1000; + + /** + * The Constant CS_LINEAR_RGB indicates linear RGB color space. + */ + public static final int CS_LINEAR_RGB = 1004; + + /** + * The Constant CS_CIEXYZ indicates CIEXYZ conversion color space. + */ + public static final int CS_CIEXYZ = 1001; + + /** + * The Constant CS_PYCC indicates Photo YCC conversion color space. + */ + public static final int CS_PYCC = 1002; + + /** + * The Constant CS_GRAY indicates linear gray scale color space. + */ + public static final int CS_GRAY = 1003; + + /** + * The cs_ gray. + */ + private static ColorSpace cs_Gray = null; + + /** + * The cs_ pycc. + */ + private static ColorSpace cs_PYCC = null; + + /** + * The cs_ ciexyz. + */ + private static ColorSpace cs_CIEXYZ = null; + + /** + * The cs_ lrgb. + */ + private static ColorSpace cs_LRGB = null; + + /** + * The cs_s rgb. + */ + private static ColorSpace cs_sRGB = null; + + /** + * The type. + */ + private int type; + + /** + * The num components. + */ + private int numComponents; + + /** + * Instantiates a ColorSpace with the specified ColorSpace type and number + * of components. + * + * @param type + * the type of color space. + * @param numcomponents + * the number of components. + */ + protected ColorSpace(int type, int numcomponents) { + this.numComponents = numcomponents; + this.type = type; + } + + /** + * Gets the name of the component for the specified component index. + * + * @param idx + * the index of the component. + * @return the name of the component. + */ + public String getName(int idx) { + if (idx < 0 || idx > numComponents - 1) { + // awt.16A=Invalid component index: {0} + throw new IllegalArgumentException(Messages.getString("awt.16A", idx)); //$NON-NLS-1$ + } + + return "Unnamed color component #" + idx; //$NON-NLS-1$ + } + + /** + * Performs the transformation of a color from this ColorSpace into the RGB + * color space. + * + * @param colorvalue + * the color value in this ColorSpace. + * @return the float array with color components in the RGB color space. + */ + public abstract float[] toRGB(float[] colorvalue); + + /** + * Performs the transformation of a color from this ColorSpace into the + * CS_CIEXYZ color space. + * + * @param colorvalue + * the color value in this ColorSpace. + * @return the float array with color components in the CS_CIEXYZ color + * space. + */ + public abstract float[] toCIEXYZ(float[] colorvalue); + + /** + * Performs the transformation of a color from the RGB color space into this + * ColorSpace. + * + * @param rgbvalue + * the float array representing a color in the RGB color space. + * @return the float array with the transformed color components. + */ + public abstract float[] fromRGB(float[] rgbvalue); + + /** + * Performs the transformation of a color from the CS_CIEXYZ color space + * into this ColorSpace. + * + * @param colorvalue + * the float array representing a color in the CS_CIEXYZ color + * space. + * @return the float array with the transformed color components. + */ + public abstract float[] fromCIEXYZ(float[] colorvalue); + + /** + * Gets the minimum normalized color component value for the specified + * component. + * + * @param component + * the component to determine the minimum value. + * @return the minimum normalized value of the component. + */ + public float getMinValue(int component) { + if (component < 0 || component > numComponents - 1) { + // awt.16A=Invalid component index: {0} + throw new IllegalArgumentException(Messages.getString("awt.16A", component)); //$NON-NLS-1$ + } + return 0; + } + + /** + * Gets the maximum normalized color component value for the specified + * component. + * + * @param component + * the component to determine the maximum value. + * @return the maximum normalized value of the component. + */ + public float getMaxValue(int component) { + if (component < 0 || component > numComponents - 1) { + // awt.16A=Invalid component index: {0} + throw new IllegalArgumentException(Messages.getString("awt.16A", component)); //$NON-NLS-1$ + } + return 1; + } + + /** + * Checks if this ColorSpace has CS_sRGB type or not. + * + * @return true, if this ColorSpace has CS_sRGB type, false otherwise. + */ + public boolean isCS_sRGB() { + // If our color space is sRGB, then cs_sRGB + // is already initialized + return (this == cs_sRGB); + } + + /** + * Gets the type of the ColorSpace. + * + * @return the type of the ColorSpace. + */ + public int getType() { + return type; + } + + /** + * Gets the number of components for this ColorSpace. + * + * @return the number of components. + */ + public int getNumComponents() { + return numComponents; + } + + + /** + * Gets the single instance of ColorSpace with the specified ColorSpace: + * CS_sRGB, CS_LINEAR_RGB, CS_CIEXYZ, CS_GRAY, or CS_PYCC. + * + * @param colorspace + * the identifier of the specified Colorspace. + * @return the single instance of the desired ColorSpace. + */ + public static ColorSpace getInstance(int colorspace) { + switch (colorspace) { + case CS_sRGB: + if (cs_sRGB == null) { + cs_sRGB = new ICC_ColorSpace( + new ICC_ProfileStub(CS_sRGB)); + LUTColorConverter.sRGB_CS = cs_sRGB; + //ICC_Profile.getInstance (CS_sRGB)); + } + return cs_sRGB; + case CS_CIEXYZ: + if (cs_CIEXYZ == null) { + cs_CIEXYZ = new ICC_ColorSpace( + new ICC_ProfileStub(CS_CIEXYZ)); + //ICC_Profile.getInstance (CS_CIEXYZ)); + } + return cs_CIEXYZ; + case CS_GRAY: + if (cs_Gray == null) { + cs_Gray = new ICC_ColorSpace( + new ICC_ProfileStub(CS_GRAY)); + LUTColorConverter.LINEAR_GRAY_CS = cs_Gray; + //ICC_Profile.getInstance (CS_GRAY)); + } + return cs_Gray; + case CS_PYCC: + if (cs_PYCC == null) { + cs_PYCC = new ICC_ColorSpace( + new ICC_ProfileStub(CS_PYCC)); + //ICC_Profile.getInstance (CS_PYCC)); + } + return cs_PYCC; + case CS_LINEAR_RGB: + if (cs_LRGB == null) { + cs_LRGB = new ICC_ColorSpace( + new ICC_ProfileStub(CS_LINEAR_RGB)); + LUTColorConverter.LINEAR_GRAY_CS = cs_Gray; + //ICC_Profile.getInstance (CS_LINEAR_RGB)); + } + return cs_LRGB; + default: + } + + // Unknown argument passed + // awt.16B=Not a predefined colorspace + throw new IllegalArgumentException(Messages.getString("Not a predefined colorspace")); //$NON-NLS-1$ + } + +} \ No newline at end of file diff --git a/awt/java/awt/color/ICC_ColorSpace.java b/awt/java/awt/color/ICC_ColorSpace.java new file mode 100644 index 000000000..5b4d7e99f --- /dev/null +++ b/awt/java/awt/color/ICC_ColorSpace.java @@ -0,0 +1,468 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +package java.awt.color; + +import org.apache.harmony.awt.gl.color.ColorConverter; +import org.apache.harmony.awt.gl.color.ColorScaler; +import org.apache.harmony.awt.gl.color.ICC_Transform; +import org.apache.harmony.awt.internal.nls.Messages; + +import java.io.*; + +/** + * This class implements the abstract class ColorSpace and represents device + * independent and device dependent color spaces. This color space is based on + * the International Color Consortium Specification (ICC) File Format for Color + * Profiles: http://www.color.org + * + * @since Android 1.0 + */ +public class ICC_ColorSpace extends ColorSpace { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 3455889114070431483L; + + // Need to keep compatibility with serialized form + /** + * The Constant serialPersistentFields. + */ + private static final ObjectStreamField[] + serialPersistentFields = { + new ObjectStreamField("thisProfile", ICC_Profile.class), //$NON-NLS-1$ + new ObjectStreamField("minVal", float[].class), //$NON-NLS-1$ + new ObjectStreamField("maxVal", float[].class), //$NON-NLS-1$ + new ObjectStreamField("diffMinMax", float[].class), //$NON-NLS-1$ + new ObjectStreamField("invDiffMinMax", float[].class), //$NON-NLS-1$ + new ObjectStreamField("needScaleInit", Boolean.TYPE) //$NON-NLS-1$ + }; + + + /** + * According to ICC specification (from http://www.color.org) "For the + * CIEXYZ encoding, each component (X, Y, and Z) is encoded as a + * u1Fixed15Number". This means that max value for this encoding is 1 + + * (32767/32768) + */ + private static final float MAX_XYZ = 1f + (32767f/32768f); + + /** + * The Constant MAX_SHORT. + */ + private static final float MAX_SHORT = 65535f; + + /** + * The Constant INV_MAX_SHORT. + */ + private static final float INV_MAX_SHORT = 1f/MAX_SHORT; + + /** + * The Constant SHORT2XYZ_FACTOR. + */ + private static final float SHORT2XYZ_FACTOR = MAX_XYZ/MAX_SHORT; + + /** + * The Constant XYZ2SHORT_FACTOR. + */ + private static final float XYZ2SHORT_FACTOR = MAX_SHORT/MAX_XYZ; + + /** + * The profile. + */ + private ICC_Profile profile = null; + + /** + * The min values. + */ + private float minValues[] = null; + + /** + * The max values. + */ + private float maxValues[] = null; + + // cache transforms here - performance gain + /** + * The to rgb transform. + */ + private ICC_Transform toRGBTransform = null; + + /** + * The from rgb transform. + */ + private ICC_Transform fromRGBTransform = null; + + /** + * The to xyz transform. + */ + private ICC_Transform toXYZTransform = null; + + /** + * The from xyz transform. + */ + private ICC_Transform fromXYZTransform = null; + + /** + * The converter. + */ + private final ColorConverter converter = new ColorConverter(); + + /** + * The scaler. + */ + private final ColorScaler scaler = new ColorScaler(); + + /** + * The scaling data loaded. + */ + private boolean scalingDataLoaded = false; + + /** + * The resolved deserialized inst. + */ + private ICC_ColorSpace resolvedDeserializedInst; + + /** + * Instantiates a new ICC color space from an ICC_Profile object. + * + * @param pf + * the ICC_Profile object. + */ + public ICC_ColorSpace(ICC_Profile pf) { + super(pf.getColorSpaceType(), pf.getNumComponents()); + + int pfClass = pf.getProfileClass(); + + switch (pfClass) { + case ICC_Profile.CLASS_COLORSPACECONVERSION: + case ICC_Profile.CLASS_DISPLAY: + case ICC_Profile.CLASS_OUTPUT: + case ICC_Profile.CLASS_INPUT: + break; // OK, it is color conversion profile + default: + // awt.168=Invalid profile class. + throw new IllegalArgumentException(Messages.getString("awt.168")); //$NON-NLS-1$ + } + + profile = pf; + fillMinMaxValues(); + } + + /** + * Gets the ICC_Profile for this ICC_ColorSpace. + * + * @return the ICC_Profile for this ICC_ColorSpace. + */ + public ICC_Profile getProfile() { + if (profile instanceof ICC_ProfileStub) { + profile = ((ICC_ProfileStub) profile).loadProfile(); + } + + return profile; + } + + /** + * Performs the transformation of a color from this ColorSpace into the RGB + * color space. + * + * @param colorvalue + * the color value in this ColorSpace. + * @return the float array with color components in the RGB color space. + */ + @Override + public float[] toRGB(float[] colorvalue) { + if (toRGBTransform == null) { + ICC_Profile sRGBProfile = + ((ICC_ColorSpace) ColorSpace.getInstance(CS_sRGB)).getProfile(); + ICC_Profile[] profiles = {getProfile(), sRGBProfile}; + toRGBTransform = new ICC_Transform(profiles); + if (!scalingDataLoaded) { + scaler.loadScalingData(this); + scalingDataLoaded = true; + } + } + + short[] data = new short[getNumComponents()]; + + scaler.scale(colorvalue, data, 0); + + short[] converted = + converter.translateColor(toRGBTransform, data, null); + + // unscale to sRGB + float[] res = new float[3]; + + res[0] = ((converted[0] & 0xFFFF)) * INV_MAX_SHORT; + res[1] = ((converted[1] & 0xFFFF)) * INV_MAX_SHORT; + res[2] = ((converted[2] & 0xFFFF)) * INV_MAX_SHORT; + + return res; + } + + /** + * Performs the transformation of a color from this ColorSpace into the + * CS_CIEXYZ color space. + * + * @param colorvalue + * the color value in this ColorSpace. + * @return the float array with color components in the CS_CIEXYZ color + * space. + */ + @Override + public float[] toCIEXYZ(float[] colorvalue) { + if (toXYZTransform == null) { + ICC_Profile xyzProfile = + ((ICC_ColorSpace) ColorSpace.getInstance(CS_CIEXYZ)).getProfile(); + ICC_Profile[] profiles = {getProfile(), xyzProfile}; + try { + int[] intents = { + ICC_Profile.icRelativeColorimetric, + ICC_Profile.icPerceptual}; + toXYZTransform = new ICC_Transform(profiles, intents); + } catch (CMMException e) { // No such tag, use what we can + toXYZTransform = new ICC_Transform(profiles); + } + + if (!scalingDataLoaded) { + scaler.loadScalingData(this); + scalingDataLoaded = true; + } + } + + short[] data = new short[getNumComponents()]; + + scaler.scale(colorvalue, data, 0); + + short[] converted = + converter.translateColor(toXYZTransform, data, null); + + // unscale to XYZ + float[] res = new float[3]; + + res[0] = ((converted[0] & 0xFFFF)) * SHORT2XYZ_FACTOR; + res[1] = ((converted[1] & 0xFFFF)) * SHORT2XYZ_FACTOR; + res[2] = ((converted[2] & 0xFFFF)) * SHORT2XYZ_FACTOR; + + return res; + } + + /** + * Performs the transformation of a color from the RGB color space into this + * ColorSpace. + * + * @param rgbvalue + * the float array representing a color in the RGB color space. + * @return the float array with the transformed color components. + */ + @Override + public float[] fromRGB(float[] rgbvalue) { + if (fromRGBTransform == null) { + ICC_Profile sRGBProfile = + ((ICC_ColorSpace) ColorSpace.getInstance(CS_sRGB)).getProfile(); + ICC_Profile[] profiles = {sRGBProfile, getProfile()}; + fromRGBTransform = new ICC_Transform(profiles); + if (!scalingDataLoaded) { + scaler.loadScalingData(this); + scalingDataLoaded = true; + } + } + + // scale rgb value to short + short[] scaledRGBValue = new short[3]; + scaledRGBValue[0] = (short)(rgbvalue[0] * MAX_SHORT + 0.5f); + scaledRGBValue[1] = (short)(rgbvalue[1] * MAX_SHORT + 0.5f); + scaledRGBValue[2] = (short)(rgbvalue[2] * MAX_SHORT + 0.5f); + + short[] converted = + converter.translateColor(fromRGBTransform, scaledRGBValue, null); + + float[] res = new float[getNumComponents()]; + + scaler.unscale(res, converted, 0); + + return res; + } + + /** + * Performs the transformation of a color from the CS_CIEXYZ color space + * into this ColorSpace. + * + * @param xyzvalue + * the float array representing a color in the CS_CIEXYZ color + * space. + * @return the float array with the transformed color components. + */ + @Override + public float[] fromCIEXYZ(float[] xyzvalue) { + if (fromXYZTransform == null) { + ICC_Profile xyzProfile = + ((ICC_ColorSpace) ColorSpace.getInstance(CS_CIEXYZ)).getProfile(); + ICC_Profile[] profiles = {xyzProfile, getProfile()}; + try { + int[] intents = { + ICC_Profile.icPerceptual, + ICC_Profile.icRelativeColorimetric}; + fromXYZTransform = new ICC_Transform(profiles, intents); + } catch (CMMException e) { // No such tag, use what we can + fromXYZTransform = new ICC_Transform(profiles); + } + + if (!scalingDataLoaded) { + scaler.loadScalingData(this); + scalingDataLoaded = true; + } + + } + + // scale xyz value to short + short[] scaledXYZValue = new short[3]; + scaledXYZValue[0] = (short)(xyzvalue[0] * XYZ2SHORT_FACTOR + 0.5f); + scaledXYZValue[1] = (short)(xyzvalue[1] * XYZ2SHORT_FACTOR + 0.5f); + scaledXYZValue[2] = (short)(xyzvalue[2] * XYZ2SHORT_FACTOR + 0.5f); + + short[] converted = + converter.translateColor(fromXYZTransform, scaledXYZValue, null); + + float[] res = new float[getNumComponents()]; + + scaler.unscale(res, converted, 0); + + return res; + } + + /** + * Gets the minimum normalized color component value for the specified + * component. + * + * @param component + * the component to determine the minimum value. + * @return the minimum normalized value of the component. + */ + @Override + public float getMinValue(int component) { + if ((component < 0) || (component > this.getNumComponents() - 1)) { + // awt.169=Component index out of range + throw new IllegalArgumentException(Messages.getString("awt.169")); //$NON-NLS-1$ + } + + return minValues[component]; + } + + /** + * Gets the maximum normalized color component value for the specified + * component. + * + * @param component + * the component to determine the maximum value. + * @return the maximum normalized value of the component. + */ + @Override + public float getMaxValue(int component) { + if ((component < 0) || (component > this.getNumComponents() - 1)) { + // awt.169=Component index out of range + throw new IllegalArgumentException(Messages.getString("awt.169")); //$NON-NLS-1$ + } + + return maxValues[component]; + } + + /** + * Fill min max values. + */ + private void fillMinMaxValues() { + int n = getNumComponents(); + maxValues = new float[n]; + minValues = new float[n]; + switch (getType()) { + case ColorSpace.TYPE_XYZ: + minValues[0] = 0; + minValues[1] = 0; + minValues[2] = 0; + maxValues[0] = MAX_XYZ; + maxValues[1] = MAX_XYZ; + maxValues[2] = MAX_XYZ; + break; + case ColorSpace.TYPE_Lab: + minValues[0] = 0; + minValues[1] = -128; + minValues[2] = -128; + maxValues[0] = 100; + maxValues[1] = 127; + maxValues[2] = 127; + break; + default: + for(int i=0; i() { + public FileInputStream run() { + FileInputStream fiStream = null; + + // Open absolute path + try { + fiStream = new FileInputStream(fName); + if (fiStream != null) { + return fiStream; + } + } catch (FileNotFoundException e) { + } + + // Check java.iccprofile.path entries + fiStream = tryPath(System.getProperty("java.iccprofile.path"), fName); //$NON-NLS-1$ + if (fiStream != null) { + return fiStream; + } + + // Check java.class.path entries + fiStream = tryPath(System.getProperty("java.class.path"), fName); //$NON-NLS-1$ + if (fiStream != null) { + return fiStream; + } + + // Check directory with java sample profiles + String home = System.getProperty("java.home"); //$NON-NLS-1$ + if (home != null) { + fiStream = tryPath(home + File.separatorChar + + "lib" + File.separatorChar + "cmm", fName //$NON-NLS-1$ //$NON-NLS-2$ + ); + } + + return fiStream; + } + }); + + if (fiStream == null) { + // awt.161=Unable to open file {0} + throw new IOException(Messages.getString("awt.161", fileName)); //$NON-NLS-1$ + } + + ICC_Profile pf = getInstance(fiStream); + fiStream.close(); + return pf; + } + + /** + * Gets the single instance of ICC_Profile with data in the specified + * InputStream. + * + * @param s + * the InputStream with ICC profile data. + * @return single instance of ICC_Profile. + * @throws IOException + * if an I/O exception has occurred during reading from + * InputStream. + * @throws IllegalArgumentException + * if the file does not contain valid ICC Profile data. + */ + public static ICC_Profile getInstance(InputStream s) throws IOException { + byte[] header = new byte[headerSize]; + // awt.162=Invalid ICC Profile Data + String invalidDataMessage = Messages.getString("awt.162"); //$NON-NLS-1$ + + // Get header from the input stream + if (s.read(header) != headerSize) { + throw new IllegalArgumentException(invalidDataMessage); + } + + // Check the profile data for consistency + if (ICC_ProfileHelper.getBigEndianFromByteArray(header, icHdrMagic) != headerMagicNumber) { + throw new IllegalArgumentException(invalidDataMessage); + } + + // Get profile size from header, create an array for profile data + int profileSize = ICC_ProfileHelper.getBigEndianFromByteArray(header, icHdrSize); + byte[] profileData = new byte[profileSize]; + + // Copy header into it + System.arraycopy(header, 0, profileData, 0, headerSize); + + // Read the profile itself + if (s.read(profileData, headerSize, profileSize - headerSize) != profileSize - headerSize) { + throw new IllegalArgumentException(invalidDataMessage); + } + + return getInstance(profileData); + } + + /** + * Gets the single instance of ICC_Profile from the specified data in a byte + * array. + * + * @param data + * the byte array of ICC profile. + * @return single instance of ICC_Profile from the specified data in a byte + * array. + * @throws IllegalArgumentException + * if the file does not contain valid ICC Profile data. + */ + public static ICC_Profile getInstance(byte[] data) { + ICC_Profile res = null; + + try { + res = new ICC_Profile(data); + } catch (CMMException e) { + // awt.162=Invalid ICC Profile Data + throw new IllegalArgumentException(Messages.getString("awt.162")); //$NON-NLS-1$ + } + + if (System.getProperty("os.name").toLowerCase().indexOf("windows") >= 0) { //$NON-NLS-1$ //$NON-NLS-2$ + try { + if (res.getColorSpaceType() == ColorSpace.TYPE_RGB + && res.getDataSize(icSigMediaWhitePointTag) > 0 + && res.getDataSize(icSigRedColorantTag) > 0 + && res.getDataSize(icSigGreenColorantTag) > 0 + && res.getDataSize(icSigBlueColorantTag) > 0 + && res.getDataSize(icSigRedTRCTag) > 0 + && res.getDataSize(icSigGreenTRCTag) > 0 + && res.getDataSize(icSigBlueTRCTag) > 0) { + res = new ICC_ProfileRGB(res.getProfileHandle()); + } else if (res.getColorSpaceType() == ColorSpace.TYPE_GRAY + && res.getDataSize(icSigMediaWhitePointTag) > 0 + && res.getDataSize(icSigGrayTRCTag) > 0) { + res = new ICC_ProfileGray(res.getProfileHandle()); + } + + } catch (CMMException e) { /* return res in this case */ + } + } + + return res; + } + + /** + * Gets the single instance of ICC_Profile with the specific color space + * defined by the ColorSpace class: CS_sRGB, CS_LINEAR_RGB, CS_CIEXYZ, + * CS_PYCC, CS_GRAY. + * + * @param cspace + * the type of color space defined in the ColorSpace class. + * @return single instance of ICC_Profile. + * @throws IllegalArgumentException + * is not one of the defined color space types. + */ + public static ICC_Profile getInstance(int cspace) { + try { + switch (cspace) { + + case ColorSpace.CS_sRGB: + if (sRGBProfile == null) { + sRGBProfile = getInstance("sRGB.pf"); //$NON-NLS-1$ + } + return sRGBProfile; + + case ColorSpace.CS_CIEXYZ: + if (xyzProfile == null) { + xyzProfile = getInstance("CIEXYZ.pf"); //$NON-NLS-1$ + } + return xyzProfile; + + case ColorSpace.CS_GRAY: + if (grayProfile == null) { + grayProfile = getInstance("GRAY.pf"); //$NON-NLS-1$ + } + return grayProfile; + + case ColorSpace.CS_PYCC: + if (pyccProfile == null) { + pyccProfile = getInstance("PYCC.pf"); //$NON-NLS-1$ + } + return pyccProfile; + + case ColorSpace.CS_LINEAR_RGB: + if (linearRGBProfile == null) { + linearRGBProfile = getInstance("LINEAR_RGB.pf"); //$NON-NLS-1$ + } + return linearRGBProfile; + } + + } catch (IOException e) { + // awt.163=Can't open color profile + throw new IllegalArgumentException(Messages.getString("Can't open color profile")); //$NON-NLS-1$ + } + + // awt.164=Not a predefined color space + throw new IllegalArgumentException(Messages.getString("Not a predefined color space")); //$NON-NLS-1$ + } + + /** + * Reads an integer from the profile header at the specified position. + * + * @param idx + * - offset in bytes from the beginning of the header + * @return the integer value from header + */ + private int getIntFromHeader(int idx) { + if (headerData == null) { + headerData = getData(icSigHead); + } + + return ((headerData[idx] & 0xFF) << 24) | ((headerData[idx + 1] & 0xFF) << 16) + | ((headerData[idx + 2] & 0xFF) << 8) | ((headerData[idx + 3] & 0xFF)); + } + + /** + * Reads byte from the profile header at the specified position. + * + * @param idx + * - offset in bytes from the beginning of the header + * @return the byte from header + */ + private byte getByteFromHeader(int idx) { + if (headerData == null) { + headerData = getData(icSigHead); + } + + return headerData[idx]; + } + + /** + * Converts ICC color space signature to the java predefined color space + * type. + * + * @param signature + * the signature + * @return the int + */ + private int csFromSignature(int signature) { + switch (signature) { + case icSigRgbData: + return ColorSpace.TYPE_RGB; + case icSigXYZData: + return ColorSpace.TYPE_XYZ; + case icSigCmykData: + return ColorSpace.TYPE_CMYK; + case icSigLabData: + return ColorSpace.TYPE_Lab; + case icSigGrayData: + return ColorSpace.TYPE_GRAY; + case icSigHlsData: + return ColorSpace.TYPE_HLS; + case icSigLuvData: + return ColorSpace.TYPE_Luv; + case icSigYCbCrData: + return ColorSpace.TYPE_YCbCr; + case icSigYxyData: + return ColorSpace.TYPE_Yxy; + case icSigHsvData: + return ColorSpace.TYPE_HSV; + case icSigCmyData: + return ColorSpace.TYPE_CMY; + case icSigSpace2CLR: + return ColorSpace.TYPE_2CLR; + case icSigSpace3CLR: + return ColorSpace.TYPE_3CLR; + case icSigSpace4CLR: + return ColorSpace.TYPE_4CLR; + case icSigSpace5CLR: + return ColorSpace.TYPE_5CLR; + case icSigSpace6CLR: + return ColorSpace.TYPE_6CLR; + case icSigSpace7CLR: + return ColorSpace.TYPE_7CLR; + case icSigSpace8CLR: + return ColorSpace.TYPE_8CLR; + case icSigSpace9CLR: + return ColorSpace.TYPE_9CLR; + case icSigSpaceACLR: + return ColorSpace.TYPE_ACLR; + case icSigSpaceBCLR: + return ColorSpace.TYPE_BCLR; + case icSigSpaceCCLR: + return ColorSpace.TYPE_CCLR; + case icSigSpaceDCLR: + return ColorSpace.TYPE_DCLR; + case icSigSpaceECLR: + return ColorSpace.TYPE_ECLR; + case icSigSpaceFCLR: + return ColorSpace.TYPE_FCLR; + default: + } + + // awt.165=Color space doesn't comply with ICC specification + throw new IllegalArgumentException(Messages.getString("awt.165")); //$NON-NLS-1$ + } + + /** + * Gets the profile handle. + * + * @return the profile handle + */ + private long getProfileHandle() { + handleStolen = true; + return profileHandle; + } + + /** + * Gets the data size. + * + * @param tagSignature + * the tag signature + * @return the data size + */ + private int getDataSize(int tagSignature) { + return NativeCMM.cmmGetProfileElementSize(profileHandle, tagSignature); + } + + /** + * Reads XYZ value from the tag data. + * + * @param tagSignature + * the tag signature + * @return the XYZ value + */ + float[] getXYZValue(int tagSignature) { + float[] res = new float[3]; + byte[] data = getData(tagSignature); + + // Convert from ICC s15Fixed16Number type + // 1 (float) = 0x10000 (s15Fixed16Number), + // hence dividing by 0x10000 + res[0] = ICC_ProfileHelper.getIntFromByteArray(data, 0) / 65536.f; + res[1] = ICC_ProfileHelper.getIntFromByteArray(data, 4) / 65536.f; + res[2] = ICC_ProfileHelper.getIntFromByteArray(data, 8) / 65536.f; + + return res; + } + + /** + * Gets the media white point. + * + * @return the media white point. + */ + float[] getMediaWhitePoint() { + return getXYZValue(icSigMediaWhitePointTag); + } + + /** + * If TRC is not a table returns gamma via return value and sets dataTRC to + * null. If TRC is a table returns 0 and fills dataTRC with values. + * + * @param tagSignature + * the tag signature + * @param dataTRC + * the data trc + * @return - gamma or zero if TRC is a table + */ + private float getGammaOrTRC(int tagSignature, short[] dataTRC) { + byte[] data = getData(tagSignature); + int trcSize = ICC_ProfileHelper.getIntFromByteArray(data, icCurveCount); + + dataTRC = null; + + if (trcSize == 0) { + return 1.0f; + } + + if (trcSize == 1) { + // Cast from ICC u8Fixed8Number to float + return ICC_ProfileHelper.getShortFromByteArray(data, icCurveData) / 256.f; + } + + // TRC is a table + dataTRC = new short[trcSize]; + for (int i = 0, pos = icCurveData; i < trcSize; i++, pos += 2) { + dataTRC[i] = ICC_ProfileHelper.getShortFromByteArray(data, pos); + } + return 0; + } + + /** + * Gets the gamma. + * + * @param tagSignature + * the tag signature + * @return the gamma + */ + float getGamma(int tagSignature) { + short[] dataTRC = null; + float gamma = getGammaOrTRC(tagSignature, dataTRC); + + if (dataTRC == null) { + return gamma; + } + // awt.166=TRC is not a simple gamma value. + throw new ProfileDataException(Messages.getString("awt.166")); //$NON-NLS-1$ + } + + /** + * Gets the TRC. + * + * @param tagSignature + * the tag signature + * @return the tRC + */ + short[] getTRC(int tagSignature) { + short[] dataTRC = null; + getGammaOrTRC(tagSignature, dataTRC); + + if (dataTRC == null) { + // awt.167=TRC is a gamma value, not a table. + throw new ProfileDataException(Messages.getString("awt.167")); //$NON-NLS-1$ + } + return dataTRC; + } +} diff --git a/awt/java/awt/color/ICC_ProfileGray.java b/awt/java/awt/color/ICC_ProfileGray.java new file mode 100644 index 000000000..f74810174 --- /dev/null +++ b/awt/java/awt/color/ICC_ProfileGray.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +package java.awt.color; + +/** + * The ICC_ProfileGray class represent profiles with TYPE_GRAY color space type, + * and includes the grayTRCTag and mediaWhitePointTag tags. The gray component + * can be transformed from a GRAY device profile color space to the CIEXYZ + * Profile through the tone reproduction curve (TRC): + *

+ * PCSY = grayTRC[deviceGray] + * + * @since Android 1.0 + */ +public class ICC_ProfileGray extends ICC_Profile { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -1124721290732002649L; + + /** + * Instantiates a new iC c_ profile gray. + * + * @param profileHandle + * the profile handle + */ + ICC_ProfileGray(long profileHandle) { + super(profileHandle); + } + + /** + * Gets the TRC as an array of shorts. + * + * @return the short array of the TRC. + */ + public short[] getTRC() { + return super.getTRC(icSigGrayTRCTag); + } + + /** + * Gets the media white point. + * + * @return the media white point + */ + @Override + public float[] getMediaWhitePoint() { + return super.getMediaWhitePoint(); + } + + /** + * Gets a gamma value representing the tone reproduction curve (TRC). + * + * @return the gamma value representing the tone reproduction curve (TRC). + */ + public float getGamma() { + return super.getGamma(icSigGrayTRCTag); + } +} + diff --git a/awt/java/awt/color/ICC_ProfileRGB.java b/awt/java/awt/color/ICC_ProfileRGB.java new file mode 100644 index 000000000..9c6010fcd --- /dev/null +++ b/awt/java/awt/color/ICC_ProfileRGB.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +package java.awt.color; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The ICC_ProfileRGB class represents profiles with RGB color space type and + * contains the redColorantTag, greenColorantTag, blueColorantTag, redTRCTag, + * greenTRCTag, blueTRCTag, and mediaWhitePointTag tags. + * + * @since Android 1.0 + */ +public class ICC_ProfileRGB extends ICC_Profile { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 8505067385152579334L; + + /** + * Instantiates a new RGB ICC_Profile. + * + * @param profileHandle + * the profile handle + */ + ICC_ProfileRGB(long profileHandle) { + super(profileHandle); + } + + /** + * The Constant REDCOMPONENT indicates the red component. + */ + public static final int REDCOMPONENT = 0; + + /** + * The Constant GREENCOMPONENT indicates the green component. + */ + public static final int GREENCOMPONENT = 1; + + /** + * The Constant BLUECOMPONENT indicates the blue component. + */ + public static final int BLUECOMPONENT = 2; + + // awt.15E=Unknown component. Must be REDCOMPONENT, GREENCOMPONENT or BLUECOMPONENT. + /** + * The Constant UNKNOWN_COMPONENT_MSG. + */ + private static final String UNKNOWN_COMPONENT_MSG = Messages + .getString("awt.15E"); //$NON-NLS-1$ + + /** + * Gets the TRC. + * + * @param component + * the tag signature. + * @return the TRC value. + */ + @Override + public short[] getTRC(int component) { + switch (component) { + case REDCOMPONENT: + return super.getTRC(icSigRedTRCTag); + case GREENCOMPONENT: + return super.getTRC(icSigGreenTRCTag); + case BLUECOMPONENT: + return super.getTRC(icSigBlueTRCTag); + default: + } + + throw new IllegalArgumentException(UNKNOWN_COMPONENT_MSG); + } + + /** + * Gets the gamma. + * + * @param component + * the tag signature. + * @return the gamma value. + */ + @Override + public float getGamma(int component) { + switch (component) { + case REDCOMPONENT: + return super.getGamma(icSigRedTRCTag); + case GREENCOMPONENT: + return super.getGamma(icSigGreenTRCTag); + case BLUECOMPONENT: + return super.getGamma(icSigBlueTRCTag); + default: + } + + throw new IllegalArgumentException(UNKNOWN_COMPONENT_MSG); + } + + /** + * Gets a float matrix which contains the X, Y, and Z components of the + * profile's redColorantTag, greenColorantTag, and blueColorantTag. + * + * @return the float matrix which contains the X, Y, and Z components of the + * profile's redColorantTag, greenColorantTag, and blueColorantTag. + */ + public float[][] getMatrix() { + float [][] m = new float[3][3]; // The matrix + + float[] redXYZ = getXYZValue(icSigRedColorantTag); + float[] greenXYZ = getXYZValue(icSigGreenColorantTag); + float[] blueXYZ = getXYZValue(icSigBlueColorantTag); + + m[0][0] = redXYZ[0]; + m[1][0] = redXYZ[1]; + m[2][0] = redXYZ[2]; + + m[0][1] = greenXYZ[0]; + m[1][1] = greenXYZ[1]; + m[2][1] = greenXYZ[2]; + + m[0][2] = blueXYZ[0]; + m[1][2] = blueXYZ[1]; + m[2][2] = blueXYZ[2]; + + return m; + } + + /** + * Gets the media white point. + * + * @return the media white point. + */ + @Override + public float[] getMediaWhitePoint() { + return super.getMediaWhitePoint(); + } +} + diff --git a/awt/java/awt/color/ICC_ProfileStub.java b/awt/java/awt/color/ICC_ProfileStub.java new file mode 100644 index 000000000..bc04c8a66 --- /dev/null +++ b/awt/java/awt/color/ICC_ProfileStub.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package java.awt.color; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectStreamException; +import java.io.OutputStream; + +import org.apache.harmony.awt.internal.nls.Messages; + +final class ICC_ProfileStub extends ICC_Profile { + private static final long serialVersionUID = 501389760875253507L; + + transient int colorspace; + + public ICC_ProfileStub(int csSpecifier) { + switch (csSpecifier) { + case ColorSpace.CS_sRGB: + case ColorSpace.CS_CIEXYZ: + case ColorSpace.CS_LINEAR_RGB: + case ColorSpace.CS_PYCC: + case ColorSpace.CS_GRAY: + break; + default: + // awt.15D=Invalid colorspace + throw new IllegalArgumentException(Messages.getString("awt.15D")); //$NON-NLS-1$ + } + colorspace = csSpecifier; + } + + @Override + public void write(String fileName) throws IOException { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + /** + * Serializable implementation + * + * @throws ObjectStreamException + */ + private Object writeReplace() throws ObjectStreamException { + return loadProfile(); + } + + @Override + public void write(OutputStream s) throws IOException { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + @Override + public void setData(int tagSignature, byte[] tagData) { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + @Override + public byte[] getData(int tagSignature) { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + @Override + public byte[] getData() { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + @Override + protected void finalize() { + } + + @Override + public int getProfileClass() { + return CLASS_COLORSPACECONVERSION; + } + + @Override + public int getPCSType() { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + @Override + public int getNumComponents() { + switch (colorspace) { + case ColorSpace.CS_sRGB: + case ColorSpace.CS_CIEXYZ: + case ColorSpace.CS_LINEAR_RGB: + case ColorSpace.CS_PYCC: + return 3; + case ColorSpace.CS_GRAY: + return 1; + default: + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + } + + @Override + public int getMinorVersion() { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + @Override + public int getMajorVersion() { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + @Override + public int getColorSpaceType() { + switch (colorspace) { + case ColorSpace.CS_sRGB: + case ColorSpace.CS_LINEAR_RGB: + return ColorSpace.TYPE_RGB; + case ColorSpace.CS_CIEXYZ: + return ColorSpace.TYPE_XYZ; + case ColorSpace.CS_PYCC: + return ColorSpace.TYPE_3CLR; + case ColorSpace.CS_GRAY: + return ColorSpace.TYPE_GRAY; + default: + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + } + + public static ICC_Profile getInstance(String fileName) throws IOException { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + public static ICC_Profile getInstance(InputStream s) throws IOException { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + public static ICC_Profile getInstance(byte[] data) { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + public static ICC_Profile getInstance(int cspace) { + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + + public ICC_Profile loadProfile() { + switch (colorspace) { + case ColorSpace.CS_sRGB: + return ICC_Profile.getInstance(ColorSpace.CS_sRGB); + case ColorSpace.CS_GRAY: + return ICC_Profile.getInstance(ColorSpace.CS_GRAY); + case ColorSpace.CS_CIEXYZ: + return ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ); + case ColorSpace.CS_LINEAR_RGB: + return ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB); + case ColorSpace.CS_PYCC: + return ICC_Profile.getInstance(ColorSpace.CS_PYCC); + default: + throw new UnsupportedOperationException("Stub cannot perform this operation"); //$NON-NLS-1$ + } + } +} \ No newline at end of file diff --git a/awt/java/awt/color/ProfileDataException.java b/awt/java/awt/color/ProfileDataException.java new file mode 100644 index 000000000..335f314ca --- /dev/null +++ b/awt/java/awt/color/ProfileDataException.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +package java.awt.color; + +/** + * The ProfileDataException class represents an error which occurs while + * accessing or processing an ICC_Profile object. + * + * @since Android 1.0 + */ +public class ProfileDataException extends RuntimeException { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 7286140888240322498L; + + /** + * Instantiates a new profile data exception with detailed message. + * + * @param s + * the detailed message of the exception. + */ + public ProfileDataException(String s) { + super(s); + } + +} + diff --git a/awt/java/awt/color/package.html b/awt/java/awt/color/package.html new file mode 100644 index 000000000..609d963a8 --- /dev/null +++ b/awt/java/awt/color/package.html @@ -0,0 +1,8 @@ + + +

+ This package contains classes representing color spaces and profiles based on the International Color Consortium (ICC) Profile Format Specification. +

+ @since Android 1.0 + + diff --git a/awt/java/awt/event/AWTEventListener.java b/awt/java/awt/event/AWTEventListener.java new file mode 100644 index 000000000..76293b3a7 --- /dev/null +++ b/awt/java/awt/event/AWTEventListener.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.AWTEvent; +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface AWTEventListener extends EventListener { + + public void eventDispatched(AWTEvent event); + +} diff --git a/awt/java/awt/event/AWTEventListenerProxy.java b/awt/java/awt/event/AWTEventListenerProxy.java new file mode 100644 index 000000000..3edc41f3e --- /dev/null +++ b/awt/java/awt/event/AWTEventListenerProxy.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.AWTEvent; + +import java.util.EventListenerProxy; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class AWTEventListenerProxy extends EventListenerProxy implements AWTEventListener { + + private AWTEventListener listener; + private long eventMask; + + public AWTEventListenerProxy(long eventMask, AWTEventListener listener) { + super(listener); + + // awt.193=Listener can't be zero + assert listener != null : Messages.getString("awt.193"); //$NON-NLS-1$ + + this.listener = listener; + this.eventMask = eventMask; + } + + public void eventDispatched(AWTEvent evt) { + listener.eventDispatched(evt); + } + + public long getEventMask() { + return eventMask; + } + +} diff --git a/awt/java/awt/event/ActionEvent.java b/awt/java/awt/event/ActionEvent.java new file mode 100644 index 000000000..e882e0db9 --- /dev/null +++ b/awt/java/awt/event/ActionEvent.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.AWTEvent; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class ActionEvent extends AWTEvent { + + private static final long serialVersionUID = -7671078796273832149L; + + public static final int SHIFT_MASK = 1; + + public static final int CTRL_MASK = 2; + + public static final int META_MASK = 4; + + public static final int ALT_MASK = 8; + + public static final int ACTION_FIRST = 1001; + + public static final int ACTION_LAST = 1001; + + public static final int ACTION_PERFORMED = 1001; + + private long when; + private int modifiers; + private String command; + + public ActionEvent(Object source, int id, String command) { + this(source, id, command, 0); + } + + public ActionEvent(Object source, int id, String command, int modifiers) { + this(source, id, command, 0l, modifiers); + } + + public ActionEvent(Object source, int id, String command, long when, int modifiers) { + super(source, id); + + this.command = command; + this.when = when; + this.modifiers = modifiers; + } + + public int getModifiers() { + return modifiers; + } + + public String getActionCommand() { + return command; + } + + public long getWhen() { + return when; + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * ActionEvent e = new ActionEvent(new Component(){}, + * ActionEvent.ACTION_PERFORMED, "Command", + * ActionEvent.SHIFT_MASK|ActionEvent.CTRL_MASK| + * ActionEvent.META_MASK|ActionEvent.ALT_MASK); + * System.out.println(e); + */ + + String idString = (id == ACTION_PERFORMED) ? + "ACTION_PERFORMED" : "unknown type"; //$NON-NLS-1$ //$NON-NLS-2$ + String modifiersString = ""; //$NON-NLS-1$ + + if ((modifiers & SHIFT_MASK) > 0) { + modifiersString += "Shift"; //$NON-NLS-1$ + } + if ((modifiers & CTRL_MASK) > 0) { + modifiersString += modifiersString.length() == 0 ? "Ctrl" : "+Ctrl"; //$NON-NLS-1$ //$NON-NLS-2$ + } + if ((modifiers & META_MASK) > 0) { + modifiersString += modifiersString.length() == 0 ? "Meta" : "+Meta"; //$NON-NLS-1$ //$NON-NLS-2$ + } + if ((modifiers & ALT_MASK) > 0) { + modifiersString += modifiersString.length() == 0 ? "Alt" : "+Alt"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + return (idString + ",cmd=" + command + ",when=" + when + //$NON-NLS-1$ //$NON-NLS-2$ + ",modifiers=" + modifiersString); //$NON-NLS-1$ + } + +} diff --git a/awt/java/awt/event/ActionListener.java b/awt/java/awt/event/ActionListener.java new file mode 100644 index 000000000..a6eee7a16 --- /dev/null +++ b/awt/java/awt/event/ActionListener.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface ActionListener extends EventListener { + + public void actionPerformed(ActionEvent e); + +} diff --git a/awt/java/awt/event/AdjustmentEvent.java b/awt/java/awt/event/AdjustmentEvent.java new file mode 100644 index 000000000..be2d6c4ba --- /dev/null +++ b/awt/java/awt/event/AdjustmentEvent.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Adjustable; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class AdjustmentEvent extends AWTEvent { + + private static final long serialVersionUID = 5700290645205279921L; + + public static final int ADJUSTMENT_FIRST = 601; + + public static final int ADJUSTMENT_LAST = 601; + + public static final int ADJUSTMENT_VALUE_CHANGED = 601; + + public static final int UNIT_INCREMENT = 1; + + public static final int UNIT_DECREMENT = 2; + + public static final int BLOCK_DECREMENT = 3; + + public static final int BLOCK_INCREMENT = 4; + + public static final int TRACK = 5; + + private int type; + private int value; + private boolean isAdjusting; + + public AdjustmentEvent(Adjustable source, int id, int type, int value) { + this(source, id, type, value, false); + } + + public AdjustmentEvent(Adjustable source, int id, int type, int value, + boolean isAdjusting) { + super(source, id); + this.type = type; + this.value = value; + this.isAdjusting = isAdjusting; + } + + public int getValue() { + return value; + } + + public int getAdjustmentType() { + return type; + } + + public boolean getValueIsAdjusting() { + return isAdjusting; + } + + public Adjustable getAdjustable() { + return (Adjustable) source; + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * AdjustmentEvent e = new AdjustmentEvent(new Scrollbar(), + * AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED, + * AdjustmentEvent.UNIT_INCREMENT, 1); + * System.out.println(e); + */ + + String idString = (id == ADJUSTMENT_VALUE_CHANGED ? + "ADJUSTMENT_VALUE_CHANGED" : "unknown type"); //$NON-NLS-1$ //$NON-NLS-2$ + String adjType = null; + + switch (type) { + case UNIT_INCREMENT: + adjType = "UNIT_INCREMENT"; //$NON-NLS-1$ + break; + case UNIT_DECREMENT: + adjType = "UNIT_DECREMENT"; //$NON-NLS-1$ + break; + case BLOCK_INCREMENT: + adjType = "BLOCK_INCREMENT"; //$NON-NLS-1$ + break; + case BLOCK_DECREMENT: + adjType = "BLOCK_DECREMENT"; //$NON-NLS-1$ + break; + case TRACK: + adjType = "TRACK"; //$NON-NLS-1$ + break; + default: + adjType = "unknown type"; //$NON-NLS-1$ + } + + return (idString + ",adjType=" + adjType + ",value=" + value + //$NON-NLS-1$ //$NON-NLS-2$ + ",isAdjusting=" + isAdjusting); //$NON-NLS-1$ + } + +} diff --git a/awt/java/awt/event/AdjustmentListener.java b/awt/java/awt/event/AdjustmentListener.java new file mode 100644 index 000000000..5f6a72473 --- /dev/null +++ b/awt/java/awt/event/AdjustmentListener.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface AdjustmentListener extends EventListener { + + public void adjustmentValueChanged(AdjustmentEvent e); + +} diff --git a/awt/java/awt/event/ComponentAdapter.java b/awt/java/awt/event/ComponentAdapter.java new file mode 100644 index 000000000..c42235f96 --- /dev/null +++ b/awt/java/awt/event/ComponentAdapter.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public abstract class ComponentAdapter implements ComponentListener { + + public ComponentAdapter() { + } + + public void componentHidden(ComponentEvent e) { + } + + public void componentMoved(ComponentEvent e) { + } + + public void componentResized(ComponentEvent e) { + } + + public void componentShown(ComponentEvent e) { + } + +} diff --git a/awt/java/awt/event/ComponentEvent.java b/awt/java/awt/event/ComponentEvent.java new file mode 100644 index 000000000..760d3abf2 --- /dev/null +++ b/awt/java/awt/event/ComponentEvent.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Component; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class ComponentEvent extends AWTEvent { + + private static final long serialVersionUID = 8101406823902992965L; + + public static final int COMPONENT_FIRST = 100; + + public static final int COMPONENT_LAST = 103; + + public static final int COMPONENT_MOVED = 100; + + public static final int COMPONENT_RESIZED = 101; + + public static final int COMPONENT_SHOWN = 102; + + public static final int COMPONENT_HIDDEN = 103; + + public ComponentEvent(Component source, int id) { + super(source, id); + } + + public Component getComponent() { + return (Component) source; + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * ComponentEvent e = new ComponentEvent(new Button("Button"), + * ComponentEvent.COMPONENT_SHOWN); + * System.out.println(e); + */ + + String idString = null; + Component c = getComponent(); + + switch (id) { + case COMPONENT_MOVED: + idString = "COMPONENT_MOVED"; //$NON-NLS-1$ + break; + case COMPONENT_RESIZED: + idString = "COMPONENT_RESIZED"; //$NON-NLS-1$ + break; + case COMPONENT_SHOWN: + return "COMPONENT_SHOWN"; //$NON-NLS-1$ + case COMPONENT_HIDDEN: + return "COMPONENT_HIDDEN"; //$NON-NLS-1$ + default: + return "unknown type"; //$NON-NLS-1$ + } + + return (idString + " (" + c.getX() + "," + c.getY() + //$NON-NLS-1$ //$NON-NLS-2$ + " " + c.getWidth()+ "x" + c.getHeight() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + +} diff --git a/awt/java/awt/event/ComponentListener.java b/awt/java/awt/event/ComponentListener.java new file mode 100644 index 000000000..a5adba2e9 --- /dev/null +++ b/awt/java/awt/event/ComponentListener.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface ComponentListener extends EventListener { + + public void componentHidden(ComponentEvent e); + + public void componentMoved(ComponentEvent e); + + public void componentResized(ComponentEvent e); + + public void componentShown(ComponentEvent e); + +} diff --git a/awt/java/awt/event/ContainerAdapter.java b/awt/java/awt/event/ContainerAdapter.java new file mode 100644 index 000000000..44983c793 --- /dev/null +++ b/awt/java/awt/event/ContainerAdapter.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public abstract class ContainerAdapter implements ContainerListener { + + public ContainerAdapter() { + } + + public void componentAdded(ContainerEvent e) { + } + + public void componentRemoved(ContainerEvent e) { + } + +} diff --git a/awt/java/awt/event/ContainerEvent.java b/awt/java/awt/event/ContainerEvent.java new file mode 100644 index 000000000..372c9e477 --- /dev/null +++ b/awt/java/awt/event/ContainerEvent.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.Component; +//???AWT: import java.awt.Container; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class ContainerEvent extends ComponentEvent { + + private static final long serialVersionUID = -4114942250539772041L; + + public static final int CONTAINER_FIRST = 300; + + public static final int CONTAINER_LAST = 301; + + public static final int COMPONENT_ADDED = 300; + + public static final int COMPONENT_REMOVED = 301; + + private Component child; + + public ContainerEvent(Component src, int id, Component child) { + super(src, id); + this.child = child; + } + + public Component getChild() { + return child; + } + + //???AWT + /* + public Container getContainer() { + return (Container) source; + } + */ + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * ContainerEvent e = new ContainerEvent(new Panel(), + * ContainerEvent.COMPONENT_ADDED, + * new Button("Button")); + * System.out.println(e); + */ + + String idString = null; + + switch (id) { + case COMPONENT_ADDED: + idString = "COMPONENT_ADDED"; //$NON-NLS-1$ + break; + case COMPONENT_REMOVED: + idString = "COMPONENT_REMOVED"; //$NON-NLS-1$ + break; + default: + idString = "unknown type"; //$NON-NLS-1$ + } + + return (idString + ",child=" + child.getName()); //$NON-NLS-1$ + } + +} diff --git a/awt/java/awt/event/ContainerListener.java b/awt/java/awt/event/ContainerListener.java new file mode 100644 index 000000000..517859e43 --- /dev/null +++ b/awt/java/awt/event/ContainerListener.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface ContainerListener extends EventListener { + + public void componentAdded(ContainerEvent e); + + public void componentRemoved(ContainerEvent e); + +} diff --git a/awt/java/awt/event/FocusAdapter.java b/awt/java/awt/event/FocusAdapter.java new file mode 100644 index 000000000..3a3e37fb0 --- /dev/null +++ b/awt/java/awt/event/FocusAdapter.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public abstract class FocusAdapter implements FocusListener { + + public FocusAdapter() { + } + + public void focusGained(FocusEvent e) { + } + + public void focusLost(FocusEvent e) { + } + +} diff --git a/awt/java/awt/event/FocusEvent.java b/awt/java/awt/event/FocusEvent.java new file mode 100644 index 000000000..4a1868957 --- /dev/null +++ b/awt/java/awt/event/FocusEvent.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.Component; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class FocusEvent extends ComponentEvent { + + private static final long serialVersionUID = 523753786457416396L; + + public static final int FOCUS_FIRST = 1004; + + public static final int FOCUS_LAST = 1005; + + public static final int FOCUS_GAINED = 1004; + + public static final int FOCUS_LOST = 1005; + + private boolean temporary; + private Component opposite; + + public FocusEvent(Component source, int id) { + this(source, id, false); + } + + public FocusEvent(Component source, int id, boolean temporary) { + this(source, id, temporary, null); + } + + public FocusEvent(Component source, int id, boolean temporary, Component opposite) { + super(source, id); + this.temporary = temporary; + this.opposite = opposite; + } + + public Component getOppositeComponent() { + return opposite; + } + + public boolean isTemporary() { + return temporary; + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * FocusEvent e = new FocusEvent(new Button("Button0"), + * FocusEvent.FOCUS_GAINED, false, new Button("Button1")); + * System.out.println(e); + */ + + String idString = null; + + switch (id) { + case FOCUS_GAINED: + idString = "FOCUS_GAINED"; //$NON-NLS-1$ + break; + case FOCUS_LOST: + idString = "FOCUS_LOST"; //$NON-NLS-1$ + break; + default: + idString = "unknown type"; //$NON-NLS-1$ + } + + return (idString + + (temporary ? ",temporary" : ",permanent") + //$NON-NLS-1$ //$NON-NLS-2$ + ",opposite=" + opposite); //$NON-NLS-1$ + } + +} diff --git a/awt/java/awt/event/FocusListener.java b/awt/java/awt/event/FocusListener.java new file mode 100644 index 000000000..6bbbd001f --- /dev/null +++ b/awt/java/awt/event/FocusListener.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface FocusListener extends EventListener { + + public void focusGained(FocusEvent e); + + public void focusLost(FocusEvent e); + +} diff --git a/awt/java/awt/event/HierarchyBoundsAdapter.java b/awt/java/awt/event/HierarchyBoundsAdapter.java new file mode 100644 index 000000000..bbfe8ff2d --- /dev/null +++ b/awt/java/awt/event/HierarchyBoundsAdapter.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public abstract class HierarchyBoundsAdapter implements HierarchyBoundsListener { + + public HierarchyBoundsAdapter() { + } + + public void ancestorMoved(HierarchyEvent e) { + } + + public void ancestorResized(HierarchyEvent e) { + } + +} diff --git a/awt/java/awt/event/HierarchyBoundsListener.java b/awt/java/awt/event/HierarchyBoundsListener.java new file mode 100644 index 000000000..3e8f2e790 --- /dev/null +++ b/awt/java/awt/event/HierarchyBoundsListener.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface HierarchyBoundsListener extends EventListener { + + public void ancestorMoved(HierarchyEvent e); + + public void ancestorResized(HierarchyEvent e); + +} diff --git a/awt/java/awt/event/HierarchyEvent.java b/awt/java/awt/event/HierarchyEvent.java new file mode 100644 index 000000000..c1d22f44c --- /dev/null +++ b/awt/java/awt/event/HierarchyEvent.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Component; +//???AWT: import java.awt.Container; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class HierarchyEvent extends AWTEvent { + + private static final long serialVersionUID = -5337576970038043990L; + + public static final int HIERARCHY_FIRST = 1400; + + public static final int HIERARCHY_CHANGED = 1400; + + public static final int ANCESTOR_MOVED = 1401; + + public static final int ANCESTOR_RESIZED = 1402; + + public static final int HIERARCHY_LAST = 1402; + + public static final int PARENT_CHANGED = 1; + + public static final int DISPLAYABILITY_CHANGED = 2; + + public static final int SHOWING_CHANGED = 4; + + //???AWT: private Container changedParent; + private Component changed; + private long changeFlag; + + //???AWT + /* + public HierarchyEvent(Component source, int id, Component changed, + Container changedParent) { + this(source, id, changed, changedParent, 0l); + } + */ + + //???AWT + /* + public HierarchyEvent(Component source, int id, Component changed, + Container changedParent, long changeFlags) { + super(source, id); + + this.changed = changed; + this.changedParent = changedParent; + this.changeFlag = changeFlags; + } + */ + //???AWT: Fake constructor, should be as above. + public HierarchyEvent(Component source, int id, Component changed, + Object changedParent, long changeFlags) { + super(source, id); + +// this.changed = changed; +// this.changedParent = changedParent; +// this.changeFlag = changeFlags; + } + + public Component getComponent() { + return (Component) source; + } + + public long getChangeFlags() { + return changeFlag; + } + + public Component getChanged() { + return changed; + } + + //???AWT + /* + public Container getChangedParent() { + return changedParent; + + } + */ + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * HierarchyEvent e = new HierarchyEvent(new Button("Button"), + * HierarchyEvent.HIERARCHY_CHANGED, + * new Panel(), new Container()); + * System.out.println(e); + */ + String paramString = null; + + switch (id) { + case HIERARCHY_CHANGED: + paramString = "HIERARCHY_CHANGED"; //$NON-NLS-1$ + break; + case ANCESTOR_MOVED: + paramString = "ANCESTOR_MOVED"; //$NON-NLS-1$ + break; + case ANCESTOR_RESIZED: + paramString = "ANCESTOR_RESIZED"; //$NON-NLS-1$ + break; + default: + paramString = "unknown type"; //$NON-NLS-1$ + } + + paramString += " ("; //$NON-NLS-1$ + + if (id == HIERARCHY_CHANGED) { + if ((changeFlag & PARENT_CHANGED) > 0) { + paramString += "PARENT_CHANGED,"; //$NON-NLS-1$ + } + if ((changeFlag & DISPLAYABILITY_CHANGED) > 0) { + paramString += "DISPLAYABILITY_CHANGED,"; //$NON-NLS-1$ + } + if ((changeFlag & SHOWING_CHANGED) > 0) { + paramString += "SHOWING_CHANGED,"; //$NON-NLS-1$ + } + } + + //???AWT + /* + return paramString + "changed=" + changed + //$NON-NLS-1$ + ",changedParent=" + changedParent + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + */ + return paramString; + } + +} diff --git a/awt/java/awt/event/HierarchyListener.java b/awt/java/awt/event/HierarchyListener.java new file mode 100644 index 000000000..ff3d9bcfc --- /dev/null +++ b/awt/java/awt/event/HierarchyListener.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface HierarchyListener extends EventListener { + + public void hierarchyChanged(HierarchyEvent e); + +} diff --git a/awt/java/awt/event/InputEvent.java b/awt/java/awt/event/InputEvent.java new file mode 100644 index 000000000..343b7a3b0 --- /dev/null +++ b/awt/java/awt/event/InputEvent.java @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.Component; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public abstract class InputEvent extends ComponentEvent { + + private static final long serialVersionUID = -2482525981698309786L; + + public static final int SHIFT_MASK = 1; + + public static final int CTRL_MASK = 2; + + public static final int META_MASK = 4; + + public static final int ALT_MASK = 8; + + public static final int ALT_GRAPH_MASK = 32; + + public static final int BUTTON1_MASK = 16; + + public static final int BUTTON2_MASK = 8; + + public static final int BUTTON3_MASK = 4; + + public static final int SHIFT_DOWN_MASK = 64; + + public static final int CTRL_DOWN_MASK = 128; + + public static final int META_DOWN_MASK = 256; + + public static final int ALT_DOWN_MASK = 512; + + public static final int BUTTON1_DOWN_MASK = 1024; + + public static final int BUTTON2_DOWN_MASK = 2048; + + public static final int BUTTON3_DOWN_MASK = 4096; + + public static final int ALT_GRAPH_DOWN_MASK = 8192; + + private static final int DOWN_MASKS = SHIFT_DOWN_MASK | CTRL_DOWN_MASK | + META_DOWN_MASK | ALT_DOWN_MASK | BUTTON1_DOWN_MASK | + BUTTON2_DOWN_MASK | BUTTON3_DOWN_MASK | ALT_GRAPH_DOWN_MASK; + + private long when; + private int modifiersEx; + + public static String getModifiersExText(int modifiers/*Ex*/) { + return MouseEvent.addMouseModifiersExText( + KeyEvent.getKeyModifiersExText(modifiers), modifiers); + } + + static int extractExFlags(int modifiers) { + int exFlags = modifiers & DOWN_MASKS; + + if ((modifiers & SHIFT_MASK) != 0) { + exFlags |= SHIFT_DOWN_MASK; + } + if ((modifiers & CTRL_MASK) != 0) { + exFlags |= CTRL_DOWN_MASK; + } + if ((modifiers & META_MASK) != 0) { + exFlags |= META_DOWN_MASK; + } + if ((modifiers & ALT_MASK) != 0) { + exFlags |= ALT_DOWN_MASK; + } + if ((modifiers & ALT_GRAPH_MASK) != 0) { + exFlags |= ALT_GRAPH_DOWN_MASK; + } + if ((modifiers & BUTTON1_MASK) != 0) { + exFlags |= BUTTON1_DOWN_MASK; + } + if ((modifiers & BUTTON2_MASK) != 0) { + exFlags |= BUTTON2_DOWN_MASK; + } + if ((modifiers & BUTTON3_MASK) != 0) { + exFlags |= BUTTON3_DOWN_MASK; + } + + return exFlags; + } + + InputEvent(Component source, int id, long when, int modifiers) { + super(source, id); + + this.when = when; + modifiersEx = extractExFlags(modifiers); + } + + public int getModifiers() { + int modifiers = 0; + + if ((modifiersEx & SHIFT_DOWN_MASK) != 0) { + modifiers |= SHIFT_MASK; + } + if ((modifiersEx & CTRL_DOWN_MASK) != 0) { + modifiers |= CTRL_MASK; + } + if ((modifiersEx & META_DOWN_MASK) != 0) { + modifiers |= META_MASK; + } + if ((modifiersEx & ALT_DOWN_MASK) != 0) { + modifiers |= ALT_MASK; + } + if ((modifiersEx & ALT_GRAPH_DOWN_MASK) != 0) { + modifiers |= ALT_GRAPH_MASK; + } + if ((modifiersEx & BUTTON1_DOWN_MASK) != 0) { + modifiers |= BUTTON1_MASK; + } + if ((modifiersEx & BUTTON2_DOWN_MASK) != 0) { + modifiers |= BUTTON2_MASK; + } + if ((modifiersEx & BUTTON3_DOWN_MASK) != 0) { + modifiers |= BUTTON3_MASK; + } + + return modifiers; + } + + public int getModifiersEx() { + return modifiersEx; + } + + void setModifiers(int modifiers) { + modifiersEx = extractExFlags(modifiers); + } + + public boolean isAltDown() { + return ((modifiersEx & ALT_DOWN_MASK) != 0); + } + + public boolean isAltGraphDown() { + return ((modifiersEx & ALT_GRAPH_DOWN_MASK) != 0); + } + + public boolean isControlDown() { + return ((modifiersEx & CTRL_DOWN_MASK) != 0); + } + + public boolean isMetaDown() { + return ((modifiersEx & META_DOWN_MASK) != 0); + } + + public boolean isShiftDown() { + return ((modifiersEx & SHIFT_DOWN_MASK) != 0); + } + + public long getWhen() { + return when; + } + + @Override + public void consume() { + super.consume(); + } + + @Override + public boolean isConsumed() { + return super.isConsumed(); + } + +} diff --git a/awt/java/awt/event/InputMethodEvent.java b/awt/java/awt/event/InputMethodEvent.java new file mode 100644 index 000000000..be001a580 --- /dev/null +++ b/awt/java/awt/event/InputMethodEvent.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.font.TextHitInfo; +import java.text.AttributedCharacterIterator; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class InputMethodEvent extends AWTEvent { + + private static final long serialVersionUID = 4727190874778922661L; + + public static final int INPUT_METHOD_FIRST = 1100; + + public static final int INPUT_METHOD_TEXT_CHANGED = 1100; + + public static final int CARET_POSITION_CHANGED = 1101; + + public static final int INPUT_METHOD_LAST = 1101; + + private AttributedCharacterIterator text; + private TextHitInfo visiblePosition; + private TextHitInfo caret; + private int committedCharacterCount; + private long when; + + public InputMethodEvent(Component src, int id, + TextHitInfo caret, + TextHitInfo visiblePos) { + this(src, id, null, 0, caret, visiblePos); + } + + public InputMethodEvent(Component src, int id, + AttributedCharacterIterator text, + int commitedCharCount, + TextHitInfo caret, + TextHitInfo visiblePos) { + this(src, id, 0l, text, commitedCharCount, caret, visiblePos); + } + + public InputMethodEvent(Component src, int id, long when, + AttributedCharacterIterator text, + int committedCharacterCount, + TextHitInfo caret, + TextHitInfo visiblePos) { + super(src, id); + + if ((id < INPUT_METHOD_FIRST) || (id > INPUT_METHOD_LAST)) { + // awt.18E=Wrong event id + throw new IllegalArgumentException(Messages.getString("awt.18E")); //$NON-NLS-1$ + } + if ((id == CARET_POSITION_CHANGED) && (text != null)) { + // awt.18F=Text must be null for CARET_POSITION_CHANGED + throw new IllegalArgumentException(Messages.getString("awt.18F")); //$NON-NLS-1$ + } + if ((text != null) && + ((committedCharacterCount < 0) || + (committedCharacterCount > + (text.getEndIndex() - text.getBeginIndex())))) { + // awt.190=Wrong committedCharacterCount + throw new IllegalArgumentException(Messages.getString("awt.190")); //$NON-NLS-1$ + } + + this.when = when; + this.text = text; + this.caret = caret; + this.visiblePosition = visiblePos; + this.committedCharacterCount = committedCharacterCount; + } + + public TextHitInfo getCaret() { + return caret; + } + + public int getCommittedCharacterCount() { + return committedCharacterCount; + } + + public AttributedCharacterIterator getText() { + return text; + } + + public TextHitInfo getVisiblePosition() { + return visiblePosition; + } + + public long getWhen() { + return when; + } + + @Override + public void consume() { + super.consume(); + } + + @Override + public boolean isConsumed() { + return super.isConsumed(); + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * InputMethodEvent e = new InputMethodEvent(new Component(){}, + * InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, + * TextHitInfo.leading(1), TextHitInfo.trailing(2)); + * System.out.println(e); + */ + String typeString = null; + + switch (id) { + case INPUT_METHOD_TEXT_CHANGED: + typeString = "INPUT_METHOD_TEXT_CHANGED"; //$NON-NLS-1$ + break; + case CARET_POSITION_CHANGED: + typeString = "CARET_POSITION_CHANGED"; //$NON-NLS-1$ + break; + default: + typeString = "unknown type"; //$NON-NLS-1$ + } + + return typeString + ",text=" + text + //$NON-NLS-1$ + ",commitedCharCount=" + committedCharacterCount + //$NON-NLS-1$ + ",caret=" + caret + ",visiblePosition=" + visiblePosition; //$NON-NLS-1$ //$NON-NLS-2$ + } + +} diff --git a/awt/java/awt/event/InputMethodListener.java b/awt/java/awt/event/InputMethodListener.java new file mode 100644 index 000000000..85eaa7e6c --- /dev/null +++ b/awt/java/awt/event/InputMethodListener.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface InputMethodListener extends EventListener { + + public void caretPositionChanged(InputMethodEvent e); + + public void inputMethodTextChanged(InputMethodEvent e); + +} diff --git a/awt/java/awt/event/InvocationEvent.java b/awt/java/awt/event/InvocationEvent.java new file mode 100644 index 000000000..58e3b7298 --- /dev/null +++ b/awt/java/awt/event/InvocationEvent.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.ActiveEvent; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class InvocationEvent extends AWTEvent implements ActiveEvent { + + private static final long serialVersionUID = 436056344909459450L; + + public static final int INVOCATION_FIRST = 1200; + + public static final int INVOCATION_DEFAULT = 1200; + + public static final int INVOCATION_LAST = 1200; + + protected Runnable runnable; + + protected Object notifier; + + protected boolean catchExceptions; + + private long when; + private Throwable throwable; + + public InvocationEvent(Object source, Runnable runnable) { + this(source, runnable, null, false); + } + + public InvocationEvent(Object source, Runnable runnable, + Object notifier, boolean catchExceptions) { + this(source, INVOCATION_DEFAULT, runnable, notifier, catchExceptions); + } + + protected InvocationEvent(Object source, int id, Runnable runnable, + Object notifier, boolean catchExceptions) + { + super(source, id); + + // awt.18C=Cannot invoke null runnable + assert runnable != null : Messages.getString("awt.18C"); //$NON-NLS-1$ + + if (source == null) { + // awt.18D=Source is null + throw new IllegalArgumentException(Messages.getString("awt.18D")); //$NON-NLS-1$ + } + this.runnable = runnable; + this.notifier = notifier; + this.catchExceptions = catchExceptions; + + throwable = null; + when = System.currentTimeMillis(); + } + + public void dispatch() { + if (!catchExceptions) { + runAndNotify(); + } else { + try { + runAndNotify(); + } catch (Throwable t) { + throwable = t; + } + } + } + + private void runAndNotify() { + if (notifier != null) { + synchronized(notifier) { + try { + runnable.run(); + } finally { + notifier.notifyAll(); + } + } + } else { + runnable.run(); + } + } + + public Exception getException() { + return (throwable != null && throwable instanceof Exception) ? + (Exception)throwable : null; + } + + public Throwable getThrowable() { + return throwable; + } + + public long getWhen() { + return when; + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * InvocationEvent e = new InvocationEvent(new Component(){}, + * new Runnable() { public void run(){} }); + * System.out.println(e); + */ + + return ((id == INVOCATION_DEFAULT ? "INVOCATION_DEFAULT" : "unknown type") + //$NON-NLS-1$ //$NON-NLS-2$ + ",runnable=" + runnable + //$NON-NLS-1$ + ",notifier=" + notifier + //$NON-NLS-1$ + ",catchExceptions=" + catchExceptions + //$NON-NLS-1$ + ",when=" + when); //$NON-NLS-1$ + } + +} diff --git a/awt/java/awt/event/ItemEvent.java b/awt/java/awt/event/ItemEvent.java new file mode 100644 index 000000000..09908f24f --- /dev/null +++ b/awt/java/awt/event/ItemEvent.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.ItemSelectable; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class ItemEvent extends AWTEvent { + + private static final long serialVersionUID = -608708132447206933L; + + public static final int ITEM_FIRST = 701; + + public static final int ITEM_LAST = 701; + + public static final int ITEM_STATE_CHANGED = 701; + + public static final int SELECTED = 1; + + public static final int DESELECTED = 2; + + private Object item; + private int stateChange; + + public ItemEvent(ItemSelectable source, int id, Object item, int stateChange) { + super(source, id); + + this.item = item; + this.stateChange = stateChange; + } + + public Object getItem() { + return item; + } + + public int getStateChange() { + return stateChange; + } + + public ItemSelectable getItemSelectable() { + return (ItemSelectable) source; + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * Checkbox c = new Checkbox("Checkbox", true); + * ItemEvent e = new ItemEvent(c, ItemEvent.ITEM_STATE_CHANGED, + * c, ItemEvent.SELECTED); + * System.out.println(e); + */ + + String stateString = null; + + switch (stateChange) { + case SELECTED: + stateString = "SELECTED"; //$NON-NLS-1$ + break; + case DESELECTED: + stateString = "DESELECTED"; //$NON-NLS-1$ + break; + default: + stateString = "unknown type"; //$NON-NLS-1$ + } + + return ((id == ITEM_STATE_CHANGED ? "ITEM_STATE_CHANGED" : "unknown type") + //$NON-NLS-1$ //$NON-NLS-2$ + ",item=" + item + ",stateChange=" + stateString); //$NON-NLS-1$ //$NON-NLS-2$ + } + +} diff --git a/awt/java/awt/event/ItemListener.java b/awt/java/awt/event/ItemListener.java new file mode 100644 index 000000000..8dec67322 --- /dev/null +++ b/awt/java/awt/event/ItemListener.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface ItemListener extends EventListener { + + public void itemStateChanged(ItemEvent e); + +} diff --git a/awt/java/awt/event/KeyAdapter.java b/awt/java/awt/event/KeyAdapter.java new file mode 100644 index 000000000..a96cca8d8 --- /dev/null +++ b/awt/java/awt/event/KeyAdapter.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public abstract class KeyAdapter implements KeyListener { + + public KeyAdapter() { + } + + public void keyPressed(KeyEvent e) { + } + + public void keyReleased(KeyEvent e) { + } + + public void keyTyped(KeyEvent e) { + } + +} diff --git a/awt/java/awt/event/KeyEvent.java b/awt/java/awt/event/KeyEvent.java new file mode 100644 index 000000000..8627f708d --- /dev/null +++ b/awt/java/awt/event/KeyEvent.java @@ -0,0 +1,687 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.Component; +import java.awt.Toolkit; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class KeyEvent extends InputEvent { + + private static final long serialVersionUID = -2352130953028126954L; + + public static final int KEY_FIRST = 400; + + public static final int KEY_LAST = 402; + + public static final int KEY_TYPED = 400; + + public static final int KEY_PRESSED = 401; + + public static final int KEY_RELEASED = 402; + + public static final int VK_ENTER = 10; + + public static final int VK_BACK_SPACE = 8; + + public static final int VK_TAB = 9; + + public static final int VK_CANCEL = 3; + + public static final int VK_CLEAR = 12; + + public static final int VK_SHIFT = 16; + + public static final int VK_CONTROL = 17; + + public static final int VK_ALT = 18; + + public static final int VK_PAUSE = 19; + + public static final int VK_CAPS_LOCK = 20; + + public static final int VK_ESCAPE = 27; + + public static final int VK_SPACE = 32; + + public static final int VK_PAGE_UP = 33; + + public static final int VK_PAGE_DOWN = 34; + + public static final int VK_END = 35; + + public static final int VK_HOME = 36; + + public static final int VK_LEFT = 37; + + public static final int VK_UP = 38; + + public static final int VK_RIGHT = 39; + + public static final int VK_DOWN = 40; + + public static final int VK_COMMA = 44; + + public static final int VK_MINUS = 45; + + public static final int VK_PERIOD = 46; + + public static final int VK_SLASH = 47; + + public static final int VK_0 = 48; + + public static final int VK_1 = 49; + + public static final int VK_2 = 50; + + public static final int VK_3 = 51; + + public static final int VK_4 = 52; + + public static final int VK_5 = 53; + + public static final int VK_6 = 54; + + public static final int VK_7 = 55; + + public static final int VK_8 = 56; + + public static final int VK_9 = 57; + + public static final int VK_SEMICOLON = 59; + + public static final int VK_EQUALS = 61; + + public static final int VK_A = 65; + + public static final int VK_B = 66; + + public static final int VK_C = 67; + + public static final int VK_D = 68; + + public static final int VK_E = 69; + + public static final int VK_F = 70; + + public static final int VK_G = 71; + + public static final int VK_H = 72; + + public static final int VK_I = 73; + + public static final int VK_J = 74; + + public static final int VK_K = 75; + + public static final int VK_L = 76; + + public static final int VK_M = 77; + + public static final int VK_N = 78; + + public static final int VK_O = 79; + + public static final int VK_P = 80; + + public static final int VK_Q = 81; + + public static final int VK_R = 82; + + public static final int VK_S = 83; + + public static final int VK_T = 84; + + public static final int VK_U = 85; + + public static final int VK_V = 86; + + public static final int VK_W = 87; + + public static final int VK_X = 88; + + public static final int VK_Y = 89; + + public static final int VK_Z = 90; + + public static final int VK_OPEN_BRACKET = 91; + + public static final int VK_BACK_SLASH = 92; + + public static final int VK_CLOSE_BRACKET = 93; + + public static final int VK_NUMPAD0 = 96; + + public static final int VK_NUMPAD1 = 97; + + public static final int VK_NUMPAD2 = 98; + + public static final int VK_NUMPAD3 = 99; + + public static final int VK_NUMPAD4 = 100; + + public static final int VK_NUMPAD5 = 101; + + public static final int VK_NUMPAD6 = 102; + + public static final int VK_NUMPAD7 = 103; + + public static final int VK_NUMPAD8 = 104; + + public static final int VK_NUMPAD9 = 105; + + public static final int VK_MULTIPLY = 106; + + public static final int VK_ADD = 107; + + public static final int VK_SEPARATER = 108; + + public static final int VK_SEPARATOR = 108; + + public static final int VK_SUBTRACT = 109; + + public static final int VK_DECIMAL = 110; + + public static final int VK_DIVIDE = 111; + + public static final int VK_DELETE = 127; + + public static final int VK_NUM_LOCK = 144; + + public static final int VK_SCROLL_LOCK = 145; + + public static final int VK_F1 = 112; + + public static final int VK_F2 = 113; + + public static final int VK_F3 = 114; + + public static final int VK_F4 = 115; + + public static final int VK_F5 = 116; + + public static final int VK_F6 = 117; + + public static final int VK_F7 = 118; + + public static final int VK_F8 = 119; + + public static final int VK_F9 = 120; + + public static final int VK_F10 = 121; + + public static final int VK_F11 = 122; + + public static final int VK_F12 = 123; + + public static final int VK_F13 = 61440; + + public static final int VK_F14 = 61441; + + public static final int VK_F15 = 61442; + + public static final int VK_F16 = 61443; + + public static final int VK_F17 = 61444; + + public static final int VK_F18 = 61445; + + public static final int VK_F19 = 61446; + + public static final int VK_F20 = 61447; + + public static final int VK_F21 = 61448; + + public static final int VK_F22 = 61449; + + public static final int VK_F23 = 61450; + + public static final int VK_F24 = 61451; + + public static final int VK_PRINTSCREEN = 154; + + public static final int VK_INSERT = 155; + + public static final int VK_HELP = 156; + + public static final int VK_META = 157; + + public static final int VK_BACK_QUOTE = 192; + + public static final int VK_QUOTE = 222; + + public static final int VK_KP_UP = 224; + + public static final int VK_KP_DOWN = 225; + + public static final int VK_KP_LEFT = 226; + + public static final int VK_KP_RIGHT = 227; + + public static final int VK_DEAD_GRAVE = 128; + + public static final int VK_DEAD_ACUTE = 129; + + public static final int VK_DEAD_CIRCUMFLEX = 130; + + public static final int VK_DEAD_TILDE = 131; + + public static final int VK_DEAD_MACRON = 132; + + public static final int VK_DEAD_BREVE = 133; + + public static final int VK_DEAD_ABOVEDOT = 134; + + public static final int VK_DEAD_DIAERESIS = 135; + + public static final int VK_DEAD_ABOVERING = 136; + + public static final int VK_DEAD_DOUBLEACUTE = 137; + + public static final int VK_DEAD_CARON = 138; + + public static final int VK_DEAD_CEDILLA = 139; + + public static final int VK_DEAD_OGONEK = 140; + + public static final int VK_DEAD_IOTA = 141; + + public static final int VK_DEAD_VOICED_SOUND = 142; + + public static final int VK_DEAD_SEMIVOICED_SOUND = 143; + + public static final int VK_AMPERSAND = 150; + + public static final int VK_ASTERISK = 151; + + public static final int VK_QUOTEDBL = 152; + + public static final int VK_LESS = 153; + + public static final int VK_GREATER = 160; + + public static final int VK_BRACELEFT = 161; + + public static final int VK_BRACERIGHT = 162; + + public static final int VK_AT = 512; + + public static final int VK_COLON = 513; + + public static final int VK_CIRCUMFLEX = 514; + + public static final int VK_DOLLAR = 515; + + public static final int VK_EURO_SIGN = 516; + + public static final int VK_EXCLAMATION_MARK = 517; + + public static final int VK_INVERTED_EXCLAMATION_MARK = 518; + + public static final int VK_LEFT_PARENTHESIS = 519; + + public static final int VK_NUMBER_SIGN = 520; + + public static final int VK_PLUS = 521; + + public static final int VK_RIGHT_PARENTHESIS = 522; + + public static final int VK_UNDERSCORE = 523; + + public static final int VK_FINAL = 24; + + public static final int VK_WINDOWS = 524; + + public static final int VK_CONTEXT_MENU = 525; + + public static final int VK_CONVERT = 28; + + public static final int VK_NONCONVERT = 29; + + public static final int VK_ACCEPT = 30; + + public static final int VK_MODECHANGE = 31; + + public static final int VK_KANA = 21; + + public static final int VK_KANJI = 25; + + public static final int VK_ALPHANUMERIC = 240; + + public static final int VK_KATAKANA = 241; + + public static final int VK_HIRAGANA = 242; + + public static final int VK_FULL_WIDTH = 243; + + public static final int VK_HALF_WIDTH = 244; + + public static final int VK_ROMAN_CHARACTERS = 245; + + public static final int VK_ALL_CANDIDATES = 256; + + public static final int VK_PREVIOUS_CANDIDATE = 257; + + public static final int VK_CODE_INPUT = 258; + + public static final int VK_JAPANESE_KATAKANA = 259; + + public static final int VK_JAPANESE_HIRAGANA = 260; + + public static final int VK_JAPANESE_ROMAN = 261; + + public static final int VK_KANA_LOCK = 262; + + public static final int VK_INPUT_METHOD_ON_OFF = 263; + + public static final int VK_CUT = 65489; + + public static final int VK_COPY = 65485; + + public static final int VK_PASTE = 65487; + + public static final int VK_UNDO = 65483; + + public static final int VK_AGAIN = 65481; + + public static final int VK_FIND = 65488; + + public static final int VK_PROPS = 65482; + + public static final int VK_STOP = 65480; + + public static final int VK_COMPOSE = 65312; + + public static final int VK_ALT_GRAPH = 65406; + + public static final int VK_BEGIN = 65368; + + public static final int VK_UNDEFINED = 0; + + public static final char CHAR_UNDEFINED = (char)(-1); + + public static final int KEY_LOCATION_UNKNOWN = 0; + + public static final int KEY_LOCATION_STANDARD = 1; + + public static final int KEY_LOCATION_LEFT = 2; + + public static final int KEY_LOCATION_RIGHT = 3; + + public static final int KEY_LOCATION_NUMPAD = 4; + + private int keyCode; + private char keyChar; + private int keyLocation; + + public static String getKeyModifiersText(int modifiers) { + return getKeyModifiersExText(extractExFlags(modifiers)); + } + + static String getKeyModifiersExText(int modifiersEx) { + String text = ""; //$NON-NLS-1$ + + if ((modifiersEx & InputEvent.META_DOWN_MASK) != 0) { + text += Toolkit.getProperty("AWT.meta", "Meta"); //$NON-NLS-1$ //$NON-NLS-2$ + } + if ((modifiersEx & InputEvent.CTRL_DOWN_MASK) != 0) { + text += ((text.length() > 0) ? "+" : "") + //$NON-NLS-1$ //$NON-NLS-2$ + Toolkit.getProperty("AWT.control", "Ctrl"); //$NON-NLS-1$ //$NON-NLS-2$ + } + if ((modifiersEx & InputEvent.ALT_DOWN_MASK) != 0) { + text += ((text.length() > 0) ? "+" : "") + //$NON-NLS-1$ //$NON-NLS-2$ + Toolkit.getProperty("AWT.alt", "Alt"); //$NON-NLS-1$ //$NON-NLS-2$ + } + if ((modifiersEx & InputEvent.SHIFT_DOWN_MASK) != 0) { + text += ((text.length() > 0) ? "+" : "") + //$NON-NLS-1$ //$NON-NLS-2$ + Toolkit.getProperty("AWT.shift", "Shift"); //$NON-NLS-1$ //$NON-NLS-2$ + } + if ((modifiersEx & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) { + text += ((text.length() > 0) ? "+" : "") + //$NON-NLS-1$ //$NON-NLS-2$ + Toolkit.getProperty("AWT.altGraph", "Alt Graph"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + return text; + } + + public static String getKeyText(int keyCode) { + String[] rawName = getPublicStaticFinalIntFieldName(keyCode); //$NON-NLS-1$ + + if ((rawName == null) || (rawName.length == 0)) { + return ("Unknown keyCode: " + (keyCode >= 0 ? "0x" : "-0x") + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + Integer.toHexString(Math.abs(keyCode))); + } + + String propertyName = getPropertyName(rawName); + String defaultName = getDefaultName(rawName); + + return Toolkit.getProperty(propertyName, defaultName); + } + + private static String getDefaultName(String[] rawName) { + String name = ""; //$NON-NLS-1$ + + for (int i = 0; true; i++) { + String part = rawName[i]; + + name += new String(new char[] {part.charAt(0)}).toUpperCase() + + part.substring(1).toLowerCase(); + + if (i == (rawName.length - 1)) { + break; + } + name += " "; //$NON-NLS-1$ + } + + return name; + } + + private static String getPropertyName(String[] rawName) { + String name = rawName[0].toLowerCase(); + + for (int i = 1; i < rawName.length; i++) { + String part = rawName[i]; + + name += new String(new char[] {part.charAt(0)}).toUpperCase() + + part.substring(1).toLowerCase(); + } + + return ("AWT." + name); //$NON-NLS-1$ + } + + private static String[] getPublicStaticFinalIntFieldName(int value) { + Field[] allFields = KeyEvent.class.getDeclaredFields(); + + try { + for (Field field : allFields) { + Class ssalc = field.getType(); + int modifiers = field.getModifiers(); + + if (ssalc.isPrimitive() && ssalc.getName().equals("int") && //$NON-NLS-1$ + Modifier.isFinal(modifiers) && Modifier.isPublic(modifiers) && + Modifier.isStatic(modifiers)) + { + if (field.getInt(null) == value){ + final String name = field.getName(); + final int prefixLength = name.indexOf("_") + 1; + return name.substring(prefixLength).split("_"); //$NON-NLS-1$ + } + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + return null; + } + + @Deprecated + public KeyEvent(Component src, int id, + long when, int modifiers, + int keyCode) { + this(src, id, when, modifiers, keyCode, + (keyCode > (2 << 7) - 1) ? CHAR_UNDEFINED : (char) keyCode); + } + + public KeyEvent(Component src, int id, + long when, int modifiers, + int keyCode, char keyChar) { + this(src, id, when, modifiers, keyCode, keyChar, KEY_LOCATION_UNKNOWN); + } + + public KeyEvent(Component src, int id, + long when, int modifiers, + int keyCode, char keyChar, + int keyLocation) { + super(src, id, when, modifiers); + + if (id == KEY_TYPED) { + if (keyCode != VK_UNDEFINED) { + // awt.191=Invalid keyCode for KEY_TYPED event, must be VK_UNDEFINED + throw new IllegalArgumentException(Messages.getString("awt.191")); //$NON-NLS-1$ + } + if (keyChar == CHAR_UNDEFINED) { + // awt.192=Invalid keyChar for KEY_TYPED event, can't be CHAR_UNDEFINED + throw new IllegalArgumentException(Messages.getString("awt.192")); //$NON-NLS-1$ + } + } + + if ((keyLocation < KEY_LOCATION_UNKNOWN) + || (keyLocation > KEY_LOCATION_NUMPAD)) { + // awt.297=Invalid keyLocation + throw new IllegalArgumentException(Messages.getString("awt.297")); //$NON-NLS-1$ + } + + this.keyChar = keyChar; + this.keyLocation = keyLocation; + this.keyCode = keyCode; + } + + public int getKeyCode() { + return keyCode; + } + + public void setKeyCode(int keyCode) { + this.keyCode = keyCode; + } + + public char getKeyChar() { + return keyChar; + } + + public void setKeyChar(char keyChar) { + this.keyChar = keyChar; + } + + public int getKeyLocation() { + return keyLocation; + } + + @Override + @Deprecated + public void setModifiers(int modifiers) { + super.setModifiers(modifiers); + } + + public boolean isActionKey() { + return ((keyChar == CHAR_UNDEFINED) && (keyCode != VK_UNDEFINED) && + !((keyCode == VK_ALT) || (keyCode == VK_ALT_GRAPH) || + (keyCode == VK_CONTROL) || (keyCode == VK_META) || (keyCode == VK_SHIFT))); + } + + @Override + public String paramString() { + /* + * The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * KeyEvent e = new KeyEvent(new Component() {}, + * KeyEvent.KEY_PRESSED, 0, + * KeyEvent.CTRL_DOWN_MASK|KeyEvent.SHIFT_DOWN_MASK, + * KeyEvent.VK_A, 'A', KeyEvent.KEY_LOCATION_STANDARD); + * System.out.println(e); + */ + + String idString = null; + String locString = null; + String paramString = null; + String keyCharString = (keyChar == '\n') ? + keyCharString = getKeyText(VK_ENTER) : "'" + keyChar + "'"; //$NON-NLS-1$ //$NON-NLS-2$ + + switch (id) { + case KEY_PRESSED: + idString = "KEY_PRESSED"; //$NON-NLS-1$ + break; + case KEY_RELEASED: + idString = "KEY_RELEASED"; //$NON-NLS-1$ + break; + case KEY_TYPED: + idString = "KEY_TYPED"; //$NON-NLS-1$ + break; + default: + idString = "unknown type"; //$NON-NLS-1$ + } + + switch(keyLocation){ + case KEY_LOCATION_STANDARD: + locString = "KEY_LOCATION_STANDARD"; //$NON-NLS-1$ + break; + case KEY_LOCATION_LEFT: + locString = "KEY_LOCATION_LEFT"; //$NON-NLS-1$ + break; + case KEY_LOCATION_RIGHT: + locString = "KEY_LOCATION_RIGHT"; //$NON-NLS-1$ + break; + case KEY_LOCATION_NUMPAD: + locString = "KEY_LOCATION_NUMPAD"; //$NON-NLS-1$ + break; + case KEY_LOCATION_UNKNOWN: + locString = "KEY_LOCATION_UNKNOWN"; //$NON-NLS-1$ + break; + default: + locString = "unknown type"; //$NON-NLS-1$ + } + + paramString = idString + ",keyCode=" + keyCode; //$NON-NLS-1$ + if (isActionKey()) { + paramString += "," + getKeyText(keyCode); //$NON-NLS-1$ + } else { + paramString += ",keyChar=" + keyCharString; //$NON-NLS-1$ + } + if (getModifiersEx() > 0) { + paramString += ",modifiers=" + getModifiersExText(getModifiersEx()) + //$NON-NLS-1$ + ",extModifiers=" + getModifiersExText(getModifiersEx()); //$NON-NLS-1$ + } + paramString += ",keyLocation=" + locString; //$NON-NLS-1$ + + return paramString; + } + +} diff --git a/awt/java/awt/event/KeyListener.java b/awt/java/awt/event/KeyListener.java new file mode 100644 index 000000000..ec144dfef --- /dev/null +++ b/awt/java/awt/event/KeyListener.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface KeyListener extends EventListener { + + public void keyPressed(KeyEvent e); + + public void keyReleased(KeyEvent e); + + public void keyTyped(KeyEvent e); + +} diff --git a/awt/java/awt/event/MouseAdapter.java b/awt/java/awt/event/MouseAdapter.java new file mode 100644 index 000000000..dc19173c3 --- /dev/null +++ b/awt/java/awt/event/MouseAdapter.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public abstract class MouseAdapter implements MouseListener { + + public MouseAdapter() { + } + + public void mouseClicked(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseReleased(MouseEvent e) { + } + +} diff --git a/awt/java/awt/event/MouseEvent.java b/awt/java/awt/event/MouseEvent.java new file mode 100644 index 000000000..2b1fa8b2a --- /dev/null +++ b/awt/java/awt/event/MouseEvent.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.Component; +import java.awt.Point; +import java.awt.Toolkit; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class MouseEvent extends InputEvent { + + private static final long serialVersionUID = -991214153494842848L; + + public static final int MOUSE_FIRST = 500; + + public static final int MOUSE_LAST = 507; + + public static final int MOUSE_CLICKED = 500; + + public static final int MOUSE_PRESSED = 501; + + public static final int MOUSE_RELEASED = 502; + + public static final int MOUSE_MOVED = 503; + + public static final int MOUSE_ENTERED = 504; + + public static final int MOUSE_EXITED = 505; + + public static final int MOUSE_DRAGGED = 506; + + public static final int MOUSE_WHEEL = 507; + + public static final int NOBUTTON = 0; + + public static final int BUTTON1 = 1; + + public static final int BUTTON2 = 2; + + public static final int BUTTON3 = 3; + + private boolean popupTrigger; + private int clickCount; + private int button; + private int x; + private int y; + + public static String getMouseModifiersText(int modifiers) { + final StringBuffer text = new StringBuffer(); + + if ((modifiers & META_MASK) != 0) { + text.append(Toolkit.getProperty("AWT.meta", "Meta")).append("+"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + if ((modifiers & SHIFT_MASK) != 0) { + text.append(Toolkit.getProperty("AWT.shift", "Shift")).append("+"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + if ((modifiers & CTRL_MASK) != 0) { + text.append(Toolkit.getProperty("AWT.control", "Ctrl")).append("+"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + if ((modifiers & ALT_MASK) != 0) { + text.append(Toolkit.getProperty("AWT.alt", "Alt")).append("+"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + if ((modifiers & ALT_GRAPH_MASK) != 0) { + text.append(Toolkit.getProperty("AWT.altGraph", "Alt Graph")).append("+"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + if ((modifiers & BUTTON1_MASK) != 0) { + text.append(Toolkit.getProperty("AWT.button1", "Button1")).append("+"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + if ((modifiers & BUTTON2_MASK) != 0) { + text.append(Toolkit.getProperty("AWT.button2", "Button2")).append("+"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + if ((modifiers & BUTTON3_MASK) != 0) { + text.append(Toolkit.getProperty("AWT.button3", "Button3")).append("+"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + return text.length() == 0 ? text.toString() : text.substring(0, text + .length() - 1); + } + + static String addMouseModifiersExText(String text, int modifiersEx) { + if ((modifiersEx & InputEvent.BUTTON1_DOWN_MASK) != 0) { + text += ((text.length() > 0) ? "+" : "") + //$NON-NLS-1$ //$NON-NLS-2$ + Toolkit.getProperty("AWT.button1", "Button1"); //$NON-NLS-1$ //$NON-NLS-2$ + } + if ((modifiersEx & InputEvent.BUTTON2_DOWN_MASK) != 0) { + text += ((text.length() > 0) ? "+" : "") + //$NON-NLS-1$ //$NON-NLS-2$ + Toolkit.getProperty("AWT.button2", "Button2"); //$NON-NLS-1$ //$NON-NLS-2$ + } + if ((modifiersEx & InputEvent.BUTTON3_DOWN_MASK) != 0) { + text += ((text.length() > 0) ? "+" : "") + //$NON-NLS-1$ //$NON-NLS-2$ + Toolkit.getProperty("AWT.button3", "Button3"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + return text; + } + + public MouseEvent(Component source, int id, long when, + int modifiers, int x, int y, + int clickCount, boolean popupTrigger) { + this(source, id, when, modifiers, x, y, + clickCount, popupTrigger, NOBUTTON); + } + + public MouseEvent(Component source, int id, long when, + int modifiers, int x, int y, + int clickCount, boolean popupTrigger, int button) { + super(source, id, when, modifiers); + + if ((button != NOBUTTON) && (button != BUTTON1) && + (button != BUTTON2) && (button != BUTTON3)) { + // awt.18B=Invalid button value + throw new IllegalArgumentException(Messages.getString("awt.18B")); //$NON-NLS-1$ + } + + this.popupTrigger = popupTrigger; + this.clickCount = clickCount; + this.button = button; + this.x = x; + this.y = y; + } + + public int getButton() { + return button; + } + + public int getClickCount() { + return clickCount; + } + + public Point getPoint() { + return new Point(x, y); + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public boolean isPopupTrigger() { + return popupTrigger; + } + + public void translatePoint(int x, int y) { + this.x += x; + this.y += y; + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * MouseEvent e = new MouseEvent(new Component(){}, + * MouseEvent.MOUSE_PRESSED, 0, + * MouseEvent.BUTTON1_DOWN_MASK|MouseEvent.CTRL_DOWN_MASK, + * 10, 20, 1, false, MouseEvent.BUTTON1); + * System.out.println(e); + */ + + String idString = null; + String paramString = null; + + switch (id) { + case MOUSE_MOVED: + idString = "MOUSE_MOVED"; //$NON-NLS-1$ + break; + case MOUSE_CLICKED: + idString = "MOUSE_CLICKED"; //$NON-NLS-1$ + break; + case MOUSE_PRESSED: + idString = "MOUSE_PRESSED"; //$NON-NLS-1$ + break; + case MOUSE_RELEASED: + idString = "MOUSE_RELEASED"; //$NON-NLS-1$ + break; + case MOUSE_DRAGGED: + idString = "MOUSE_DRAGGED"; //$NON-NLS-1$ + break; + case MOUSE_ENTERED: + idString = "MOUSE_ENTERED"; //$NON-NLS-1$ + break; + case MOUSE_EXITED: + idString = "MOUSE_EXITED"; //$NON-NLS-1$ + break; + case MOUSE_WHEEL: + idString = "MOUSE_WHEEL"; //$NON-NLS-1$ + break; + default: + idString = "unknown type"; //$NON-NLS-1$ + } + + paramString = idString + ",(" + getX() + "," + getY() + ")" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + ",button=" + button; //$NON-NLS-1$ + if (getModifiersEx() > 0) { + paramString += + ",modifiers=" + getModifiersExText(getModifiersEx()) + //$NON-NLS-1$ + ",extModifiers=" + getModifiersExText(getModifiersEx()); //$NON-NLS-1$ + } + paramString += ",clickCount=" + getClickCount(); //$NON-NLS-1$ + + return paramString; + } + +} diff --git a/awt/java/awt/event/MouseListener.java b/awt/java/awt/event/MouseListener.java new file mode 100644 index 000000000..95879b90e --- /dev/null +++ b/awt/java/awt/event/MouseListener.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface MouseListener extends EventListener { + + public void mouseClicked(MouseEvent e); + + public void mouseEntered(MouseEvent e); + + public void mouseExited(MouseEvent e); + + public void mousePressed(MouseEvent e); + + public void mouseReleased(MouseEvent e); + +} diff --git a/awt/java/awt/event/MouseMotionAdapter.java b/awt/java/awt/event/MouseMotionAdapter.java new file mode 100644 index 000000000..1ecd0d5ad --- /dev/null +++ b/awt/java/awt/event/MouseMotionAdapter.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public abstract class MouseMotionAdapter implements MouseMotionListener { + + public MouseMotionAdapter() { + } + + public void mouseDragged(MouseEvent e) { + } + + public void mouseMoved(MouseEvent e) { + } + +} diff --git a/awt/java/awt/event/MouseMotionListener.java b/awt/java/awt/event/MouseMotionListener.java new file mode 100644 index 000000000..e1313c340 --- /dev/null +++ b/awt/java/awt/event/MouseMotionListener.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface MouseMotionListener extends EventListener { + + public void mouseDragged(MouseEvent e); + + public void mouseMoved(MouseEvent e); + +} diff --git a/awt/java/awt/event/MouseWheelEvent.java b/awt/java/awt/event/MouseWheelEvent.java new file mode 100644 index 000000000..a3ed42436 --- /dev/null +++ b/awt/java/awt/event/MouseWheelEvent.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.Component; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class MouseWheelEvent extends MouseEvent { + + private static final long serialVersionUID = -9187413581993563929L; + + public static final int WHEEL_UNIT_SCROLL = 0; + + public static final int WHEEL_BLOCK_SCROLL = 1; + + private int wheelRotation; + private int scrollAmount; + private int scrollType; + + public MouseWheelEvent(Component source, int id, long when, int modifiers, + int x, int y, int clickCount, boolean popupTrigger, int scrollType, + int scrollAmount, int wheelRotation) { + super(source, id, when, modifiers, x, y, clickCount, popupTrigger); + + this.scrollType = scrollType; + this.scrollAmount = scrollAmount; + this.wheelRotation = wheelRotation; + } + + public int getScrollAmount() { + return scrollAmount; + } + + public int getScrollType() { + return scrollType; + } + + public int getWheelRotation() { + return wheelRotation; + } + + public int getUnitsToScroll() { + return (scrollAmount * wheelRotation); + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * MouseWheelEvent e = new MouseWheelEvent(new Component(){}, + * MouseWheelEvent.MOUSE_WHEEL, 0, + * MouseEvent.BUTTON1_DOWN_MASK|MouseEvent.CTRL_DOWN_MASK, + * 10, 20, 1, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, + * 1, 3); + * System.out.println(e); + */ + + String paramString = super.paramString(); + String typeString = null; + + switch (scrollType) { + case WHEEL_UNIT_SCROLL: + typeString = "WHEEL_UNIT_SCROLL"; //$NON-NLS-1$ + break; + case WHEEL_BLOCK_SCROLL: + typeString = "WHEEL_BLOCK_SCROLL"; //$NON-NLS-1$ + break; + default: + typeString = "unknown type"; //$NON-NLS-1$ + } + + paramString += ",scrollType=" + typeString + //$NON-NLS-1$ + ",scrollAmount=" + scrollAmount + //$NON-NLS-1$ + ",wheelRotation=" + wheelRotation; //$NON-NLS-1$ + + return paramString; + } + +} diff --git a/awt/java/awt/event/MouseWheelListener.java b/awt/java/awt/event/MouseWheelListener.java new file mode 100644 index 000000000..2d6a98236 --- /dev/null +++ b/awt/java/awt/event/MouseWheelListener.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface MouseWheelListener extends EventListener { + + public void mouseWheelMoved(MouseWheelEvent e); + +} diff --git a/awt/java/awt/event/PaintEvent.java b/awt/java/awt/event/PaintEvent.java new file mode 100644 index 000000000..22ac0908d --- /dev/null +++ b/awt/java/awt/event/PaintEvent.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.Component; +import java.awt.Rectangle; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class PaintEvent extends ComponentEvent { + + private static final long serialVersionUID = 1267492026433337593L; + + public static final int PAINT_FIRST = 800; + + public static final int PAINT_LAST = 801; + + public static final int PAINT = 800; + + public static final int UPDATE = 801; + + private Rectangle updateRect; + + public PaintEvent(Component source, int id, Rectangle updateRect) { + super(source, id); + + this.updateRect = updateRect; + } + + public Rectangle getUpdateRect() { + return updateRect; + } + + public void setUpdateRect(Rectangle updateRect) { + this.updateRect = updateRect; + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * PaintEvent e = new PaintEvent(new Component(){}, + * PaintEvent.PAINT, new Rectangle(0, 0, 10, 20)); + * System.out.println(e); + */ + + String typeString = null; + + switch (id) { + case PAINT: + typeString = "PAINT"; //$NON-NLS-1$ + break; + case UPDATE: + typeString = "UPDATE"; //$NON-NLS-1$ + break; + default: + typeString = "unknown type"; //$NON-NLS-1$ + } + + return typeString + ",updateRect=" + updateRect; //$NON-NLS-1$ + } + +} diff --git a/awt/java/awt/event/TextEvent.java b/awt/java/awt/event/TextEvent.java new file mode 100644 index 000000000..2a690ad50 --- /dev/null +++ b/awt/java/awt/event/TextEvent.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.awt.AWTEvent; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class TextEvent extends AWTEvent { + + private static final long serialVersionUID = 6269902291250941179L; + + public static final int TEXT_FIRST = 900; + + public static final int TEXT_LAST = 900; + + public static final int TEXT_VALUE_CHANGED = 900; + + public TextEvent(Object src, int id) { + super(src, id); + } + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * TextEvent e = new TextEvent(new Component(){}, + * TextEvent.TEXT_VALUE_CHANGED); + * System.out.println(e); + */ + + return (id == TEXT_VALUE_CHANGED) ? + "TEXT_VALUE_CHANGED" : "unknown type"; //$NON-NLS-1$ //$NON-NLS-2$ + } + +} diff --git a/awt/java/awt/event/TextListener.java b/awt/java/awt/event/TextListener.java new file mode 100644 index 000000000..05757c42a --- /dev/null +++ b/awt/java/awt/event/TextListener.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface TextListener extends EventListener { + + public void textValueChanged(TextEvent e); + +} + diff --git a/awt/java/awt/event/WindowAdapter.java b/awt/java/awt/event/WindowAdapter.java new file mode 100644 index 000000000..970aa8de4 --- /dev/null +++ b/awt/java/awt/event/WindowAdapter.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public abstract class WindowAdapter implements WindowListener, WindowStateListener, WindowFocusListener { + + public WindowAdapter() { + } + + public void windowActivated(WindowEvent e) { + } + + public void windowClosed(WindowEvent e) { + } + + public void windowClosing(WindowEvent e) { + } + + public void windowDeactivated(WindowEvent e) { + } + + public void windowDeiconified(WindowEvent e) { + } + + public void windowGainedFocus(WindowEvent e) { + } + + public void windowIconified(WindowEvent e) { + } + + public void windowLostFocus(WindowEvent e) { + } + + public void windowOpened(WindowEvent e) { + } + + public void windowStateChanged(WindowEvent e) { + } + +} diff --git a/awt/java/awt/event/WindowEvent.java b/awt/java/awt/event/WindowEvent.java new file mode 100644 index 000000000..474d2ac82 --- /dev/null +++ b/awt/java/awt/event/WindowEvent.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + + +//???AWT +//import java.awt.Window; +//import java.awt.Frame; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class WindowEvent extends ComponentEvent { + + private static final long serialVersionUID = -1567959133147912127L; + + public static final int WINDOW_FIRST = 200; + + public static final int WINDOW_OPENED = 200; + + public static final int WINDOW_CLOSING = 201; + + public static final int WINDOW_CLOSED = 202; + + public static final int WINDOW_ICONIFIED = 203; + + public static final int WINDOW_DEICONIFIED = 204; + + public static final int WINDOW_ACTIVATED = 205; + + public static final int WINDOW_DEACTIVATED = 206; + + public static final int WINDOW_GAINED_FOCUS = 207; + + public static final int WINDOW_LOST_FOCUS = 208; + + public static final int WINDOW_STATE_CHANGED = 209; + + public static final int WINDOW_LAST = 209; + + //???AWT: private Window oppositeWindow; + private int oldState; + private int newState; + + //???AWT + /* + public WindowEvent(Window source, int id) { + this(source, id, null); + } + + public WindowEvent(Window source, int id, Window opposite) { + this(source, id, opposite, Frame.NORMAL, Frame.NORMAL); + } + + public WindowEvent(Window source, int id, int oldState, int newState) { + this(source, id, null, oldState, newState); + } + + public WindowEvent(Window source, int id, Window opposite, + int oldState, int newState) { + super(source, id); + + oppositeWindow = opposite; + this.oldState = oldState; + this.newState = newState; + } + */ + //???AWT: Fake constructor + public WindowEvent() { + super(null, 0); + } + + public int getNewState() { + return newState; + } + + public int getOldState() { + return oldState; + } + + //???AWT + /* + public Window getOppositeWindow() { + return oppositeWindow; + } + + public Window getWindow() { + return (Window) source; + } + */ + + @Override + public String paramString() { + /* The format is based on 1.5 release behavior + * which can be revealed by the following code: + * + * WindowEvent e = new WindowEvent(new Frame(), + * WindowEvent.WINDOW_OPENED); + * System.out.println(e); + */ + + String typeString = null; + + switch (id) { + case WINDOW_OPENED: + typeString = "WINDOW_OPENED"; //$NON-NLS-1$ + break; + case WINDOW_CLOSING: + typeString = "WINDOW_CLOSING"; //$NON-NLS-1$ + break; + case WINDOW_CLOSED: + typeString = "WINDOW_CLOSED"; //$NON-NLS-1$ + break; + case WINDOW_ICONIFIED: + typeString = "WINDOW_ICONIFIED"; //$NON-NLS-1$ + break; + case WINDOW_DEICONIFIED: + typeString = "WINDOW_DEICONIFIED"; //$NON-NLS-1$ + break; + case WINDOW_ACTIVATED: + typeString = "WINDOW_ACTIVATED"; //$NON-NLS-1$ + break; + case WINDOW_DEACTIVATED: + typeString = "WINDOW_DEACTIVATED"; //$NON-NLS-1$ + break; + case WINDOW_GAINED_FOCUS: + typeString = "WINDOW_GAINED_FOCUS"; //$NON-NLS-1$ + break; + case WINDOW_LOST_FOCUS: + typeString = "WINDOW_LOST_FOCUS"; //$NON-NLS-1$ + break; + case WINDOW_STATE_CHANGED: + typeString = "WINDOW_STATE_CHANGED"; //$NON-NLS-1$ + break; + default: + typeString = "unknown type"; //$NON-NLS-1$ + } + + //???AWT + /* + return typeString + ",opposite=" + oppositeWindow + //$NON-NLS-1$ + ",oldState=" + oldState + ",newState=" + newState; //$NON-NLS-1$ //$NON-NLS-2$ + */ + return typeString; + } + +} diff --git a/awt/java/awt/event/WindowFocusListener.java b/awt/java/awt/event/WindowFocusListener.java new file mode 100644 index 000000000..528459f3b --- /dev/null +++ b/awt/java/awt/event/WindowFocusListener.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface WindowFocusListener extends EventListener { + + public void windowGainedFocus(WindowEvent e); + + public void windowLostFocus(WindowEvent e); + +} diff --git a/awt/java/awt/event/WindowListener.java b/awt/java/awt/event/WindowListener.java new file mode 100644 index 000000000..31bd547bd --- /dev/null +++ b/awt/java/awt/event/WindowListener.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface WindowListener extends EventListener { + + public void windowActivated(WindowEvent e); + + public void windowClosed(WindowEvent e); + + public void windowClosing(WindowEvent e); + + public void windowDeactivated(WindowEvent e); + + public void windowDeiconified(WindowEvent e); + + public void windowIconified(WindowEvent e); + + public void windowOpened(WindowEvent e); + +} diff --git a/awt/java/awt/event/WindowStateListener.java b/awt/java/awt/event/WindowStateListener.java new file mode 100644 index 000000000..ba14d9ee8 --- /dev/null +++ b/awt/java/awt/event/WindowStateListener.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov + * @version $Revision$ + */ +package java.awt.event; + +import java.util.EventListener; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface WindowStateListener extends EventListener { + + public void windowStateChanged(WindowEvent e); + +} + diff --git a/awt/java/awt/font/FontRenderContext.java b/awt/java/awt/font/FontRenderContext.java new file mode 100644 index 000000000..d7de00f20 --- /dev/null +++ b/awt/java/awt/font/FontRenderContext.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package java.awt.font; + +import java.awt.geom.AffineTransform; + +/** + * The FontRenderContext class contains the information about text measurement. + * Anti-aliasing and fractional-metrics modes are defined by an application and + * affect the size of a character. + * + * @since Android 1.0 + */ +public class FontRenderContext { + + // Affine transform of this mode + /** + * The transform. + */ + private AffineTransform transform; + + // Is the anti-aliased mode used + /** + * The anti aliased. + */ + private boolean fAntiAliased; + + // Is the fractional metrics used + /** + * The fractional metrics. + */ + private boolean fFractionalMetrics; + + + /** + * Instantiates a new FontRenderContext object with the specified + * AffineTransform, anti-aliasing and fractional metrics flags. + * + * @param trans + * the AffineTransform. + * @param antiAliased + * the anti-aliasing flag. + * @param usesFractionalMetrics + * the fractional metrics flag. + */ + public FontRenderContext(AffineTransform trans, boolean antiAliased, + boolean usesFractionalMetrics) { + if (trans != null){ + transform = new AffineTransform(trans); + } + fAntiAliased = antiAliased; + fFractionalMetrics = usesFractionalMetrics; + } + + /** + * Instantiates a new FontRenderContext object. + */ + protected FontRenderContext() { + } + + /** + * Compares the specified Object with current FontRenderContext object. + * + * @param obj + * the Object to be compared. + * @return true, if the specified Object is equal to current + * FontRenderContext object. + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj != null) { + try { + return equals((FontRenderContext) obj); + } catch (ClassCastException e) { + return false; + } + } + return false; + + } + + /** + * Gets the transform which is used for scaling typographical points to + * pixels in this FontRenderContext. + * + * @return the AffineTransform which is used for scaling typographical + * points to pixels in this FontRenderContext. + */ + public AffineTransform getTransform() { + if (transform != null){ + return new AffineTransform(transform); + } + return new AffineTransform(); + } + + /** + * Compares the specified FontRenderContext object with current + * FontRenderContext. + * + * @param frc + * the FontRenderContext object to be compared. + * @return true, if the specified FontRenderContext object is equal to + * current FontRenderContext. + */ + public boolean equals(FontRenderContext frc) { + if (this == frc){ + return true; + } + + if (frc == null){ + return false; + } + + if (!frc.getTransform().equals(this.getTransform()) && + !frc.isAntiAliased() == this.fAntiAliased && + !frc.usesFractionalMetrics() == this.fFractionalMetrics){ + return false; + } + return true; + } + + /** + * Returns true if the text fractional metrics are used in this + * FontRenderContext. + * + * @return true, if the text fractional metrics are used in this + * FontRenderContext, false otherwise. + */ + public boolean usesFractionalMetrics() { + return this.fFractionalMetrics; + } + + /** + * Returns true if anti-aliasing is used in this FontRenderContext. + * + * @return true, if is anti-aliasing is used in this FontRenderContext, + * false otherwise. + */ + public boolean isAntiAliased() { + return this.fAntiAliased; + } + + /** + * Returns hash code of the FontRenderContext object. + * + * @return the hash code of the FontRenderContext object. + */ + @Override + public int hashCode() { + return this.getTransform().hashCode() ^ + new Boolean(this.fFractionalMetrics).hashCode() ^ + new Boolean(this.fAntiAliased).hashCode(); + } + +} + diff --git a/awt/java/awt/font/GlyphJustificationInfo.java b/awt/java/awt/font/GlyphJustificationInfo.java new file mode 100644 index 000000000..b03de0aa1 --- /dev/null +++ b/awt/java/awt/font/GlyphJustificationInfo.java @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt.font; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The GlyphJustificationInfo class provides information about the glyph's + * justification properties. There are four justification properties: weight, + * priority, absorb, and limit. + *

+ * There are two sets of metrics: growing and shrinking. Growing metrics are + * used when the glyphs are to be spread apart to fit a larger width. Shrinking + * metrics are used when the glyphs are to be moved together to fit a smaller + * width. + *

+ * + * @since Android 1.0 + */ +public final class GlyphJustificationInfo { + + /** + * The Constant PRIORITY_KASHIDA indicates the highest justification + * priority. + */ + public static final int PRIORITY_KASHIDA = 0; + + /** + * The Constant PRIORITY_WHITESPACE indicates the second highest + * justification priority. + */ + public static final int PRIORITY_WHITESPACE = 1; + + /** + * The Constant PRIORITY_INTERCHAR indicates the second lowest justification + * priority. + */ + public static final int PRIORITY_INTERCHAR = 2; + + /** + * The Constant PRIORITY_NONE indicates the lowest justification priority. + */ + public static final int PRIORITY_NONE = 3; + + /** + * The grow absorb flag indicates if this glyph absorbs all extra space at + * this and lower priority levels when it grows. + */ + public final boolean growAbsorb; + + /** + * The grow left limit value represents the maximum value by which the left + * side of this glyph grows. + */ + public final float growLeftLimit; + + /** + * The grow right limit value repesents the maximum value by which the right + * side of this glyph grows. + */ + public final float growRightLimit; + + /** + * The grow priority value represents the priority level of this glyph as it + * is growing. + */ + public final int growPriority; + + /** + * The shrink absorb fleg indicates this glyph absorbs all remaining + * shrinkage at this and lower priority levels as it shrinks. + */ + public final boolean shrinkAbsorb; + + /** + * The shrink left limit value represents the maximum value by which the + * left side of this glyph shrinks. + */ + public final float shrinkLeftLimit; + + /** + * The shrink right limit value represents the maximum value by which the + * right side of this glyph shrinks. + */ + public final float shrinkRightLimit; + + /** + * The shrink priority represents the glyth's priority level as it is + * shrinking. + */ + public final int shrinkPriority; + + /** + * The weight of the glyph. + */ + public final float weight; + + /** + * Instantiates a new GlyphJustificationInfo object which contains glyph's + * justification properties. + * + * @param weight + * the weight of glyph. + * @param growAbsorb + * indicates if this glyph contais all space at this priority and + * lower priority levels when it grows. + * @param growPriority + * indicates the priority level of this glyph when it grows. + * @param growLeftLimit + * indicates the maximum value of which the left side of this + * glyph can grow. + * @param growRightLimit + * the maximum value of which the right side of this glyph can + * grow. + * @param shrinkAbsorb + * indicates if this glyph contains all remaining shrinkage at + * this and lower priority levels when it shrinks. + * @param shrinkPriority + * indicates the glyph's priority level when it shrinks. + * @param shrinkLeftLimit + * indicates the maximum value of which the left side of this + * glyph can shrink. + * @param shrinkRightLimit + * indicates the maximum amount by which the right side of this + * glyph can shrink. + */ + public GlyphJustificationInfo(float weight, boolean growAbsorb, int growPriority, + float growLeftLimit, float growRightLimit, boolean shrinkAbsorb, int shrinkPriority, + float shrinkLeftLimit, float shrinkRightLimit) { + + if (weight < 0) { + // awt.19C=weight must be a positive number + throw new IllegalArgumentException(Messages.getString("awt.19C")); //$NON-NLS-1$ + } + this.weight = weight; + + if (growLeftLimit < 0) { + // awt.19D=growLeftLimit must be a positive number + throw new IllegalArgumentException(Messages.getString("awt.19D")); //$NON-NLS-1$ + } + this.growLeftLimit = growLeftLimit; + + if (growRightLimit < 0) { + // awt.19E=growRightLimit must be a positive number + throw new IllegalArgumentException(Messages.getString("awt.19E")); //$NON-NLS-1$ + } + this.growRightLimit = growRightLimit; + + if ((shrinkPriority < 0) || (shrinkPriority > PRIORITY_NONE)) { + // awt.19F=incorrect value for shrinkPriority, more than + // PRIORITY_NONE or less than PRIORITY_KASHIDA value + throw new IllegalArgumentException(Messages.getString("awt.19F")); //$NON-NLS-1$ + } + this.shrinkPriority = shrinkPriority; + + if ((growPriority < 0) || (growPriority > PRIORITY_NONE)) { + // awt.200=incorrect value for growPriority, more than PRIORITY_NONE + // or less than PRIORITY_KASHIDA value + throw new IllegalArgumentException(Messages.getString("awt.200")); //$NON-NLS-1$ + } + this.growPriority = growPriority; + + if (shrinkLeftLimit < 0) { + // awt.201=shrinkLeftLimit must be a positive number + throw new IllegalArgumentException(Messages.getString("awt.201")); //$NON-NLS-1$ + } + this.shrinkLeftLimit = shrinkLeftLimit; + + if (shrinkRightLimit < 0) { + // awt.202=shrinkRightLimit must be a positive number + throw new IllegalArgumentException(Messages.getString("awt.202")); //$NON-NLS-1$ + } + this.shrinkRightLimit = shrinkRightLimit; + + this.shrinkAbsorb = shrinkAbsorb; + this.growAbsorb = growAbsorb; + } +} diff --git a/awt/java/awt/font/GlyphMetrics.java b/awt/java/awt/font/GlyphMetrics.java new file mode 100644 index 000000000..287172231 --- /dev/null +++ b/awt/java/awt/font/GlyphMetrics.java @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt.font; + +import java.awt.geom.Rectangle2D; + +/** + * The GlyphMetrics class provides information about the size and shape of a + * single glyph. Each glyph has information to specify whether its baseline is + * horizontal or vertical as well as information on how it interacts with other + * characters in a text, given as one of the following types: STANDARD, + * LIGATURE, COMBINING, or COMPONENT. + * + * @since Android 1.0 + */ +public final class GlyphMetrics { + + // advance width of the glyph character cell + /** + * The advance x. + */ + private float advanceX; + + // advance height of the glyph character cell + /** + * The advance y. + */ + private float advanceY; + + // flag if the glyph horizontal + /** + * The horizontal. + */ + private boolean horizontal; + + // glyph type code + /** + * The glyph type. + */ + private byte glyphType; + + // bounding box for outline of the glyph + /** + * The bounds. + */ + private Rectangle2D.Float bounds; + + /** + * The Constant STANDARD indicates a glyph that represents a single + * character. + */ + public static final byte STANDARD = 0; + + /** + * The Constant LIGATURE indicates a glyph that represents multiple + * characters as a ligature. + */ + public static final byte LIGATURE = 1; + + /** + * The Constant COMBINING indicates a glyph which has no caret position + * between glyphs (for example umlaut). + */ + public static final byte COMBINING = 2; + + /** + * The Constant COMPONENT indicates a glyph with no corresponding character + * in the backing store. + */ + public static final byte COMPONENT = 3; + + /** + * The Constant WHITESPACE indicates a glyph without visual representation. + */ + public static final byte WHITESPACE = 4; + + /** + * Instantiates a new GlyphMetrics object with the specified parameters. + * + * @param horizontal + * specifies if metrics are for a horizontal baseline (true + * value), or a vertical baseline (false value). + * @param advanceX + * the X component of the glyph's advance. + * @param advanceY + * the Y component of the glyph's advance. + * @param bounds + * the glyph's bounds. + * @param glyphType + * the glyph's type. + */ + public GlyphMetrics(boolean horizontal, float advanceX, float advanceY, Rectangle2D bounds, + byte glyphType) { + this.horizontal = horizontal; + this.advanceX = advanceX; + this.advanceY = advanceY; + + this.bounds = new Rectangle2D.Float(); + this.bounds.setRect(bounds); + + this.glyphType = glyphType; + } + + /** + * Instantiates a new horizontal GlyphMetrics with the specified parameters. + * + * @param advanceX + * the X component of the glyph's advance. + * @param bounds + * the glyph's bounds. + * @param glyphType + * the glyph's type. + */ + public GlyphMetrics(float advanceX, Rectangle2D bounds, byte glyphType) { + this.advanceX = advanceX; + this.advanceY = 0; + + this.horizontal = true; + + this.bounds = new Rectangle2D.Float(); + this.bounds.setRect(bounds); + + this.glyphType = glyphType; + } + + /** + * Gets the glyph's bounds. + * + * @return glyph's bounds. + */ + public Rectangle2D getBounds2D() { + return (Rectangle2D.Float)this.bounds.clone(); + } + + /** + * Checks if this glyph is whitespace or not. + * + * @return true, if this glyph is whitespace, false otherwise. + */ + public boolean isWhitespace() { + return ((this.glyphType & 4) == WHITESPACE); + } + + /** + * Checks if this glyph is standard or not. + * + * @return true, if this glyph is standard, false otherwise. + */ + public boolean isStandard() { + return ((this.glyphType & 3) == STANDARD); + } + + /** + * Checks if this glyph is ligature or not. + * + * @return true, if this glyph is ligature, false otherwise. + */ + public boolean isLigature() { + return ((this.glyphType & 3) == LIGATURE); + } + + /** + * Checks if this glyph is component or not. + * + * @return true, if this glyph is component, false otherwise. + */ + public boolean isComponent() { + return ((this.glyphType & 3) == COMPONENT); + } + + /** + * Checks if this glyph is combining or not. + * + * @return true, if this glyph is combining, false otherwise. + */ + public boolean isCombining() { + return ((this.glyphType & 3) == COMBINING); + } + + /** + * Gets the glyph's type. + * + * @return the glyph's type. + */ + public int getType() { + return this.glyphType; + } + + /** + * Gets the distance from the right (for horizontal) or bottom (for + * vertical) of the glyph bounds to the advance. + * + * @return the distance from the right (for horizontal) or bottom (for + * vertical) of the glyph bounds to the advance. + */ + public float getRSB() { + if (this.horizontal) { + return this.advanceX - this.bounds.x - (float)this.bounds.getWidth(); + } + return this.advanceY - this.bounds.y - (float)this.bounds.getHeight(); + } + + /** + * Gets the distance from 0, 0 to the left (for horizontal) or top (for + * vertical) of the glyph bounds. + * + * @return the distance from 0, 0 to the left (for horizontal) or top (for + * vertical) of the glyph bounds. + */ + public float getLSB() { + if (this.horizontal) { + return this.bounds.x; + } + return this.bounds.y; + } + + /** + * Gets the Y component of the glyph's advance. + * + * @return the Y component of the glyph's advance. + */ + public float getAdvanceY() { + return this.advanceY; + } + + /** + * Gets the X component of the glyph's advance. + * + * @return the X component of the glyph's advance. + */ + public float getAdvanceX() { + return this.advanceX; + } + + /** + * Gets the glyph's advance along the baseline. + * + * @return the glyph's advance. + */ + public float getAdvance() { + if (this.horizontal) { + return this.advanceX; + } + return this.advanceY; + } + +} diff --git a/awt/java/awt/font/GlyphVector.java b/awt/java/awt/font/GlyphVector.java new file mode 100644 index 000000000..a72b774bf --- /dev/null +++ b/awt/java/awt/font/GlyphVector.java @@ -0,0 +1,403 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt.font; + +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphJustificationInfo; +import java.awt.font.GlyphMetrics; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * The GlyphVector class contains a collection of glyphs with geometric + * information and each glyph's location. Each GlyphVector can be associated + * with only one Font. GlyphVector contains the following properties for each + * glyph: + *
    + *
  • the glyph position;
  • + *
  • the transform of the glyph;
  • + *
  • the metrics of the glyph in the context of the GlyphVector.
  • + *
+ * + * @since Android 1.0 + */ +public abstract class GlyphVector implements Cloneable { + + /** + * The Constant FLAG_HAS_TRANSFORMS indicates that this GlyphVector has + * per-glyph transforms. + */ + public static final int FLAG_HAS_TRANSFORMS = 1; + + /** + * The Constant FLAG_HAS_POSITION_ADJUSTMENTS indicates that the GlyphVector + * has per-glyph position adjustments. + */ + public static final int FLAG_HAS_POSITION_ADJUSTMENTS = 2; + + /** + * The Constant FLAG_RUN_RTL indicates that this GlyphVector has a right to + * left run direction. + */ + public static final int FLAG_RUN_RTL = 4; + + /** + * The Constant FLAG_COMPLEX_GLYPHS indicates that this GlyphVector has a + * complex glyph to char mapping. + */ + public static final int FLAG_COMPLEX_GLYPHS = 8; + + /** + * The Constant FLAG_MASK indicates a mask for supported flags from + * getLayoutFlags. + */ + public static final int FLAG_MASK = 15; // (|) mask of other flags + + /** + * Instantiates a new GlyphVector. + */ + public GlyphVector() { + } + + /** + * Gets the pixel bounds of the GlyphVector when rendered at the specified + * location with the specified FontRenderContext. + * + * @param frc + * the FontRenderContext. + * @param x + * the X coordinate of the GlyphVector's location. + * @param y + * the Y coordinate of the GlyphVector's location. + * @return the pixel bounds + */ + public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) { + // default implementation - integer Rectangle, that encloses visual + // bounds rectangle + Rectangle2D visualRect = getVisualBounds(); + + int minX = (int)Math.floor(visualRect.getMinX() + x); + int minY = (int)Math.floor(visualRect.getMinY() + y); + int width = (int)Math.ceil(visualRect.getMaxX() + x) - minX; + int height = (int)Math.ceil(visualRect.getMaxY() + y) - minY; + + return new Rectangle(minX, minY, width, height); + } + + /** + * Gets the pixel bounds of the glyph with the specified index in this + * GlyphVector which is rendered with the specified FontRenderContext at the + * specified location. + * + * @param index + * the glyph index in this GlyphVector. + * @param frc + * the FontRenderContext. + * @param x + * the X coordinate of the GlyphVector's location. + * @param y + * the Y coordinate of the GlyphVector's location. + * @return a Rectangle bounds. + */ + public Rectangle getGlyphPixelBounds(int index, FontRenderContext frc, float x, float y) { + Rectangle2D visualRect = getGlyphVisualBounds(index).getBounds2D(); + + int minX = (int)Math.floor(visualRect.getMinX() + x); + int minY = (int)Math.floor(visualRect.getMinY() + y); + int width = (int)Math.ceil(visualRect.getMaxX() + x) - minX; + int height = (int)Math.ceil(visualRect.getMaxY() + y) - minY; + + return new Rectangle(minX, minY, width, height); + } + + /** + * Gets the visual bounds of the GlyphVector. + * + * @return the visual bounds of the GlyphVector. + */ + public abstract Rectangle2D getVisualBounds(); + + /** + * Gets the logical bounds of the GlyphVector. + * + * @return the logical bounds of the GlyphVector. + */ + public abstract Rectangle2D getLogicalBounds(); + + /** + * Sets the position of the specified glyph in this GlyphVector. + * + * @param glyphIndex + * the glyph index in this GlyphVector. + * @param newPos + * the new position of the glyph at the specified glyphIndex. + */ + public abstract void setGlyphPosition(int glyphIndex, Point2D newPos); + + /** + * Gets the position of the specified glyph in this GlyphVector. + * + * @param glyphIndex + * the glyph index in this GlyphVector. + * @return the position of the specified glyph in this GlyphVector. + */ + public abstract Point2D getGlyphPosition(int glyphIndex); + + /** + * Sets the affine transform to a glyph with the specified index in this + * GlyphVector. + * + * @param glyphIndex + * the glyth index in this GlyphVector. + * @param trans + * the AffineTransform to be assigned to the specified glyph. + */ + public abstract void setGlyphTransform(int glyphIndex, AffineTransform trans); + + /** + * Gets the transform of the specified glyph in this GlyphVector. + * + * @param glyphIndex + * the glyph index in this GlyphVector. + * @return the new transform of the glyph. + */ + public abstract AffineTransform getGlyphTransform(int glyphIndex); + + /** + * Compares this GlyphVector with the specified GlyphVector objects. + * + * @param glyphVector + * the GlyphVector object to be compared. + * @return true, if this GlyphVector is equal to the specified GlyphVector + * object, false otherwise. + */ + public abstract boolean equals(GlyphVector glyphVector); + + /** + * Gets the metrics of the glyph with the specified index in this + * GlyphVector. + * + * @param glyphIndex + * index in this GlyphVector. + * @return the metrics of the glyph with the specified index in this + * GlyphVector. + */ + public abstract GlyphMetrics getGlyphMetrics(int glyphIndex); + + /** + * Gets the justification information of the glyph whose index is specified. + * + * @param glyphIndex + * the glyph index. + * @return the GlyphJustificationInfo for the specified glyph. + */ + public abstract GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex); + + /** + * Gets the FontRenderContext of this GlyphVector. + * + * @return the FontRenderContext of this GlyphVector. + */ + public abstract FontRenderContext getFontRenderContext(); + + /** + * Gets a Shape object which defines the visual representation of the + * specified glyph in this GlyphVector, translated a distance of x in the X + * direction and y in the Y direction. + * + * @param glyphIndex + * the glyth index in this GlyphVector. + * @param x + * the distance in the X direction to translate the shape object + * before returning it. + * @param y + * the distance in the Y direction to translate the shape object + * before returning it. + * @return a Shape object which represents the visual representation of the + * specified glyph in this GlyphVector - glyph outline. + */ + public Shape getGlyphOutline(int glyphIndex, float x, float y) { + Shape initialShape = getGlyphOutline(glyphIndex); + AffineTransform trans = AffineTransform.getTranslateInstance(x, y); + return trans.createTransformedShape(initialShape); + } + + /** + * Gets the visual bounds of the specified glyph in the GlyphVector. + * + * @param glyphIndex + * the glyph index in this GlyphVector. + * @return the glyph visual bounds of the glyph with the specified index in + * the GlyphVector. + */ + public abstract Shape getGlyphVisualBounds(int glyphIndex); + + /** + * Gets a Shape object which defines the visual representation of the + * specified glyph in this GlyphVector. + * + * @param glyphIndex + * the glyth index in this GlyphVector. + * @return a Shape object which represents the visual representation of the + * specified glyph in this GlyphVector - glyph outline. + */ + public abstract Shape getGlyphOutline(int glyphIndex); + + /** + * Gets the logical bounds of the specified glyph in the GlyphVector. + * + * @param glyphIndex + * the index in this GlyphVector of the glyph from which to + * retrieve its logical bounds + * @return the logical bounds of the specified glyph in the GlyphVector. + */ + public abstract Shape getGlyphLogicalBounds(int glyphIndex); + + /** + * Gets the visual representation of this GlyphVector rendered in x, y + * location as a Shape object. + * + * @param x + * the x coordinate of the GlyphVector. + * @param y + * the y coordinate of the GlyphVector. + * @return the visual representation of this GlyphVector as a Shape object. + */ + public abstract Shape getOutline(float x, float y); + + /** + * Gets the visual representation of this GlyphVector as a Shape object. + * + * @return the visual representation of this GlyphVector as a Shape object. + */ + public abstract Shape getOutline(); + + /** + * Gets the font of this GlyphVector. + * + * @return the font of this GlyphVector. + */ + public abstract Font getFont(); + + /** + * Gets an array of the glyph codes of the specified glyphs. + * + * @param beginGlyphIndex + * the index into this GlyphVector at which to start retrieving + * glyph codes. + * @param numEntries + * the number of glyph codes. + * @param codeReturn + * the array into which the resulting glyphcodes will be written. + * @return the array of the glyph codes. + */ + public abstract int[] getGlyphCodes(int beginGlyphIndex, int numEntries, int[] codeReturn); + + /** + * Gets an array of the character indices of the specified glyphs. + * + * @param beginGlyphIndex + * the index of the first glyph to return information for. + * @param numEntries + * the number of glyph indices to return. + * @param codeReturn + * the array into which the resulting character indices will be + * written. + * @return an array of character indices for the specifies glyphs. + */ + public int[] getGlyphCharIndices(int beginGlyphIndex, int numEntries, int[] codeReturn) { + if (codeReturn == null) { + codeReturn = new int[numEntries]; + } + + for (int i = 0; i < numEntries; i++) { + codeReturn[i] = getGlyphCharIndex(i + beginGlyphIndex); + } + return codeReturn; + } + + /** + * Gets an array of the positions of the specified glyphs in this + * GlyphVector. + * + * @param beginGlyphIndex + * the index of the first glyph to return information for. + * @param numEntries + * the number of glyphs to return information for. + * @param positionReturn + * the array where the result will be stored. + * @return an array of glyph positions. + */ + public abstract float[] getGlyphPositions(int beginGlyphIndex, int numEntries, + float[] positionReturn); + + /** + * Gets the glyph code of the specified glyph. + * + * @param glyphIndex + * the index in this GlyphVector which corresponds to the glyph + * from which to retrieve the glyphcode. + * @return the glyphcode of the specified glyph. + */ + public abstract int getGlyphCode(int glyphIndex); + + /** + * Gets the first logical character's index of the specified glyph. + * + * @param glyphIndex + * the glyph index. + * @return the the first logical character's index. + */ + public int getGlyphCharIndex(int glyphIndex) { + // default implemetation one-to-one + return glyphIndex; + } + + /** + * Sets default layout to this GlyphVector. + */ + public abstract void performDefaultLayout(); + + /** + * Gets the number of glyphs in the GlyphVector. + * + * @return the number of glyphs in the GlyphVector. + */ + public abstract int getNumGlyphs(); + + /** + * Gets flags which describe the global state of the GlyphVector. The + * default implementation returns 0. + * + * @return the layout flags + */ + public int getLayoutFlags() { + // default implementation - returned value is 0 + return 0; + } + +} diff --git a/awt/java/awt/font/GraphicAttribute.java b/awt/java/awt/font/GraphicAttribute.java new file mode 100644 index 000000000..8480e0f6a --- /dev/null +++ b/awt/java/awt/font/GraphicAttribute.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt.font; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The GraphicAttribute abstract class provides an opportunity to insert + * graphical elements in printed text. + * + * @since Android 1.0 + */ +public abstract class GraphicAttribute { + + /** + * The Constant TOP_ALIGNMENT indicates using the top line to calculate + * placement of graphics. + */ + public static final int TOP_ALIGNMENT = -1; + + /** + * The Constant BOTTOM_ALIGNMENT indicates using the bottom line to + * calculate placement of graphics. + */ + public static final int BOTTOM_ALIGNMENT = -2; + + /** + * The Constant ROMAN_BASELINE indicates the placement of the roman baseline + * with respect to the graphics origin. + */ + public static final int ROMAN_BASELINE = 0; + + /** + * The Constant CENTER_BASELINE indicates the placement of the center + * baseline with respect to the graphics origin. + */ + public static final int CENTER_BASELINE = 1; + + /** + * The Constant HANGING_BASELINE indicates the placement of the hanging + * baseline with respect to the graphics origin. + */ + public static final int HANGING_BASELINE = 2; + + // the alignment of this GraphicAttribute + /** + * The alignment. + */ + private int alignment; + + /** + * Instantiates a new graphic attribute with the specified alignment. + * + * @param align + * the specified alignment. + */ + protected GraphicAttribute(int align) { + if ((align < BOTTOM_ALIGNMENT) || (align > HANGING_BASELINE)) { + // awt.198=Illegal alignment argument + throw new IllegalArgumentException(Messages.getString("awt.198")); //$NON-NLS-1$ + } + this.alignment = align; + } + + /** + * Draws the GraphicAttribute at the specified location. + * + * @param graphics + * the Graphics. + * @param x + * the X coordinate of GraphicAttribute location. + * @param y + * the Y coordinate of GraphicAttribute location. + */ + public abstract void draw(Graphics2D graphics, float x, float y); + + /** + * Gets the GraphicAttribute's advance. It's the distance from the point at + * which the graphic is rendered and the point where the next character or + * graphic is rendered. + * + * @return the GraphicAttribute's advance. + */ + public abstract float getAdvance(); + + /** + * Gets the alignment of this GraphicAttribute. + * + * @return the alignment of this GraphicAttribute. + */ + public final int getAlignment() { + return this.alignment; + } + + /** + * Gets the ascent of this GraphicAttribute. + * + * @return the ascent of this GraphicAttribute. + */ + public abstract float getAscent(); + + /** + * Gets the bounds of this GraphicAttribute. + * + * @return the bounds of this GraphicAttribute. + */ + public Rectangle2D getBounds() { + float ascent = getAscent(); + float advance = getAdvance(); + float descent = getDescent(); + + // Default implementation - see API documentation. + return new Rectangle2D.Float(0, -ascent, advance, ascent + descent); + } + + /** + * Gets the descent of this GraphicAttribute. + * + * @return the descent of this GraphicAttribute. + */ + public abstract float getDescent(); + + /** + * Gets the GlyphJustificationInfo of this GraphicAttribute. + * + * @return the GlyphJustificationInfo of this GraphicAttribute. + */ + public GlyphJustificationInfo getJustificationInfo() { + + /* + * Default implementation. Since documentation doesn't describe default + * values, they were calculated based on 1.5 release behavior and can be + * obtained using next test sample: // Create GraphicAttribute class + * implementation public class MyGraphicAttribute extends + * GraphicAttribute { protected MyGraphicAttribute(int align) { + * super(align); } public float getDescent() { return 0; } public float + * getAdvance() { return 1; } public void draw(Graphics2D g2, float x, + * float y) { } public float getAscent() { return 0; } } + * MyGraphicAttribute myGA = gat.new MyGraphicAttribute(0); // print + * justification parameters + * System.out.println(myGA.getJustificationInfo().growAbsorb); + * System.out.println(myGA.getJustificationInfo().shrinkAbsorb); + * System.out.println(myGA.getJustificationInfo().growLeftLimit); + * System.out.println(myGA.getJustificationInfo().growPriority); + * System.out.println(myGA.getJustificationInfo().growRightLimit); + * System.out.println(myGA.getJustificationInfo().shrinkLeftLimit); + * System.out.println(myGA.getJustificationInfo().shrinkPriority); + * System.out.println(myGA.getJustificationInfo().shrinkRightLimit); + * System.out.println(myGA.getJustificationInfo().weight); + */ + float advance = getAdvance(); + return new GlyphJustificationInfo(advance, false, + GlyphJustificationInfo.PRIORITY_INTERCHAR, advance / 3, advance / 3, false, + GlyphJustificationInfo.PRIORITY_WHITESPACE, 0, 0); + } + +} diff --git a/awt/java/awt/font/ImageGraphicAttribute.java b/awt/java/awt/font/ImageGraphicAttribute.java new file mode 100644 index 000000000..d6d4758d3 --- /dev/null +++ b/awt/java/awt/font/ImageGraphicAttribute.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt.font; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.geom.Rectangle2D; + +import org.apache.harmony.misc.HashCode; + +/** + * The ImageGraphicAttribute class provides an opportunity to insert images to a + * text. + * + * @since Android 1.0 + */ +public final class ImageGraphicAttribute extends GraphicAttribute { + + // Image object rendered by this ImageGraphicAttribute + /** + * The image. + */ + private Image fImage; + + // X coordinate of the origin point + /** + * The origin x. + */ + private float fOriginX; + + // Y coordinate of the origin point + /** + * The origin y. + */ + private float fOriginY; + + // the width of the image object + /** + * The img width. + */ + private float fImgWidth; + + // the height of the image object + /** + * The img height. + */ + private float fImgHeight; + + /** + * Instantiates a new ImageGraphicAttribute with the specified image, + * alignment and origins. + * + * @param image + * the Image to be rendered by ImageGraphicAttribute. + * @param alignment + * the alignment of the ImageGraphicAttribute. + * @param originX + * the origin X coordinate in the image of ImageGraphicAttribute. + * @param originY + * the origin Y coordinate in the image of ImageGraphicAttribute. + */ + public ImageGraphicAttribute(Image image, int alignment, float originX, float originY) { + super(alignment); + + this.fImage = image; + this.fOriginX = originX; + this.fOriginY = originY; + + this.fImgWidth = fImage.getWidth(null); + this.fImgHeight = fImage.getHeight(null); + + } + + /** + * Instantiates a new ImageGraphicAttribute with the specified image and + * alignment. + * + * @param image + * the Image to be rendered by ImageGraphicAttribute. + * @param alignment + * the alignment of the ImageGraphicAttribute. + */ + public ImageGraphicAttribute(Image image, int alignment) { + this(image, alignment, 0, 0); + } + + /** + * Returns a hash code of this ImageGraphicAttribute object. + * + * @return the hash code of this ImageGraphicAttribute object. + */ + @Override + public int hashCode() { + HashCode hash = new HashCode(); + + hash.append(fImage.hashCode()); + hash.append(getAlignment()); + return hash.hashCode(); + } + + /** + * Compares the specified ImageGraphicAttribute object with this + * ImageGraphicAttribute object. + * + * @param iga + * the ImageGraphicAttribute object to be compared. + * @return true, if the specified ImageGraphicAttribute object is equal to + * this ImageGraphicAttribute object, false otherwise. + */ + public boolean equals(ImageGraphicAttribute iga) { + if (iga == null) { + return false; + } + + if (iga == this) { + return true; + } + + return (fOriginX == iga.fOriginX && fOriginY == iga.fOriginY + && getAlignment() == iga.getAlignment() && fImage.equals(iga.fImage)); + } + + /** + * Compares the specified Object with this ImageGraphicAttribute object. + * + * @param obj + * the Object to be compared. + * @return true, if the specified Object is equal to this + * ImageGraphicAttribute object, false otherwise. + */ + @Override + public boolean equals(Object obj) { + try { + return equals((ImageGraphicAttribute)obj); + } catch (ClassCastException e) { + return false; + } + + } + + @Override + public void draw(Graphics2D g2, float x, float y) { + g2.drawImage(fImage, (int)(x - fOriginX), (int)(y - fOriginY), null); + } + + @Override + public float getAdvance() { + return Math.max(0, fImgWidth - fOriginX); + } + + @Override + public float getAscent() { + return Math.max(0, fOriginY); + } + + @Override + public Rectangle2D getBounds() { + return new Rectangle2D.Float(-fOriginX, -fOriginY, fImgWidth, fImgHeight); + } + + @Override + public float getDescent() { + return Math.max(0, fImgHeight - fOriginY); + } + +} diff --git a/awt/java/awt/font/LineBreakMeasurer.java b/awt/java/awt/font/LineBreakMeasurer.java new file mode 100644 index 000000000..4800093f6 --- /dev/null +++ b/awt/java/awt/font/LineBreakMeasurer.java @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/* + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package java.awt.font; + +import java.text.AttributedCharacterIterator; //???AWT: import java.text.BreakIterator; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The class LineBreakMeasurer provides methods to measure the graphical + * representation of a text in order to determine where to add line breaks so + * the resulting line of text fits its wrapping width. The wrapping width + * defines the visual width of the paragraph. + * + * @since Android 1.0 + */ +public final class LineBreakMeasurer { + + /** + * The tm. + */ + private TextMeasurer tm = null; + + // ???AWT private BreakIterator bi = null; + /** + * The position. + */ + private int position = 0; + + /** + * The maxpos. + */ + int maxpos = 0; + + /** + * Instantiates a new LineBreakMeasurer object for the specified text. + * + * @param text + * the AttributedCharacterIterator object which contains text + * with at least one character. + * @param frc + * the FontRenderContext represented information about graphic + * device. + */ + public LineBreakMeasurer(AttributedCharacterIterator text, FontRenderContext frc) { + // ???AWT: this(text, BreakIterator.getLineInstance(), frc); + } + + /* + * ???AWT public LineBreakMeasurer( AttributedCharacterIterator text, + * BreakIterator bi, FontRenderContext frc ) { tm = new TextMeasurer(text, + * frc); this.bi = bi; this.bi.setText(text); position = + * text.getBeginIndex(); maxpos = tm.aci.getEndIndex(); } + */ + + /** + * Deletes a character from the specified position of the text, updates this + * LineBreakMeasurer object. + * + * @param newText + * the new text. + * @param pos + * the position of the character which is deleted. + */ + public void deleteChar(AttributedCharacterIterator newText, int pos) { + tm.deleteChar(newText, pos); + // ???AWT: bi.setText(newText); + + position = newText.getBeginIndex(); + + maxpos--; + } + + /** + * Gets current position of this LineBreakMeasurer. + * + * @return the current position of this LineBreakMeasurer + */ + public int getPosition() { + return position; + } + + /** + * Inserts a character at the specified position in the text, updates this + * LineBreakMeasurer object. + * + * @param newText + * the new text. + * @param pos + * the position of the character which is inserted. + */ + public void insertChar(AttributedCharacterIterator newText, int pos) { + tm.insertChar(newText, pos); + // ???AWT: bi.setText(newText); + + position = newText.getBeginIndex(); + + maxpos++; + } + + /** + * Returns the next line of text, updates current position in this + * LineBreakMeasurer. + * + * @param wrappingWidth + * the maximum visible line width. + * @param offsetLimit + * the limit point within the text indicating that no further + * text should be included on the line; the paragraph break. + * @param requireNextWord + * if true, null is returned (the entire word at the current + * position does not fit within the wrapping width); if false, a + * valid layout is returned that includes at least the character + * at the current position. + * @return the next TextLayout which begins at the current position and + * represents the next line of text with width wrappingWidth, null + * is returned if the entire word at the current position does not + * fit within the wrapping width. + */ + public TextLayout nextLayout(float wrappingWidth, int offsetLimit, boolean requireNextWord) { + if (position == maxpos) { + return null; + } + + int nextPosition = nextOffset(wrappingWidth, offsetLimit, requireNextWord); + + if (nextPosition == position) { + return null; + } + TextLayout layout = tm.getLayout(position, nextPosition); + position = nextPosition; + return layout; + } + + /** + * Returns the next line of text. + * + * @param wrappingWidth + * the maximum visible line width. + * @return the next line of text. + */ + public TextLayout nextLayout(float wrappingWidth) { + return nextLayout(wrappingWidth, maxpos, false); + } + + /** + * Returns the end position of the next line of text. + * + * @param wrappingWidth + * the maximum visible line width. + * @return the end position of the next line of text. + */ + public int nextOffset(float wrappingWidth) { + return nextOffset(wrappingWidth, maxpos, false); + } + + /** + * Returns the end position of the next line of text. + * + * @param wrappingWidth + * the maximum visible line width. + * @param offsetLimit + * the limit point withing the text indicating that no further + * text should be included on the line; the paragraph break. + * @param requireNextWord + * if true, the current position is returned if the entire next + * word does not fit within wrappingWidth; if false, the offset + * returned is at least one greater than the current position. + * @return the end position of the next line of text. + * @throws IllegalArgumentException + * if the offsetLimit is less than the current position. + */ + public int nextOffset(float wrappingWidth, int offsetLimit, boolean requireNextWord) { + if (offsetLimit <= position) { + // awt.203=Offset limit should be greater than current position. + throw new IllegalArgumentException(Messages.getString("awt.203")); //$NON-NLS-1$ + } + + if (position == maxpos) { + return position; + } + + int breakPos = tm.getLineBreakIndex(position, wrappingWidth); + int correctedPos = breakPos; + + // This check is required because bi.preceding(maxpos) throws an + // exception + /* + * ???AWT if (breakPos == maxpos) { correctedPos = maxpos; } else if + * (Character.isWhitespace(bi.getText().setIndex(breakPos))) { + * correctedPos = bi.following(breakPos); } else { correctedPos = + * bi.preceding(breakPos); } + */ + + if (position >= correctedPos) { + if (requireNextWord) { + correctedPos = position; + } else { + correctedPos = Math.max(position + 1, breakPos); + } + } + + return Math.min(correctedPos, offsetLimit); + } + + /** + * Sets the new position of this LineBreakMeasurer. + * + * @param pos + * the new position of this LineBreakMeasurer. + */ + public void setPosition(int pos) { + if (tm.aci.getBeginIndex() > pos || maxpos < pos) { + // awt.33=index is out of range + throw new IllegalArgumentException(Messages.getString("awt.33")); //$NON-NLS-1$ + } + position = pos; + } +} diff --git a/awt/java/awt/font/LineMetrics.java b/awt/java/awt/font/LineMetrics.java new file mode 100644 index 000000000..4b03e5db2 --- /dev/null +++ b/awt/java/awt/font/LineMetrics.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt.font; + +/** + * The LineMetrics class provides information such as concerning how the text is + * positioned with respect to the base line, such as ascent, descent, and + * leading. + * + * @since Android 1.0 + */ +public abstract class LineMetrics { + + /** + * Gets the baseline offsets of the text according to the the baseline of + * this text. + * + * @return the baseline offsets of the text according to the the baseline of + * this text. + */ + public abstract float[] getBaselineOffsets(); + + /** + * Gets the number of characters of the text. + * + * @return the number of characters of the text. + */ + public abstract int getNumChars(); + + /** + * Gets the baseline index, returns one of the following index: + * ROMAN_BASELINE, CENTER_BASELINE, HANGING_BASELINE. + * + * @return the baseline index: ROMAN_BASELINE, CENTER_BASELINE or + * HANGING_BASELINE. + */ + public abstract int getBaselineIndex(); + + /** + * Gets the thickness of the underline. + * + * @return the thickness of the underline. + */ + public abstract float getUnderlineThickness(); + + /** + * Gets the offset of the underline. + * + * @return the offset of the underline. + */ + public abstract float getUnderlineOffset(); + + /** + * Gets the thickness of strike through line. + * + * @return the thickness of strike through line. + */ + public abstract float getStrikethroughThickness(); + + /** + * Gets the offset of the strike through line. + * + * @return the offset of the strike through line. + */ + public abstract float getStrikethroughOffset(); + + /** + * Gets the leading of the text. + * + * @return the leading of the text. + */ + public abstract float getLeading(); + + /** + * Gets the height of the text as a sum of the ascent, the descent and the + * leading. + * + * @return the height of the text as a sum of the ascent, the descent and + * the leading. + */ + public abstract float getHeight(); + + /** + * Gets the descent of the text. + * + * @return the descent of the text. + */ + public abstract float getDescent(); + + /** + * Gets the ascent of the text. + * + * @return the ascent of the text. + */ + public abstract float getAscent(); + +} diff --git a/awt/java/awt/font/MultipleMaster.java b/awt/java/awt/font/MultipleMaster.java new file mode 100644 index 000000000..d264f2483 --- /dev/null +++ b/awt/java/awt/font/MultipleMaster.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt.font; + +import java.awt.Font; + +/** + * The MultipleMaster interface provides methods to manipulate MultipleMaster + * type fonts and retrieve graphical and design data from them. + * + * @since Android 1.0 + */ +public interface MultipleMaster { + + /** + * Derives a new multiple master font based on the specified parameters. + * + * @param glyphWidths + * float array which represents width of each glyph in font + * space. + * @param avgStemWidth + * the average stem width in font space. + * @param typicalCapHeight + * the typical upper case char height. + * @param typicalXHeight + * the typical lower case char height. + * @param italicAngle + * the slope angle for italics. + * @return a MultipleMaster font. + */ + public Font deriveMMFont(float[] glyphWidths, float avgStemWidth, float typicalCapHeight, + float typicalXHeight, float italicAngle); + + /** + * Derives a new multiple master font based on the design axis values + * contained in the specified array. + * + * @param axes + * an float array which contains axis values. + * @return a MultipleMaster font. + */ + public Font deriveMMFont(float[] axes); + + /** + * Gets default design values for the axes. + * + * @return the default design values for the axes. + */ + public float[] getDesignAxisDefaults(); + + /** + * Gets the array of design axis names. + * + * @return the array of design axis names. + */ + public String[] getDesignAxisNames(); + + /** + * Gets the array of design axis ranges. + * + * @return the array of design axis ranges. + */ + public float[] getDesignAxisRanges(); + + /** + * Gets the number of multiple master design controls. + * + * @return the number of multiple master design controls. + */ + public int getNumDesignAxes(); + +} diff --git a/awt/java/awt/font/OpenType.java b/awt/java/awt/font/OpenType.java new file mode 100644 index 000000000..db66911c4 --- /dev/null +++ b/awt/java/awt/font/OpenType.java @@ -0,0 +1,418 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt.font; + +/** + * The OpenType interface provides constants and methods for getting instance + * data for fonts of type OpenType and TrueType. For more information, see the + * + * OpenType specification. + * + * @since Android 1.0 + */ +public interface OpenType { + + /** + * The Constant TAG_ACNT indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_ACNT = 1633906292; + + /** + * The Constant TAG_AVAR indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_AVAR = 1635148146; + + /** + * The Constant TAG_BASE indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_BASE = 1111577413; + + /** + * The Constant TAG_BDAT indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_BDAT = 1650745716; + + /** + * The Constant TAG_BLOC indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_BLOC = 1651273571; + + /** + * The Constant TAG_BSLN indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_BSLN = 1651731566; + + /** + * The Constant TAG_CFF indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_CFF = 1128678944; + + /** + * The Constant TAG_CMAP indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_CMAP = 1668112752; + + /** + * The Constant TAG_CVAR indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_CVAR = 1668702578; + + /** + * The Constant TAG_CVT indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_CVT = 1668707360; + + /** + * The Constant TAG_DSIG indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_DSIG = 1146308935; + + /** + * The Constant TAG_EBDT indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_EBDT = 1161970772; + + /** + * The Constant TAG_EBLC indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_EBLC = 1161972803; + + /** + * The Constant TAG_EBSC indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_EBSC = 1161974595; + + /** + * The Constant TAG_FDSC indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_FDSC = 1717859171; + + /** + * The Constant TAG_FEAT indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_FEAT = 1717920116; + + /** + * The Constant TAG_FMTX indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_FMTX = 1718449272; + + /** + * The Constant TAG_FPGM indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_FPGM = 1718642541; + + /** + * The Constant TAG_FVAR indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_FVAR = 1719034226; + + /** + * The Constant TAG_GASP indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_GASP = 1734439792; + + /** + * The Constant TAG_GDEF indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_GDEF = 1195656518; + + /** + * The Constant TAG_GLYF indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_GLYF = 1735162214; + + /** + * The Constant TAG_GPOS indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_GPOS = 1196445523; + + /** + * The Constant TAG_GSUB indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_GSUB = 1196643650; + + /** + * The Constant TAG_GVAR indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_GVAR = 1735811442; + + /** + * The Constant TAG_HDMX indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_HDMX = 1751412088; + + /** + * The Constant TAG_HEAD indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_HEAD = 1751474532; + + /** + * The Constant TAG_HHEA indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_HHEA = 1751672161; + + /** + * The Constant TAG_HMTX indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_HMTX = 1752003704; + + /** + * The Constant TAG_JSTF indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_JSTF = 1246975046; + + /** + * The Constant TAG_JUST indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_JUST = 1786082164; + + /** + * The Constant TAG_KERN indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_KERN = 1801810542; + + /** + * The Constant TAG_LCAR indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_LCAR = 1818452338; + + /** + * The Constant TAG_LOCA indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_LOCA = 1819239265; + + /** + * The Constant TAG_LTSH indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_LTSH = 1280594760; + + /** + * The Constant TAG_MAXP indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_MAXP = 1835104368; + + /** + * The Constant TAG_MMFX indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_MMFX = 1296909912; + + /** + * The Constant TAG_MMSD indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_MMSD = 1296913220; + + /** + * The Constant TAG_MORT indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_MORT = 1836020340; + + /** + * The Constant TAG_NAME indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_NAME = 1851878757; + + /** + * The Constant TAG_OPBD indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_OPBD = 1836020340; + + /** + * The Constant TAG_OS2 indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_OS2 = 1330851634; + + /** + * The Constant TAG_PCLT indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_PCLT = 1346587732; + + /** + * The Constant TAG_POST indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_POST = 1886352244; + + /** + * The Constant TAG_PREP indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_PREP = 1886545264; + + /** + * The Constant TAG_PROP indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_PROP = 1886547824; + + /** + * The Constant TAG_TRAK indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_TRAK = 1953653099; + + /** + * The Constant TAG_TYP1 indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_TYP1 = 1954115633; + + /** + * The Constant TAG_VDMX indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_VDMX = 1447316824; + + /** + * The Constant TAG_VHEA indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_VHEA = 1986553185; + + /** + * The Constant TAG_VMTX indicates corresponding table tag in the Open Type + * Specification. + */ + public static final int TAG_VMTX = 1986884728; + + /** + * Returns the OpenType font version. + * + * @return the the OpenType font version. + */ + public int getVersion(); + + /** + * Gets the table for a specified tag. Sfnt tables include cmap, name and + * head items. + * + * @param sfntTag + * the sfnt tag. + * @return a byte array contains the font data corresponding to the + * specified tag. + */ + public byte[] getFontTable(int sfntTag); + + /** + * Gets the table for a specified tag. Sfnt tables include cmap, name and + * head items. + * + * @param sfntTag + * the sfnt tag. + * @param offset + * the offset of the returned table. + * @param count + * the number of returned table. + * @return the table corresponding to sfntTag and containing the bytes + * starting at offset byte and including count bytes. + */ + public byte[] getFontTable(int sfntTag, int offset, int count); + + /** + * Gets the table for a specified tag. Sfnt tables include cmap, name and + * head items. + * + * @param strSfntTag + * the str sfnt tag as a String. + * @return a byte array contains the font data corresponding to the + * specified tag. + */ + public byte[] getFontTable(String strSfntTag); + + /** + * Gets the table for a specified tag. Sfnt tables include cmap, name and + * head items. + * + * @param strSfntTag + * the sfnt tag as a String. + * @param offset + * the offset of the returned table. + * @param count + * the number of returned table. + * @return the table corresponding to sfntTag and containing the bytes + * starting at offset byte and including count bytes. + */ + public byte[] getFontTable(String strSfntTag, int offset, int count); + + /** + * Gets the table size for a specified tag. + * + * @param strSfntTag + * the sfnt tag as a String. + * @return the table size for a specified tag. + */ + public int getFontTableSize(String strSfntTag); + + /** + * Gets the table size for a specified tag. + * + * @param sfntTag + * the sfnt tag. + * @return the table size for a specified tag. + */ + public int getFontTableSize(int sfntTag); + +} diff --git a/awt/java/awt/font/ShapeGraphicAttribute.java b/awt/java/awt/font/ShapeGraphicAttribute.java new file mode 100644 index 000000000..182bffddc --- /dev/null +++ b/awt/java/awt/font/ShapeGraphicAttribute.java @@ -0,0 +1,206 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt.font; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; + +import org.apache.harmony.misc.HashCode; + +/** + * The ShapeGraphicAttribute class provides an opportunity to insert shapes to a + * text. + * + * @since Android 1.0 + */ +public final class ShapeGraphicAttribute extends GraphicAttribute { + + // shape to render + /** + * The shape. + */ + private Shape fShape; + + // flag, if the shape should be stroked (true) or filled (false) + /** + * The stroke. + */ + private boolean fStroke; + + // bounds of the shape + /** + * The bounds. + */ + private Rectangle2D fBounds; + + // X coordinate of the origin point + /** + * The origin x. + */ + private float fOriginX; + + // Y coordinate of the origin point + /** + * The origin y. + */ + private float fOriginY; + + // width of the shape + /** + * The shape width. + */ + private float fShapeWidth; + + // height of the shape + /** + * The shape height. + */ + private float fShapeHeight; + + /** + * The Constant STROKE indicates whether the Shape is stroked or not. + */ + public static final boolean STROKE = true; + + /** + * The Constant FILL indicates whether the Shape is filled or not. + */ + public static final boolean FILL = false; + + /** + * Instantiates a new ShapeGraphicAttribute object for the specified Shape. + * + * @param shape + * the shape to be rendered by this ShapeGraphicAttribute. + * @param alignment + * the alignment of this ShapeGraphicAttribute. + * @param stroke + * true if the Shape is stroked, false if the Shape is filled. + */ + public ShapeGraphicAttribute(Shape shape, int alignment, boolean stroke) { + super(alignment); + + this.fShape = shape; + this.fStroke = stroke; + + this.fBounds = fShape.getBounds2D(); + + this.fOriginX = (float)fBounds.getMinX(); + this.fOriginY = (float)fBounds.getMinY(); + + this.fShapeWidth = (float)fBounds.getWidth(); + this.fShapeHeight = (float)fBounds.getHeight(); + } + + /** + * Returns a hash code of this ShapeGraphicAttribute object. + * + * @return a hash code of this ShapeGraphicAttribute object. + */ + @Override + public int hashCode() { + HashCode hash = new HashCode(); + + hash.append(fShape.hashCode()); + hash.append(getAlignment()); + return hash.hashCode(); + } + + /** + * Compares this ShapeGraphicAttribute object to the specified + * ShapeGraphicAttribute object. + * + * @param sga + * the ShapeGraphicAttribute object to be compared. + * @return true, if this ShapeGraphicAttribute object is equal to the + * specified ShapeGraphicAttribute object, false otherwise. + */ + public boolean equals(ShapeGraphicAttribute sga) { + if (sga == null) { + return false; + } + + if (sga == this) { + return true; + } + + return (fStroke == sga.fStroke && getAlignment() == sga.getAlignment() && fShape + .equals(sga.fShape)); + + } + + /** + * Compares this ShapeGraphicAttribute object to the specified Object. + * + * @param obj + * the Object to be compared. + * @return true, if this ShapeGraphicAttribute object is equal to the + * specified Object, false otherwise. + */ + @Override + public boolean equals(Object obj) { + try { + return equals((ShapeGraphicAttribute)obj); + } catch (ClassCastException e) { + return false; + } + } + + @Override + public void draw(Graphics2D g2, float x, float y) { + AffineTransform at = AffineTransform.getTranslateInstance(x, y); + if (fStroke == STROKE) { + Stroke oldStroke = g2.getStroke(); + g2.setStroke(new BasicStroke()); + g2.draw(at.createTransformedShape(fShape)); + g2.setStroke(oldStroke); + } else { + g2.fill(at.createTransformedShape(fShape)); + } + + } + + @Override + public float getAdvance() { + return Math.max(0, fShapeWidth + fOriginX); + } + + @Override + public float getAscent() { + return Math.max(0, -fOriginY); + } + + @Override + public Rectangle2D getBounds() { + return (Rectangle2D)fBounds.clone(); + } + + @Override + public float getDescent() { + return Math.max(0, fShapeHeight + fOriginY); + } + +} diff --git a/awt/java/awt/font/TextHitInfo.java b/awt/java/awt/font/TextHitInfo.java new file mode 100644 index 000000000..6460ebace --- /dev/null +++ b/awt/java/awt/font/TextHitInfo.java @@ -0,0 +1,215 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/* + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package java.awt.font; + +import org.apache.harmony.misc.HashCode; + +/** + * The TextHitInfo class provides information about a caret position in a text + * model for insertion or deletion of a character in a text. The TextHitInfo + * defines two biases of the character: leading or trailing. Leading position + * means the left edge of the specified character (TextHitInfo.leading(2) method + * for "text" returns the left side of "x"). Trailing position means the right + * edge of the specified character (TextHitInfo.trailing(2) method for "text" + * returns the right side of "x"). + * + * @since Android 1.0 + */ +public final class TextHitInfo { + + /** + * The char idx. + */ + private int charIdx; // Represents character index in the line + + /** + * The is trailing. + */ + private boolean isTrailing; + + /** + * Instantiates a new text hit info. + * + * @param idx + * the idx. + * @param isTrailing + * the is trailing. + */ + private TextHitInfo(int idx, boolean isTrailing) { + charIdx = idx; + this.isTrailing = isTrailing; + } + + /** + * Returns the textual string representation of this TextHitInfo instance. + * + * @return the string representation. + */ + @Override + public String toString() { + return new String("TextHitInfo[" + charIdx + ", " + //$NON-NLS-1$ //$NON-NLS-2$ + (isTrailing ? "Trailing" : "Leading") + "]" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + ); + } + + /** + * Compares this TextHitInfo object with the specified object. + * + * @param obj + * the Object to be compared. + * @return true, if the specified object is a TextHitInfo object with the + * same data values as this TextHitInfo, false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof TextHitInfo) { + return equals((TextHitInfo)obj); + } + return false; + } + + /** + * Compares this TextHitInfo object with the specified TextHitInfo object. + * + * @param thi + * the TextHitInfo object to be compared. + * @return true, if this TextHitInfo object has the same data values as the + * specified TextHitInfo object, false otherwise. + */ + public boolean equals(TextHitInfo thi) { + return thi != null && thi.charIdx == charIdx && thi.isTrailing == isTrailing; + } + + /** + * Gets a TextHitInfo object with its character index at the specified + * offset from the character index of this TextHitInfo. + * + * @param offset + * the offset. + * @return the TextHitInfo. + */ + public TextHitInfo getOffsetHit(int offset) { + return new TextHitInfo(charIdx + offset, isTrailing); + } + + /** + * Gets a TextHitInfo associated with the other side of the insertion point. + * + * @return the other hit. + */ + public TextHitInfo getOtherHit() { + return isTrailing ? new TextHitInfo(charIdx + 1, false) + : new TextHitInfo(charIdx - 1, true); + } + + /** + * Returns true if the leading edge of the character is hit, false if the + * trailing edge of the character is hit. + * + * @return true if the leading edge of the character is hit, false if the + * trailing edge of the character is hit. + */ + public boolean isLeadingEdge() { + return !isTrailing; + } + + /** + * Returns the hash code value of this TextHitInfo instance. + * + * @return the hash code value. + */ + @Override + public int hashCode() { + return HashCode.combine(charIdx, isTrailing); + } + + /** + * Gets the insertion index. + * + * @return the insertion index: character index if the leading edge is hit, + * or character index + 1 if the trailing edge is hit. + */ + public int getInsertionIndex() { + return isTrailing ? charIdx + 1 : charIdx; + } + + /** + * Gets the index of the character hit. + * + * @return the character hit's index. + */ + public int getCharIndex() { + return charIdx; + } + + /** + * Returns a TextHitInfo associated with the trailing edge of the character + * at the specified char index. + * + * @param charIndex + * the char index. + * @return the TextHitInfo associated with the trailing edge of the + * character at the specified char index. + */ + public static TextHitInfo trailing(int charIndex) { + return new TextHitInfo(charIndex, true); + } + + /** + * Returns a TextHitInfo object associated with the leading edge of the + * character at the specified char index. + * + * @param charIndex + * the char index. + * @return the TextHitInfo object associated with the leading edge of the + * character at the specified char index. + */ + public static TextHitInfo leading(int charIndex) { + return new TextHitInfo(charIndex, false); + } + + /** + * Returns a (trailing) TextHitInfo object associated with the character + * before the specified offset. + * + * @param offset + * the offset. + * @return the TextHitInfo object associated with the character before the + * specified offset. + */ + public static TextHitInfo beforeOffset(int offset) { + return new TextHitInfo(offset - 1, true); + } + + /** + * Returns a (leading) TextHitInfo object associated with the character + * after the specified offset. + * + * @param offset + * the offset. + * @return the TextHitInfo object associated with the character after the + * specified offset. + */ + public static TextHitInfo afterOffset(int offset) { + return new TextHitInfo(offset, false); + } +} diff --git a/awt/java/awt/font/TextLayout.java b/awt/java/awt/font/TextLayout.java new file mode 100644 index 000000000..cc6f0ba3a --- /dev/null +++ b/awt/java/awt/font/TextLayout.java @@ -0,0 +1,927 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package java.awt.font; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.geom.GeneralPath; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.util.Map; + +import org.apache.harmony.awt.gl.font.BasicMetrics; +import org.apache.harmony.awt.gl.font.CaretManager; +import org.apache.harmony.awt.gl.font.TextMetricsCalculator; +import org.apache.harmony.awt.gl.font.TextRunBreaker; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The TextLayout class defines the graphical representation of character data. + * This class provides method for obtaining information about cursor positioning + * and movement, split cursors for text with different directions, logical and + * visual highlighting, multiple baselines, hits, justification, ascent, + * descent, and advance, and rendering. A TextLayout object can be rendered + * using Graphics context. + * + * @since Android 1.0 + */ +public final class TextLayout implements Cloneable { + + /** + * The CaretPolicy class provides a policy for obtaining the caret location. + * The single getStrongCaret method specifies the policy. + */ + public static class CaretPolicy { + + /** + * Instantiates a new CaretPolicy. + */ + public CaretPolicy() { + // Nothing to do + } + + /** + * Returns whichever of the two specified TextHitInfo objects has the + * stronger caret (higher character level) in the specified TextLayout. + * + * @param hit1 + * the first TextHitInfo of the specified TextLayout. + * @param hit2 + * the second TextHitInfo of the specified TextLayout. + * @param layout + * the TextLayout. + * @return the TextHitInfo with the stronger caret. + */ + public TextHitInfo getStrongCaret(TextHitInfo hit1, TextHitInfo hit2, TextLayout layout) { + // Stronger hit is the one with greater level. + // If the level is same, leading edge is stronger. + + int level1 = layout.getCharacterLevel(hit1.getCharIndex()); + int level2 = layout.getCharacterLevel(hit2.getCharIndex()); + + if (level1 == level2) { + return (hit2.isLeadingEdge() && (!hit1.isLeadingEdge())) ? hit2 : hit1; + } + return level1 > level2 ? hit1 : hit2; + } + + } + + /** + * The Constant DEFAULT_CARET_POLICY indicates the default caret policy. + */ + public static final TextLayout.CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy(); + + /** + * The breaker. + */ + private TextRunBreaker breaker; + + /** + * The metrics valid. + */ + private boolean metricsValid = false; + + /** + * The tmc. + */ + private TextMetricsCalculator tmc; + + /** + * The metrics. + */ + private BasicMetrics metrics; + + /** + * The caret manager. + */ + private CaretManager caretManager; + + /** + * The justification width. + */ + float justificationWidth = -1; + + /** + * Instantiates a new TextLayout object from the specified string and Font. + * + * @param string + * the string to be displayed. + * @param font + * the font of the text. + * @param frc + * the FontRenderContext object for obtaining information about a + * graphics device. + */ + public TextLayout(String string, Font font, FontRenderContext frc) { + if (string == null) { + // awt.01='{0}' parameter is null + throw new IllegalArgumentException(Messages.getString("awt.01", "string")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (font == null) { + // awt.01='{0}' parameter is null + throw new IllegalArgumentException(Messages.getString("awt.01", "font")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (string.length() == 0) { + // awt.02='{0}' parameter has zero length + throw new IllegalArgumentException(Messages.getString("awt.02", "string")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + AttributedString as = new AttributedString(string); + as.addAttribute(TextAttribute.FONT, font); + this.breaker = new TextRunBreaker(as.getIterator(), frc); + caretManager = new CaretManager(breaker); + } + + /** + * Instantiates a new TextLayout from the specified text and a map of + * attributes. + * + * @param string + * the string to be displayed. + * @param attributes + * the attributes to be used for obtaining the text style. + * @param frc + * the FontRenderContext object for obtaining information about a + * graphics device. + */ + public TextLayout(String string, + Map attributes, + FontRenderContext frc) { + if (string == null) { + // awt.01='{0}' parameter is null + throw new IllegalArgumentException(Messages.getString("awt.01", "string")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (attributes == null) { + // awt.01='{0}' parameter is null + throw new IllegalArgumentException(Messages.getString("awt.01", "attributes")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (string.length() == 0) { + // awt.02='{0}' parameter has zero length + throw new IllegalArgumentException(Messages.getString("awt.02", "string")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + AttributedString as = new AttributedString(string); + as.addAttributes(attributes, 0, string.length()); + this.breaker = new TextRunBreaker(as.getIterator(), frc); + caretManager = new CaretManager(breaker); + } + + /** + * Instantiates a new TextLayout from the AttributedCharacterIterator. + * + * @param text + * the AttributedCharacterIterator. + * @param frc + * the FontRenderContext object for obtaining information about a + * graphics device. + */ + public TextLayout(AttributedCharacterIterator text, FontRenderContext frc) { + if (text == null) { + // awt.03='{0}' iterator parameter is null + throw new IllegalArgumentException(Messages.getString("awt.03", "text")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (text.getBeginIndex() == text.getEndIndex()) { + // awt.04='{0}' iterator parameter has zero length + throw new IllegalArgumentException(Messages.getString("awt.04", "text")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + this.breaker = new TextRunBreaker(text, frc); + caretManager = new CaretManager(breaker); + } + + /** + * Instantiates a new text layout. + * + * @param breaker + * the breaker. + */ + TextLayout(TextRunBreaker breaker) { + this.breaker = breaker; + caretManager = new CaretManager(this.breaker); + } + + /** + * Returns a hash code of this TextLayout object. + * + * @return a hash code of this TextLayout object. + */ + @Override + public int hashCode() { + return breaker.hashCode(); + } + + /** + * Returns a copy of this object. + * + * @return a copy of this object. + */ + @Override + protected Object clone() { + TextLayout res = new TextLayout((TextRunBreaker)breaker.clone()); + + if (justificationWidth >= 0) { + res.handleJustify(justificationWidth); + } + + return res; + } + + /** + * Compares this TextLayout object to the specified TextLayout object. + * + * @param layout + * the TextLayout object to be compared. + * @return true, if this TextLayout object is equal to the specified + * TextLayout object, false otherwise. + */ + public boolean equals(TextLayout layout) { + if (layout == null) { + return false; + } + return this.breaker.equals(layout.breaker); + } + + /** + * Compares this TextLayout object to the specified Object. + * + * @param obj + * the Object to be compared. + * @return true, if this TextLayout object is equal to the specified Object, + * false otherwise. + */ + @Override + public boolean equals(Object obj) { + return obj instanceof TextLayout ? equals((TextLayout)obj) : false; + } + + /** + * Gets the string representation for this TextLayout. + * + * @return the string representation for this TextLayout. + */ + @Override + public String toString() { // what for? + return super.toString(); + } + + /** + * Draws this TextLayout at the specified location with the specified + * Graphics2D context. + * + * @param g2d + * the Graphics2D object which renders this TextLayout. + * @param x + * the X coordinate of the TextLayout origin. + * @param y + * the Y coordinate of the TextLayout origin. + */ + public void draw(Graphics2D g2d, float x, float y) { + updateMetrics(); + breaker.drawSegments(g2d, x, y); + } + + /** + * Update metrics. + */ + private void updateMetrics() { + if (!metricsValid) { + breaker.createAllSegments(); + tmc = new TextMetricsCalculator(breaker); + metrics = tmc.createMetrics(); + metricsValid = true; + } + } + + /** + * Gets the advance of this TextLayout object. + * + * @return the advance of this TextLayout object. + */ + public float getAdvance() { + updateMetrics(); + return metrics.getAdvance(); + } + + /** + * Gets the ascent of this TextLayout object. + * + * @return the ascent of this TextLayout object. + */ + public float getAscent() { + updateMetrics(); + return metrics.getAscent(); + } + + /** + * Gets the baseline of this TextLayout object. + * + * @return the baseline of this TextLayout object. + */ + public byte getBaseline() { + updateMetrics(); + return (byte)metrics.getBaseLineIndex(); + } + + /** + * Gets the float array of offsets for the baselines which are used in this + * TextLayout. + * + * @return the float array of offsets for the baselines which are used in + * this TextLayout. + */ + public float[] getBaselineOffsets() { + updateMetrics(); + return tmc.getBaselineOffsets(); + } + + /** + * Gets the black box bounds of the characters in the specified area. The + * black box bounds is an Shape which contains all bounding boxes of all the + * glyphs of the characters between firstEndpoint and secondEndpoint + * parameters values. + * + * @param firstEndpoint + * the first point of the area. + * @param secondEndpoint + * the second point of the area. + * @return the Shape which contains black box bounds. + */ + public Shape getBlackBoxBounds(int firstEndpoint, int secondEndpoint) { + updateMetrics(); + if (firstEndpoint < secondEndpoint) { + return breaker.getBlackBoxBounds(firstEndpoint, secondEndpoint); + } + return breaker.getBlackBoxBounds(secondEndpoint, firstEndpoint); + } + + /** + * Gets the bounds of this TextLayout. + * + * @return the bounds of this TextLayout. + */ + public Rectangle2D getBounds() { + updateMetrics(); + return breaker.getVisualBounds(); + } + + /** + * Gets information about the caret of the specified TextHitInfo. + * + * @param hitInfo + * the TextHitInfo. + * @return the information about the caret of the specified TextHitInfo. + */ + public float[] getCaretInfo(TextHitInfo hitInfo) { + updateMetrics(); + return caretManager.getCaretInfo(hitInfo); + } + + /** + * Gets information about the caret of the specified TextHitInfo of a + * character in this TextLayout. + * + * @param hitInfo + * the TextHitInfo of a character in this TextLayout. + * @param bounds + * the bounds to which the caret info is constructed. + * @return the caret of the specified TextHitInfo. + */ + public float[] getCaretInfo(TextHitInfo hitInfo, Rectangle2D bounds) { + updateMetrics(); + return caretManager.getCaretInfo(hitInfo); + } + + /** + * Gets a Shape which represents the caret of the specified TextHitInfo in + * the bounds of this TextLayout. + * + * @param hitInfo + * the TextHitInfo. + * @param bounds + * the bounds to which the caret info is constructed. + * @return the Shape which represents the caret. + */ + public Shape getCaretShape(TextHitInfo hitInfo, Rectangle2D bounds) { + updateMetrics(); + return caretManager.getCaretShape(hitInfo, this); + } + + /** + * Gets a Shape which represents the caret of the specified TextHitInfo in + * the bounds of this TextLayout. + * + * @param hitInfo + * the TextHitInfo. + * @return the Shape which represents the caret. + */ + public Shape getCaretShape(TextHitInfo hitInfo) { + updateMetrics(); + return caretManager.getCaretShape(hitInfo, this); + } + + /** + * Gets two Shapes for the strong and weak carets with default caret policy + * and null bounds: the first element is the strong caret, the second is the + * weak caret or null. + * + * @param offset + * an offset in the TextLayout. + * @return an array of two Shapes corresponded to the strong and weak + * carets. + */ + public Shape[] getCaretShapes(int offset) { + return getCaretShapes(offset, null, TextLayout.DEFAULT_CARET_POLICY); + } + + /** + * Gets two Shapes for the strong and weak carets with the default caret + * policy: the first element is the strong caret, the second is the weak + * caret or null. + * + * @param offset + * an offset in the TextLayout. + * @param bounds + * the bounds to which to extend the carets. + * @return an array of two Shapes corresponded to the strong and weak + * carets. + */ + public Shape[] getCaretShapes(int offset, Rectangle2D bounds) { + return getCaretShapes(offset, bounds, TextLayout.DEFAULT_CARET_POLICY); + } + + /** + * Gets two Shapes for the strong and weak carets: the first element is the + * strong caret, the second is the weak caret or null. + * + * @param offset + * an offset in the TextLayout. + * @param bounds + * the bounds to which to extend the carets. + * @param policy + * the specified CaretPolicy. + * @return an array of two Shapes corresponded to the strong and weak + * carets. + */ + public Shape[] getCaretShapes(int offset, Rectangle2D bounds, TextLayout.CaretPolicy policy) { + if (offset < 0 || offset > breaker.getCharCount()) { + // awt.195=Offset is out of bounds + throw new IllegalArgumentException(Messages.getString("awt.195")); //$NON-NLS-1$ + } + + updateMetrics(); + return caretManager.getCaretShapes(offset, bounds, policy, this); + } + + /** + * Gets the number of characters in this TextLayout. + * + * @return the number of characters in this TextLayout. + */ + public int getCharacterCount() { + return breaker.getCharCount(); + } + + /** + * Gets the level of the character with the specified index. + * + * @param index + * the specified index of the character. + * @return the level of the character. + */ + public byte getCharacterLevel(int index) { + if (index == -1 || index == getCharacterCount()) { + return (byte)breaker.getBaseLevel(); + } + return breaker.getLevel(index); + } + + /** + * Gets the descent of this TextLayout. + * + * @return the descent of this TextLayout. + */ + public float getDescent() { + updateMetrics(); + return metrics.getDescent(); + } + + /** + * Gets the TextLayout wich is justified with the specified width related to + * this TextLayout. + * + * @param justificationWidth + * the width which is used for justification. + * @return a TextLayout justified to the specified width. + * @throws Error + * the error occures if this TextLayout has been already + * justified. + */ + public TextLayout getJustifiedLayout(float justificationWidth) throws Error { + float justification = breaker.getJustification(); + + if (justification < 0) { + // awt.196=Justification impossible, layout already justified + throw new Error(Messages.getString("awt.196")); //$NON-NLS-1$ + } else if (justification == 0) { + return this; + } + + TextLayout justifiedLayout = new TextLayout((TextRunBreaker)breaker.clone()); + justifiedLayout.handleJustify(justificationWidth); + return justifiedLayout; + } + + /** + * Gets the leading of this TextLayout. + * + * @return the leading of this TextLayout. + */ + public float getLeading() { + updateMetrics(); + return metrics.getLeading(); + } + + /** + * Gets a Shape representing the logical selection betweeen the specified + * endpoints and extended to the natural bounds of this TextLayout. + * + * @param firstEndpoint + * the first selected endpoint within the area of characters + * @param secondEndpoint + * the second selected endpoint within the area of characters + * @return a Shape represented the logical selection betweeen the specified + * endpoints. + */ + public Shape getLogicalHighlightShape(int firstEndpoint, int secondEndpoint) { + updateMetrics(); + return getLogicalHighlightShape(firstEndpoint, secondEndpoint, breaker.getLogicalBounds()); + } + + /** + * Gets a Shape representing the logical selection betweeen the specified + * endpoints and extended to the specified bounds of this TextLayout. + * + * @param firstEndpoint + * the first selected endpoint within the area of characters + * @param secondEndpoint + * the second selected endpoint within the area of characters + * @param bounds + * the specified bounds of this TextLayout. + * @return a Shape represented the logical selection betweeen the specified + * endpoints. + */ + public Shape getLogicalHighlightShape(int firstEndpoint, int secondEndpoint, Rectangle2D bounds) { + updateMetrics(); + + if (firstEndpoint > secondEndpoint) { + if (secondEndpoint < 0 || firstEndpoint > breaker.getCharCount()) { + // awt.197=Endpoints are out of range + throw new IllegalArgumentException(Messages.getString("awt.197")); //$NON-NLS-1$ + } + return caretManager.getLogicalHighlightShape(secondEndpoint, firstEndpoint, bounds, + this); + } + if (firstEndpoint < 0 || secondEndpoint > breaker.getCharCount()) { + // awt.197=Endpoints are out of range + throw new IllegalArgumentException(Messages.getString("awt.197")); //$NON-NLS-1$ + } + return caretManager.getLogicalHighlightShape(firstEndpoint, secondEndpoint, bounds, this); + } + + /** + * Gets the logical ranges of text which corresponds to a visual selection. + * + * @param hit1 + * the first endpoint of the visual range. + * @param hit2 + * the second endpoint of the visual range. + * @return the logical ranges of text which corresponds to a visual + * selection. + */ + public int[] getLogicalRangesForVisualSelection(TextHitInfo hit1, TextHitInfo hit2) { + return caretManager.getLogicalRangesForVisualSelection(hit1, hit2); + } + + /** + * Gets the TextHitInfo for the next caret to the left (or up at the end of + * the line) of the specified offset. + * + * @param offset + * the offset in this TextLayout. + * @return the TextHitInfo for the next caret to the left (or up at the end + * of the line) of the specified hit, or null if there is no hit. + */ + public TextHitInfo getNextLeftHit(int offset) { + return getNextLeftHit(offset, DEFAULT_CARET_POLICY); + } + + /** + * Gets the TextHitInfo for the next caret to the left (or up at the end of + * the line) of the specified hit. + * + * @param hitInfo + * the initial hit. + * @return the TextHitInfo for the next caret to the left (or up at the end + * of the line) of the specified hit, or null if there is no hit. + */ + public TextHitInfo getNextLeftHit(TextHitInfo hitInfo) { + breaker.createAllSegments(); + return caretManager.getNextLeftHit(hitInfo); + } + + /** + * Gets the TextHitInfo for the next caret to the left (or up at the end of + * the line) of the specified offset, given the specified caret policy. + * + * @param offset + * the offset in this TextLayout. + * @param policy + * the policy to be used for obtaining the strong caret. + * @return the TextHitInfo for the next caret to the left of the specified + * offset, or null if there is no hit. + */ + public TextHitInfo getNextLeftHit(int offset, TextLayout.CaretPolicy policy) { + if (offset < 0 || offset > breaker.getCharCount()) { + // awt.195=Offset is out of bounds + throw new IllegalArgumentException(Messages.getString("awt.195")); //$NON-NLS-1$ + } + + TextHitInfo hit = TextHitInfo.afterOffset(offset); + TextHitInfo strongHit = policy.getStrongCaret(hit, hit.getOtherHit(), this); + TextHitInfo nextLeftHit = getNextLeftHit(strongHit); + + if (nextLeftHit != null) { + return policy.getStrongCaret(getVisualOtherHit(nextLeftHit), nextLeftHit, this); + } + return null; + } + + /** + * Gets the TextHitInfo for the next caret to the right (or down at the end + * of the line) of the specified hit. + * + * @param hitInfo + * the initial hit. + * @return the TextHitInfo for the next caret to the right (or down at the + * end of the line) of the specified hit, or null if there is no + * hit. + */ + public TextHitInfo getNextRightHit(TextHitInfo hitInfo) { + breaker.createAllSegments(); + return caretManager.getNextRightHit(hitInfo); + } + + /** + * Gets the TextHitInfo for the next caret to the right (or down at the end + * of the line) of the specified offset. + * + * @param offset + * the offset in this TextLayout. + * @return the TextHitInfo for the next caret to the right of the specified + * offset, or null if there is no hit. + */ + public TextHitInfo getNextRightHit(int offset) { + return getNextRightHit(offset, DEFAULT_CARET_POLICY); + } + + /** + * Gets the TextHitInfo for the next caret to the right (or down at the end + * of the line) of the specified offset, given the specified caret policy. + * + * @param offset + * the offset in this TextLayout. + * @param policy + * the policy to be used for obtaining the strong caret. + * @return the TextHitInfo for the next caret to the right of the specified + * offset, or null if there is no hit. + */ + public TextHitInfo getNextRightHit(int offset, TextLayout.CaretPolicy policy) { + if (offset < 0 || offset > breaker.getCharCount()) { + // awt.195=Offset is out of bounds + throw new IllegalArgumentException(Messages.getString("awt.195")); //$NON-NLS-1$ + } + + TextHitInfo hit = TextHitInfo.afterOffset(offset); + TextHitInfo strongHit = policy.getStrongCaret(hit, hit.getOtherHit(), this); + TextHitInfo nextRightHit = getNextRightHit(strongHit); + + if (nextRightHit != null) { + return policy.getStrongCaret(getVisualOtherHit(nextRightHit), nextRightHit, this); + } + return null; + } + + /** + * Gets the outline of this TextLayout as a Shape. + * + * @param xform + * the AffineTransform to be used to transform the outline before + * returning it, or null if no transformation is desired. + * @return the outline of this TextLayout as a Shape. + */ + public Shape getOutline(AffineTransform xform) { + breaker.createAllSegments(); + + GeneralPath outline = breaker.getOutline(); + + if (outline != null && xform != null) { + outline.transform(xform); + } + + return outline; + } + + /** + * Gets the visible advance of this TextLayout which is defined as diffence + * between leading (advance) and trailing whitespace. + * + * @return the visible advance of this TextLayout. + */ + public float getVisibleAdvance() { + updateMetrics(); + + // Trailing whitespace _SHOULD_ be reordered (Unicode spec) to + // base direction, so it is also trailing + // in logical representation. We use this fact. + int lastNonWhitespace = breaker.getLastNonWhitespace(); + + if (lastNonWhitespace < 0) { + return 0; + } else if (lastNonWhitespace == getCharacterCount() - 1) { + return getAdvance(); + } else if (justificationWidth >= 0) { // Layout is justified + return justificationWidth; + } else { + breaker.pushSegments(breaker.getACI().getBeginIndex(), lastNonWhitespace + + breaker.getACI().getBeginIndex() + 1); + + breaker.createAllSegments(); + + float visAdvance = tmc.createMetrics().getAdvance(); + + breaker.popSegments(); + return visAdvance; + } + } + + /** + * Gets a Shape which corresponds to the highlighted (selected) area based + * on two hit locations within the text and extends to the bounds. + * + * @param hit1 + * the first text hit location. + * @param hit2 + * the second text hit location. + * @param bounds + * the rectangle that the highlighted area should be extended or + * restricted to. + * @return a Shape which corresponds to the highlighted (selected) area. + */ + public Shape getVisualHighlightShape(TextHitInfo hit1, TextHitInfo hit2, Rectangle2D bounds) { + return caretManager.getVisualHighlightShape(hit1, hit2, bounds, this); + } + + /** + * Gets a Shape which corresponds to the highlighted (selected) area based + * on two hit locations within the text. + * + * @param hit1 + * the first text hit location. + * @param hit2 + * the second text hit location. + * @return a Shape which corresponds to the highlighted (selected) area. + */ + public Shape getVisualHighlightShape(TextHitInfo hit1, TextHitInfo hit2) { + breaker.createAllSegments(); + return caretManager.getVisualHighlightShape(hit1, hit2, breaker.getLogicalBounds(), this); + } + + /** + * Gets the TextHitInfo for a hit on the opposite side of the specified + * hit's caret. + * + * @param hitInfo + * the specified TextHitInfo. + * @return the TextHitInfo for a hit on the opposite side of the specified + * hit's caret. + */ + public TextHitInfo getVisualOtherHit(TextHitInfo hitInfo) { + return caretManager.getVisualOtherHit(hitInfo); + } + + /** + * Justifies the text; this method should be overridden by subclasses. + * + * @param justificationWidth + * the width for justification. + */ + protected void handleJustify(float justificationWidth) { + float justification = breaker.getJustification(); + + if (justification < 0) { + // awt.196=Justification impossible, layout already justified + throw new IllegalStateException(Messages.getString("awt.196")); //$NON-NLS-1$ + } else if (justification == 0) { + return; + } + + float gap = (justificationWidth - getVisibleAdvance()) * justification; + breaker.justify(gap); + this.justificationWidth = justificationWidth; + + // Correct metrics + tmc = new TextMetricsCalculator(breaker); + tmc.correctAdvance(metrics); + } + + /** + * Returns a TextHitInfo object that gives information on which division + * point (between two characters) is corresponds to a hit (such as a mouse + * click) at the specified coordinates. + * + * @param x + * the X coordinate in this TextLayout. + * @param y + * the Y coordinate in this TextLayout. TextHitInfo object + * corresponding to the given coordinates within the text. + * @return the information about the character at the specified position. + */ + public TextHitInfo hitTestChar(float x, float y) { + return hitTestChar(x, y, getBounds()); + } + + /** + * Returns a TextHitInfo object that gives information on which division + * point (between two characters) is corresponds to a hit (such as a mouse + * click) at the specified coordinates within the specified text rectangle. + * + * @param x + * the X coordinate in this TextLayout. + * @param y + * the Y coordinate in this TextLayout. + * @param bounds + * the bounds of the text area. TextHitInfo object corresponding + * to the given coordinates within the text. + * @return the information about the character at the specified position. + */ + public TextHitInfo hitTestChar(float x, float y, Rectangle2D bounds) { + if (x > bounds.getMaxX()) { + return breaker.isLTR() ? TextHitInfo.trailing(breaker.getCharCount() - 1) : TextHitInfo + .leading(0); + } + + if (x < bounds.getMinX()) { + return breaker.isLTR() ? TextHitInfo.leading(0) : TextHitInfo.trailing(breaker + .getCharCount() - 1); + } + + return breaker.hitTest(x, y); + } + + /** + * Returns true if this TextLayout has a "left to right" direction. + * + * @return true if this TextLayout has a "left to right" direction, false if + * this TextLayout has a "right to left" direction. + */ + public boolean isLeftToRight() { + return breaker.isLTR(); + } + + /** + * Returns true if this TextLayout is vertical, false otherwise. + * + * @return true if this TextLayout is vertical, false if horizontal. + */ + public boolean isVertical() { + return false; + } +} diff --git a/awt/java/awt/font/TextMeasurer.java b/awt/java/awt/font/TextMeasurer.java new file mode 100644 index 000000000..9741f59c4 --- /dev/null +++ b/awt/java/awt/font/TextMeasurer.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/* + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package java.awt.font; + +import java.text.AttributedCharacterIterator; + +import org.apache.harmony.awt.gl.font.TextMetricsCalculator; +import org.apache.harmony.awt.gl.font.TextRunBreaker; + +/** + * The TextMeasurer class provides utilities for line break operations. + * + * @since Android 1.0 + */ +public final class TextMeasurer implements Cloneable { + + /** + * The aci. + */ + AttributedCharacterIterator aci; + + /** + * The frc. + */ + FontRenderContext frc; + + /** + * The breaker. + */ + TextRunBreaker breaker = null; + + /** + * The tmc. + */ + TextMetricsCalculator tmc = null; + + /** + * Instantiates a new text measurer from the specified text. + * + * @param text + * the source text. + * @param frc + * the FontRenderContext. + */ + public TextMeasurer(AttributedCharacterIterator text, FontRenderContext frc) { + this.aci = text; + this.frc = frc; + breaker = new TextRunBreaker(aci, this.frc); + tmc = new TextMetricsCalculator(breaker); + } + + /** + * Replaces the current text with the new text, inserting a break character + * at the specified insert position. + * + * @param newParagraph + * the new paragraph text. + * @param insertPos + * the position in the text where the character is inserted. + */ + public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) { + AttributedCharacterIterator oldAci = aci; + aci = newParagraph; + if ((oldAci.getEndIndex() - oldAci.getBeginIndex()) + - (aci.getEndIndex() - aci.getBeginIndex()) != -1) { + breaker = new TextRunBreaker(aci, this.frc); + tmc = new TextMetricsCalculator(breaker); + } else { + breaker.insertChar(newParagraph, insertPos); + } + } + + /** + * Replaces the current text with the new text and deletes a character at + * the specified position. + * + * @param newParagraph + * the paragraph text after deletion. + * @param deletePos + * the position in the text where the character is removed. + */ + public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) { + AttributedCharacterIterator oldAci = aci; + aci = newParagraph; + if ((oldAci.getEndIndex() - oldAci.getBeginIndex()) + - (aci.getEndIndex() - aci.getBeginIndex()) != 1) { + breaker = new TextRunBreaker(aci, this.frc); + tmc = new TextMetricsCalculator(breaker); + } else { + breaker.deleteChar(newParagraph, deletePos); + } + } + + /** + * Returns a copy of this object. + * + * @return a copy of this object. + */ + @Override + protected Object clone() { + return new TextMeasurer((AttributedCharacterIterator)aci.clone(), frc); + } + + /** + * Returns a TextLayout of the specified character range. + * + * @param start + * the index of the first character. + * @param limit + * the index after the last character. + * @return a TextLayout for the characters beginning at "start" up to "end". + */ + public TextLayout getLayout(int start, int limit) { + breaker.pushSegments(start - aci.getBeginIndex(), limit - aci.getBeginIndex()); + + breaker.createAllSegments(); + TextLayout layout = new TextLayout((TextRunBreaker)breaker.clone()); + + breaker.popSegments(); + return layout; + } + + /** + * Returns the graphical width of a line beginning at "start" parameter and + * including characters up to "end" parameter. "start" and "end" are + * absolute indices, not relative to the "start" of the paragraph. + * + * @param start + * the character index at which to start measuring. + * @param end + * the character index at which to stop measuring. + * @return the graphical width of a line beginning at "start" and including + * characters up to "end". + */ + public float getAdvanceBetween(int start, int end) { + breaker.pushSegments(start - aci.getBeginIndex(), end - aci.getBeginIndex()); + + breaker.createAllSegments(); + float retval = tmc.createMetrics().getAdvance(); + + breaker.popSegments(); + return retval; + } + + /** + * Returns the index of the first character which is not fit on a line + * beginning at start and possible measuring up to maxAdvance in graphical + * width. + * + * @param start + * he character index at which to start measuring. + * @param maxAdvance + * the graphical width in which the line must fit. + * @return the index after the last character that is fit on a line + * beginning at start, which is not longer than maxAdvance in + * graphical width. + */ + public int getLineBreakIndex(int start, float maxAdvance) { + breaker.createAllSegments(); + return breaker.getLineBreakIndex(start - aci.getBeginIndex(), maxAdvance) + + aci.getBeginIndex(); + } +} diff --git a/awt/java/awt/font/TransformAttribute.java b/awt/java/awt/font/TransformAttribute.java new file mode 100644 index 000000000..ff2caa251 --- /dev/null +++ b/awt/java/awt/font/TransformAttribute.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package java.awt.font; + +import java.awt.geom.AffineTransform; +import java.io.Serializable; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The TransformAttribute class is a wrapper for the AffineTransform class in + * order to use it as attribute. + * + * @since Android 1.0 + */ +public final class TransformAttribute implements Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 3356247357827709530L; + + // affine transform of this TransformAttribute instance + /** + * The transform. + */ + private AffineTransform fTransform; + + /** + * Instantiates a new TransformAttribute from the specified AffineTransform. + * + * @param transform + * the AffineTransform to be wrapped. + */ + public TransformAttribute(AffineTransform transform) { + if (transform == null) { + // awt.94=transform can not be null + throw new IllegalArgumentException(Messages.getString("awt.94")); //$NON-NLS-1$ + } + if (!transform.isIdentity()) { + this.fTransform = new AffineTransform(transform); + } + } + + /** + * Gets the initial AffineTransform which is wrapped. + * + * @return the initial AffineTransform which is wrapped. + */ + public AffineTransform getTransform() { + if (fTransform != null) { + return new AffineTransform(fTransform); + } + return new AffineTransform(); + } + + /** + * Checks if this transform is an identity transform. + * + * @return true, if this transform is an identity transform, false + * otherwise. + */ + public boolean isIdentity() { + return (fTransform == null); + } + +} diff --git a/awt/java/awt/font/package.html b/awt/java/awt/font/package.html new file mode 100644 index 000000000..788dcc0a0 --- /dev/null +++ b/awt/java/awt/font/package.html @@ -0,0 +1,8 @@ + + +

+ This package contains classes to support the representation of different types of fonts for example TrueType fonts. +

+ @since Android 1.0 + + diff --git a/awt/java/awt/geom/AffineTransform.java b/awt/java/awt/geom/AffineTransform.java new file mode 100644 index 000000000..8a6938cf0 --- /dev/null +++ b/awt/java/awt/geom/AffineTransform.java @@ -0,0 +1,1267 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.awt.Shape; +import java.io.IOException; +import java.io.Serializable; + +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.misc.HashCode; + +/** + * The Class AffineTransform represents a linear transformation (rotation, + * scaling, or shear) followed by a translation that acts on a coordinate space. + * It preserves collinearity of points and ratios of distances between collinear + * points: so if A, B, and C are on a line, then after the space has been + * transformed via the affine transform, the images of the three points will + * still be on a line, and the ratio of the distance from A to B with the + * distance from B to C will be the same as the corresponding ratio in the image + * space. + * + * @since Android 1.0 + */ +public class AffineTransform implements Cloneable, Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 1330973210523860834L; + + /** + * The Constant TYPE_IDENTITY. + */ + public static final int TYPE_IDENTITY = 0; + + /** + * The Constant TYPE_TRANSLATION. + */ + public static final int TYPE_TRANSLATION = 1; + + /** + * The Constant TYPE_UNIFORM_SCALE. + */ + public static final int TYPE_UNIFORM_SCALE = 2; + + /** + * The Constant TYPE_GENERAL_SCALE. + */ + public static final int TYPE_GENERAL_SCALE = 4; + + /** + * The Constant TYPE_QUADRANT_ROTATION. + */ + public static final int TYPE_QUADRANT_ROTATION = 8; + + /** + * The Constant TYPE_GENERAL_ROTATION. + */ + public static final int TYPE_GENERAL_ROTATION = 16; + + /** + * The Constant TYPE_GENERAL_TRANSFORM. + */ + public static final int TYPE_GENERAL_TRANSFORM = 32; + + /** + * The Constant TYPE_FLIP. + */ + public static final int TYPE_FLIP = 64; + + /** + * The Constant TYPE_MASK_SCALE. + */ + public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE; + + /** + * The Constant TYPE_MASK_ROTATION. + */ + public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION; + + /** + * The TYPE_UNKNOWN is an initial type value. + */ + static final int TYPE_UNKNOWN = -1; + + /** + * The min value equivalent to zero. If absolute value less then ZERO it + * considered as zero. + */ + static final double ZERO = 1E-10; + + /** + * The values of transformation matrix. + */ + double m00; + + /** + * The m10. + */ + double m10; + + /** + * The m01. + */ + double m01; + + /** + * The m11. + */ + double m11; + + /** + * The m02. + */ + double m02; + + /** + * The m12. + */ + double m12; + + /** + * The transformation type. + */ + transient int type; + + /** + * Instantiates a new affine transform of type TYPE_IDENTITY + * (which leaves coordinates unchanged). + */ + public AffineTransform() { + type = TYPE_IDENTITY; + m00 = m11 = 1.0; + m10 = m01 = m02 = m12 = 0.0; + } + + /** + * Instantiates a new affine transform that has the same data as the given + * AffineTransform. + * + * @param t + * the transform to copy. + */ + public AffineTransform(AffineTransform t) { + this.type = t.type; + this.m00 = t.m00; + this.m10 = t.m10; + this.m01 = t.m01; + this.m11 = t.m11; + this.m02 = t.m02; + this.m12 = t.m12; + } + + /** + * Instantiates a new affine transform by specifying the values of the 2x3 + * transformation matrix as floats. The type is set to the default type: + * TYPE_UNKNOWN + * + * @param m00 + * the m00 entry in the transformation matrix. + * @param m10 + * the m10 entry in the transformation matrix. + * @param m01 + * the m01 entry in the transformation matrix. + * @param m11 + * the m11 entry in the transformation matrix. + * @param m02 + * the m02 entry in the transformation matrix. + * @param m12 + * the m12 entry in the transformation matrix. + */ + public AffineTransform(float m00, float m10, float m01, float m11, float m02, float m12) { + this.type = TYPE_UNKNOWN; + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + } + + /** + * Instantiates a new affine transform by specifying the values of the 2x3 + * transformation matrix as doubles. The type is set to the default type: + * TYPE_UNKNOWN + * + * @param m00 + * the m00 entry in the transformation matrix. + * @param m10 + * the m10 entry in the transformation matrix. + * @param m01 + * the m01 entry in the transformation matrix. + * @param m11 + * the m11 entry in the transformation matrix. + * @param m02 + * the m02 entry in the transformation matrix. + * @param m12 + * the m12 entry in the transformation matrix. + */ + public AffineTransform(double m00, double m10, double m01, double m11, double m02, double m12) { + this.type = TYPE_UNKNOWN; + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + } + + /** + * Instantiates a new affine transform by reading the values of the + * transformation matrix from an array of floats. The mapping from the array + * to the matrix starts with matrix[0] giving the top-left + * entry of the matrix and proceeds with the usual left-to-right and + * top-down ordering. + *

+ * If the array has only four entries, then the two entries of the last row + * of the transformation matrix default to zero. + * + * @param matrix + * the array of four or six floats giving the values of the + * matrix. + * @throws ArrayIndexOutOfBoundsException + * if the size of the array is 0, 1, 2, 3, or 5. + */ + public AffineTransform(float[] matrix) { + this.type = TYPE_UNKNOWN; + m00 = matrix[0]; + m10 = matrix[1]; + m01 = matrix[2]; + m11 = matrix[3]; + if (matrix.length > 4) { + m02 = matrix[4]; + m12 = matrix[5]; + } + } + + /** + * Instantiates a new affine transform by reading the values of the + * transformation matrix from an array of doubles. The mapping from the + * array to the matrix starts with matrix[0] giving the + * top-left entry of the matrix and proceeds with the usual left-to-right + * and top-down ordering. + *

+ * If the array has only four entries, then the two entries of the last row + * of the transformation matrix default to zero. + * + * @param matrix + * the array of four or six doubles giving the values of the + * matrix. + * @throws ArrayIndexOutOfBoundsException + * if the size of the array is 0, 1, 2, 3, or 5. + */ + public AffineTransform(double[] matrix) { + this.type = TYPE_UNKNOWN; + m00 = matrix[0]; + m10 = matrix[1]; + m01 = matrix[2]; + m11 = matrix[3]; + if (matrix.length > 4) { + m02 = matrix[4]; + m12 = matrix[5]; + } + } + + /** + * Returns type of the affine transformation. + *

+ * The type is computed as follows: Label the entries of the transformation + * matrix as three rows (m00, m01), (m10, m11), and (m02, m12). Then if the + * original basis vectors are (1, 0) and (0, 1), the new basis vectors after + * transformation are given by (m00, m01) and (m10, m11), and the + * translation vector is (m02, m12). + *

+ * The types are classified as follows:
TYPE_IDENTITY - no change
+ * TYPE_TRANSLATION - The translation vector isn't zero
+ * TYPE_UNIFORM_SCALE - The new basis vectors have equal length
+ * TYPE_GENERAL_SCALE - The new basis vectors dont' have equal length
+ * TYPE_FLIP - The new basis vector orientation differs from the original + * one
TYPE_QUADRANT_ROTATION - The new basis is a rotation of the + * original by 90, 180, 270, or 360 degrees
TYPE_GENERAL_ROTATION - The + * new basis is a rotation of the original by an arbitrary angle
+ * TYPE_GENERAL_TRANSFORM - The transformation can't be inverted.
+ *

+ * Note that multiple types are possible, thus the types can be combined + * using bitwise combinations. + * + * @return the type of the Affine Transform. + */ + public int getType() { + if (type != TYPE_UNKNOWN) { + return type; + } + + int type = 0; + + if (m00 * m01 + m10 * m11 != 0.0) { + type |= TYPE_GENERAL_TRANSFORM; + return type; + } + + if (m02 != 0.0 || m12 != 0.0) { + type |= TYPE_TRANSLATION; + } else if (m00 == 1.0 && m11 == 1.0 && m01 == 0.0 && m10 == 0.0) { + type = TYPE_IDENTITY; + return type; + } + + if (m00 * m11 - m01 * m10 < 0.0) { + type |= TYPE_FLIP; + } + + double dx = m00 * m00 + m10 * m10; + double dy = m01 * m01 + m11 * m11; + if (dx != dy) { + type |= TYPE_GENERAL_SCALE; + } else if (dx != 1.0) { + type |= TYPE_UNIFORM_SCALE; + } + + if ((m00 == 0.0 && m11 == 0.0) || (m10 == 0.0 && m01 == 0.0 && (m00 < 0.0 || m11 < 0.0))) { + type |= TYPE_QUADRANT_ROTATION; + } else if (m01 != 0.0 || m10 != 0.0) { + type |= TYPE_GENERAL_ROTATION; + } + + return type; + } + + /** + * Gets the scale x entry of the transformation matrix (the upper left + * matrix entry). + * + * @return the scale x value. + */ + public double getScaleX() { + return m00; + } + + /** + * Gets the scale y entry of the transformation matrix (the lower right + * entry of the linear transformation). + * + * @return the scale y value. + */ + public double getScaleY() { + return m11; + } + + /** + * Gets the shear x entry of the transformation matrix (the upper right + * entry of the linear transformation). + * + * @return the shear x value. + */ + public double getShearX() { + return m01; + } + + /** + * Gets the shear y entry of the transformation matrix (the lower left entry + * of the linear transformation). + * + * @return the shear y value. + */ + public double getShearY() { + return m10; + } + + /** + * Gets the x coordinate of the translation vector. + * + * @return the x coordinate of the translation vector. + */ + public double getTranslateX() { + return m02; + } + + /** + * Gets the y coordinate of the translation vector. + * + * @return the y coordinate of the translation vector. + */ + public double getTranslateY() { + return m12; + } + + /** + * Checks if the AffineTransformation is the identity. + * + * @return true, if the AffineTransformation is the identity. + */ + public boolean isIdentity() { + return getType() == TYPE_IDENTITY; + } + + /** + * Writes the values of the transformation matrix into the given array of + * doubles. If the array has length 4, only the linear transformation part + * will be written into it. If it has length greater than 4, the translation + * vector will be included as well. + * + * @param matrix + * the array to fill with the values of the matrix. + * @throws ArrayIndexOutOfBoundsException + * if the size of the array is 0, 1, 2, 3, or 5. + */ + public void getMatrix(double[] matrix) { + matrix[0] = m00; + matrix[1] = m10; + matrix[2] = m01; + matrix[3] = m11; + if (matrix.length > 4) { + matrix[4] = m02; + matrix[5] = m12; + } + } + + /** + * Gets the determinant of the linear transformation matrix. + * + * @return the determinant of the linear transformation matrix. + */ + public double getDeterminant() { + return m00 * m11 - m01 * m10; + } + + /** + * Sets the transform in terms of a list of double values. + * + * @param m00 + * the m00 coordinate of the transformation matrix. + * @param m10 + * the m10 coordinate of the transformation matrix. + * @param m01 + * the m01 coordinate of the transformation matrix. + * @param m11 + * the m11 coordinate of the transformation matrix. + * @param m02 + * the m02 coordinate of the transformation matrix. + * @param m12 + * the m12 coordinate of the transformation matrix. + */ + public void setTransform(double m00, double m10, double m01, double m11, double m02, double m12) { + this.type = TYPE_UNKNOWN; + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + } + + /** + * Sets the transform's data to match the data of the transform sent as a + * parameter. + * + * @param t + * the transform that gives the new values. + */ + public void setTransform(AffineTransform t) { + type = t.type; + setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12); + } + + /** + * Sets the transform to the identity transform. + */ + public void setToIdentity() { + type = TYPE_IDENTITY; + m00 = m11 = 1.0; + m10 = m01 = m02 = m12 = 0.0; + } + + /** + * Sets the transformation to a translation alone. Sets the linear part of + * the transformation to identity and the translation vector to the values + * sent as parameters. Sets the type to TYPE_IDENTITY if the + * resulting AffineTransformation is the identity transformation, otherwise + * sets it to TYPE_TRANSLATION. + * + * @param mx + * the distance to translate in the x direction. + * @param my + * the distance to translate in the y direction. + */ + public void setToTranslation(double mx, double my) { + m00 = m11 = 1.0; + m01 = m10 = 0.0; + m02 = mx; + m12 = my; + if (mx == 0.0 && my == 0.0) { + type = TYPE_IDENTITY; + } else { + type = TYPE_TRANSLATION; + } + } + + /** + * Sets the transformation to being a scale alone, eliminating rotation, + * shear, and translation elements. Sets the type to + * TYPE_IDENTITY if the resulting AffineTransformation is the + * identity transformation, otherwise sets it to TYPE_UNKNOWN. + * + * @param scx + * the scaling factor in the x direction. + * @param scy + * the scaling factor in the y direction. + */ + public void setToScale(double scx, double scy) { + m00 = scx; + m11 = scy; + m10 = m01 = m02 = m12 = 0.0; + if (scx != 1.0 || scy != 1.0) { + type = TYPE_UNKNOWN; + } else { + type = TYPE_IDENTITY; + } + } + + /** + * Sets the transformation to being a shear alone, eliminating rotation, + * scaling, and translation elements. Sets the type to + * TYPE_IDENTITY if the resulting AffineTransformation is the + * identity transformation, otherwise sets it to TYPE_UNKNOWN. + * + * @param shx + * the shearing factor in the x direction. + * @param shy + * the shearing factor in the y direction. + */ + public void setToShear(double shx, double shy) { + m00 = m11 = 1.0; + m02 = m12 = 0.0; + m01 = shx; + m10 = shy; + if (shx != 0.0 || shy != 0.0) { + type = TYPE_UNKNOWN; + } else { + type = TYPE_IDENTITY; + } + } + + /** + * Sets the transformation to being a rotation alone, eliminating shearing, + * scaling, and translation elements. Sets the type to + * TYPE_IDENTITY if the resulting AffineTransformation is the + * identity transformation, otherwise sets it to TYPE_UNKNOWN. + * + * @param angle + * the angle of rotation in radians. + */ + public void setToRotation(double angle) { + double sin = Math.sin(angle); + double cos = Math.cos(angle); + if (Math.abs(cos) < ZERO) { + cos = 0.0; + sin = sin > 0.0 ? 1.0 : -1.0; + } else if (Math.abs(sin) < ZERO) { + sin = 0.0; + cos = cos > 0.0 ? 1.0 : -1.0; + } + m00 = m11 = cos; + m01 = -sin; + m10 = sin; + m02 = m12 = 0.0; + type = TYPE_UNKNOWN; + } + + /** + * Sets the transformation to being a rotation followed by a translation. + * Sets the type to TYPE_UNKNOWN. + * + * @param angle + * the angle of rotation in radians. + * @param px + * the distance to translate in the x direction. + * @param py + * the distance to translate in the y direction. + */ + public void setToRotation(double angle, double px, double py) { + setToRotation(angle); + m02 = px * (1.0 - m00) + py * m10; + m12 = py * (1.0 - m00) - px * m10; + type = TYPE_UNKNOWN; + } + + /** + * Creates a new AffineTransformation that is a translation alone with the + * translation vector given by the values sent as parameters. The new + * transformation's type is TYPE_IDENTITY if the + * AffineTransformation is the identity transformation, otherwise it's + * TYPE_TRANSLATION. + * + * @param mx + * the distance to translate in the x direction. + * @param my + * the distance to translate in the y direction. + * @return the new AffineTransformation. + */ + public static AffineTransform getTranslateInstance(double mx, double my) { + AffineTransform t = new AffineTransform(); + t.setToTranslation(mx, my); + return t; + } + + /** + * Creates a new AffineTransformation that is a scale alone. The new + * transformation's type is TYPE_IDENTITY if the + * AffineTransformation is the identity transformation, otherwise it's + * TYPE_UNKNOWN. + * + * @param scx + * the scaling factor in the x direction. + * @param scY + * the scaling factor in the y direction. + * @return the new AffineTransformation. + */ + public static AffineTransform getScaleInstance(double scx, double scY) { + AffineTransform t = new AffineTransform(); + t.setToScale(scx, scY); + return t; + } + + /** + * Creates a new AffineTransformation that is a shear alone. The new + * transformation's type is TYPE_IDENTITY if the + * AffineTransformation is the identity transformation, otherwise it's + * TYPE_UNKNOWN. + * + * @param shx + * the shearing factor in the x direction. + * @param shy + * the shearing factor in the y direction. + * @return the new AffineTransformation. + */ + public static AffineTransform getShearInstance(double shx, double shy) { + AffineTransform m = new AffineTransform(); + m.setToShear(shx, shy); + return m; + } + + /** + * Creates a new AffineTransformation that is a rotation alone. The new + * transformation's type is TYPE_IDENTITY if the + * AffineTransformation is the identity transformation, otherwise it's + * TYPE_UNKNOWN. + * + * @param angle + * the angle of rotation in radians. + * @return the new AffineTransformation. + */ + public static AffineTransform getRotateInstance(double angle) { + AffineTransform t = new AffineTransform(); + t.setToRotation(angle); + return t; + } + + /** + * Creates a new AffineTransformation that is a rotation followed by a + * translation. Sets the type to TYPE_UNKNOWN. + * + * @param angle + * the angle of rotation in radians. + * @param x + * the distance to translate in the x direction. + * @param y + * the distance to translate in the y direction. + * @return the new AffineTransformation. + */ + public static AffineTransform getRotateInstance(double angle, double x, double y) { + AffineTransform t = new AffineTransform(); + t.setToRotation(angle, x, y); + return t; + } + + /** + * Applies a translation to this AffineTransformation. + * + * @param mx + * the distance to translate in the x direction. + * @param my + * the distance to translate in the y direction. + */ + public void translate(double mx, double my) { + concatenate(AffineTransform.getTranslateInstance(mx, my)); + } + + /** + * Applies a scaling transformation to this AffineTransformation. + * + * @param scx + * the scaling factor in the x direction. + * @param scy + * the scaling factor in the y direction. + */ + public void scale(double scx, double scy) { + concatenate(AffineTransform.getScaleInstance(scx, scy)); + } + + /** + * Applies a shearing transformation to this AffineTransformation. + * + * @param shx + * the shearing factor in the x direction. + * @param shy + * the shearing factor in the y direction. + */ + public void shear(double shx, double shy) { + concatenate(AffineTransform.getShearInstance(shx, shy)); + } + + /** + * Applies a rotation transformation to this AffineTransformation. + * + * @param angle + * the angle of rotation in radians. + */ + public void rotate(double angle) { + concatenate(AffineTransform.getRotateInstance(angle)); + } + + /** + * Applies a rotation and translation transformation to this + * AffineTransformation. + * + * @param angle + * the angle of rotation in radians. + * @param px + * the distance to translate in the x direction. + * @param py + * the distance to translate in the y direction. + */ + public void rotate(double angle, double px, double py) { + concatenate(AffineTransform.getRotateInstance(angle, px, py)); + } + + /** + * Multiplies the matrix representations of two AffineTransform objects. + * + * @param t1 + * - the AffineTransform object is a multiplicand + * @param t2 + * - the AffineTransform object is a multiplier + * @return an AffineTransform object that is the result of t1 multiplied by + * the matrix t2. + */ + AffineTransform multiply(AffineTransform t1, AffineTransform t2) { + return new AffineTransform(t1.m00 * t2.m00 + t1.m10 * t2.m01, // m00 + t1.m00 * t2.m10 + t1.m10 * t2.m11, // m01 + t1.m01 * t2.m00 + t1.m11 * t2.m01, // m10 + t1.m01 * t2.m10 + t1.m11 * t2.m11, // m11 + t1.m02 * t2.m00 + t1.m12 * t2.m01 + t2.m02, // m02 + t1.m02 * t2.m10 + t1.m12 * t2.m11 + t2.m12);// m12 + } + + /** + * Applies the given AffineTransform to this AffineTransform via matrix + * multiplication. + * + * @param t + * the AffineTransform to apply to this AffineTransform. + */ + public void concatenate(AffineTransform t) { + setTransform(multiply(t, this)); + } + + /** + * Changes the current AffineTransform the one obtained by taking the + * transform t and applying this AffineTransform to it. + * + * @param t + * the AffineTransform that this AffineTransform is multiplied + * by. + */ + public void preConcatenate(AffineTransform t) { + setTransform(multiply(this, t)); + } + + /** + * Creates an AffineTransform that is the inverse of this transform. + * + * @return the affine transform that is the inverse of this AffineTransform. + * @throws NoninvertibleTransformException + * if this AffineTransform cannot be inverted (the determinant + * of the linear transformation part is zero). + */ + public AffineTransform createInverse() throws NoninvertibleTransformException { + double det = getDeterminant(); + if (Math.abs(det) < ZERO) { + // awt.204=Determinant is zero + throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$ + } + return new AffineTransform(m11 / det, // m00 + -m10 / det, // m10 + -m01 / det, // m01 + m00 / det, // m11 + (m01 * m12 - m11 * m02) / det, // m02 + (m10 * m02 - m00 * m12) / det // m12 + ); + } + + /** + * Apply the current AffineTransform to the point. + * + * @param src + * the original point. + * @param dst + * Point2D object to be filled with the destination coordinates + * (where the original point is sent by this AffineTransform). + * May be null. + * @return the point in the AffineTransform's image space where the original + * point is sent. + */ + public Point2D transform(Point2D src, Point2D dst) { + if (dst == null) { + if (src instanceof Point2D.Double) { + dst = new Point2D.Double(); + } else { + dst = new Point2D.Float(); + } + } + + double x = src.getX(); + double y = src.getY(); + + dst.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12); + return dst; + } + + /** + * Applies this AffineTransform to an array of points. + * + * @param src + * the array of points to be transformed. + * @param srcOff + * the offset in the source point array of the first point to be + * transformed. + * @param dst + * the point array where the images of the points (after applying + * the AffineTransformation) should be placed. + * @param dstOff + * the offset in the destination array where the new values + * should be written. + * @param length + * the number of points to transform. + * @throws ArrayIndexOutOfBoundsException + * if srcOff + length > src.length or + * dstOff + length > dst.length. + */ + public void transform(Point2D[] src, int srcOff, Point2D[] dst, int dstOff, int length) { + while (--length >= 0) { + Point2D srcPoint = src[srcOff++]; + double x = srcPoint.getX(); + double y = srcPoint.getY(); + Point2D dstPoint = dst[dstOff]; + if (dstPoint == null) { + if (srcPoint instanceof Point2D.Double) { + dstPoint = new Point2D.Double(); + } else { + dstPoint = new Point2D.Float(); + } + } + dstPoint.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12); + dst[dstOff++] = dstPoint; + } + } + + /** + * Applies this AffineTransform to a set of points given as an array of + * double values where every two values in the array give the coordinates of + * a point; the even-indexed values giving the x coordinates and the + * odd-indexed values giving the y coordinates. + * + * @param src + * the array of points to be transformed. + * @param srcOff + * the offset in the source point array of the first point to be + * transformed. + * @param dst + * the point array where the images of the points (after applying + * the AffineTransformation) should be placed. + * @param dstOff + * the offset in the destination array where the new values + * should be written. + * @param length + * the number of points to transform. + * @throws ArrayIndexOutOfBoundsException + * if srcOff + length*2 > src.length or + * dstOff + length*2 > dst.length. + */ + public void transform(double[] src, int srcOff, double[] dst, int dstOff, int length) { + int step = 2; + if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) { + srcOff = srcOff + length * 2 - 2; + dstOff = dstOff + length * 2 - 2; + step = -2; + } + while (--length >= 0) { + double x = src[srcOff + 0]; + double y = src[srcOff + 1]; + dst[dstOff + 0] = x * m00 + y * m01 + m02; + dst[dstOff + 1] = x * m10 + y * m11 + m12; + srcOff += step; + dstOff += step; + } + } + + /** + * Applies this AffineTransform to a set of points given as an array of + * float values where every two values in the array give the coordinates of + * a point; the even-indexed values giving the x coordinates and the + * odd-indexed values giving the y coordinates. + * + * @param src + * the array of points to be transformed. + * @param srcOff + * the offset in the source point array of the first point to be + * transformed. + * @param dst + * the point array where the images of the points (after applying + * the AffineTransformation) should be placed. + * @param dstOff + * the offset in the destination array where the new values + * should be written. + * @param length + * the number of points to transform. + * @throws ArrayIndexOutOfBoundsException + * if srcOff + length*2 > src.length or + * dstOff + length*2 > dst.length. + */ + public void transform(float[] src, int srcOff, float[] dst, int dstOff, int length) { + int step = 2; + if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) { + srcOff = srcOff + length * 2 - 2; + dstOff = dstOff + length * 2 - 2; + step = -2; + } + while (--length >= 0) { + float x = src[srcOff + 0]; + float y = src[srcOff + 1]; + dst[dstOff + 0] = (float)(x * m00 + y * m01 + m02); + dst[dstOff + 1] = (float)(x * m10 + y * m11 + m12); + srcOff += step; + dstOff += step; + } + } + + /** + * Applies this AffineTransform to a set of points given as an array of + * float values where every two values in the array give the coordinates of + * a point; the even-indexed values giving the x coordinates and the + * odd-indexed values giving the y coordinates. The destination coordinates + * are given as values of type double. + * + * @param src + * the array of points to be transformed. + * @param srcOff + * the offset in the source point array of the first point to be + * transformed. + * @param dst + * the point array where the images of the points (after applying + * the AffineTransformation) should be placed. + * @param dstOff + * the offset in the destination array where the new values + * should be written. + * @param length + * the number of points to transform. + * @throws ArrayIndexOutOfBoundsException + * if srcOff + length*2 > src.length or + * dstOff + length*2 > dst.length. + */ + public void transform(float[] src, int srcOff, double[] dst, int dstOff, int length) { + while (--length >= 0) { + float x = src[srcOff++]; + float y = src[srcOff++]; + dst[dstOff++] = x * m00 + y * m01 + m02; + dst[dstOff++] = x * m10 + y * m11 + m12; + } + } + + /** + * Applies this AffineTransform to a set of points given as an array of + * double values where every two values in the array give the coordinates of + * a point; the even-indexed values giving the x coordinates and the + * odd-indexed values giving the y coordinates. The destination coordinates + * are given as values of type float. + * + * @param src + * the array of points to be transformed. + * @param srcOff + * the offset in the source point array of the first point to be + * transformed. + * @param dst + * the point array where the images of the points (after applying + * the AffineTransformation) should be placed. + * @param dstOff + * the offset in the destination array where the new values + * should be written. + * @param length + * the number of points to transform. + * @throws ArrayIndexOutOfBoundsException + * if srcOff + length*2 > src.length or + * dstOff + length*2 > dst.length. + */ + public void transform(double[] src, int srcOff, float[] dst, int dstOff, int length) { + while (--length >= 0) { + double x = src[srcOff++]; + double y = src[srcOff++]; + dst[dstOff++] = (float)(x * m00 + y * m01 + m02); + dst[dstOff++] = (float)(x * m10 + y * m11 + m12); + } + } + + /** + * Transforms the point according to the linear transformation part of this + * AffineTransformation (without applying the translation). + * + * @param src + * the original point. + * @param dst + * the point object where the result of the delta transform is + * written. + * @return the result of applying the delta transform (linear part only) to + * the original point. + */ + // TODO: is this right? if dst is null, we check what it's an + // instance of? Shouldn't it be src instanceof Point2D.Double? + public Point2D deltaTransform(Point2D src, Point2D dst) { + if (dst == null) { + if (dst instanceof Point2D.Double) { + dst = new Point2D.Double(); + } else { + dst = new Point2D.Float(); + } + } + + double x = src.getX(); + double y = src.getY(); + + dst.setLocation(x * m00 + y * m01, x * m10 + y * m11); + return dst; + } + + /** + * Applies the linear transformation part of this AffineTransform (ignoring + * the translation part) to a set of points given as an array of double + * values where every two values in the array give the coordinates of a + * point; the even-indexed values giving the x coordinates and the + * odd-indexed values giving the y coordinates. + * + * @param src + * the array of points to be transformed. + * @param srcOff + * the offset in the source point array of the first point to be + * transformed. + * @param dst + * the point array where the images of the points (after applying + * the delta transformation) should be placed. + * @param dstOff + * the offset in the destination array where the new values + * should be written. + * @param length + * the number of points to transform. + * @throws ArrayIndexOutOfBoundsException + * if srcOff + length*2 > src.length or + * dstOff + length*2 > dst.length. + */ + public void deltaTransform(double[] src, int srcOff, double[] dst, int dstOff, int length) { + while (--length >= 0) { + double x = src[srcOff++]; + double y = src[srcOff++]; + dst[dstOff++] = x * m00 + y * m01; + dst[dstOff++] = x * m10 + y * m11; + } + } + + /** + * Transforms the point according to the inverse of this + * AffineTransformation. + * + * @param src + * the original point. + * @param dst + * the point object where the result of the inverse transform is + * written (may be null). + * @return the result of applying the inverse transform. Inverse transform. + * @throws NoninvertibleTransformException + * if this AffineTransform cannot be inverted (the determinant + * of the linear transformation part is zero). + */ + public Point2D inverseTransform(Point2D src, Point2D dst) + throws NoninvertibleTransformException { + double det = getDeterminant(); + if (Math.abs(det) < ZERO) { + // awt.204=Determinant is zero + throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$ + } + + if (dst == null) { + if (src instanceof Point2D.Double) { + dst = new Point2D.Double(); + } else { + dst = new Point2D.Float(); + } + } + + double x = src.getX() - m02; + double y = src.getY() - m12; + + dst.setLocation((x * m11 - y * m01) / det, (y * m00 - x * m10) / det); + return dst; + } + + /** + * Applies the inverse of this AffineTransform to a set of points given as + * an array of double values where every two values in the array give the + * coordinates of a point; the even-indexed values giving the x coordinates + * and the odd-indexed values giving the y coordinates. + * + * @param src + * the array of points to be transformed. + * @param srcOff + * the offset in the source point array of the first point to be + * transformed. + * @param dst + * the point array where the images of the points (after applying + * the inverse of the AffineTransformation) should be placed. + * @param dstOff + * the offset in the destination array where the new values + * should be written. + * @param length + * the number of points to transform. + * @throws ArrayIndexOutOfBoundsException + * if srcOff + length*2 > src.length or + * dstOff + length*2 > dst.length. + * @throws NoninvertibleTransformException + * if this AffineTransform cannot be inverted (the determinant + * of the linear transformation part is zero). + */ + public void inverseTransform(double[] src, int srcOff, double[] dst, int dstOff, int length) + throws NoninvertibleTransformException { + double det = getDeterminant(); + if (Math.abs(det) < ZERO) { + // awt.204=Determinant is zero + throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$ + } + + while (--length >= 0) { + double x = src[srcOff++] - m02; + double y = src[srcOff++] - m12; + dst[dstOff++] = (x * m11 - y * m01) / det; + dst[dstOff++] = (y * m00 - x * m10) / det; + } + } + + /** + * Creates a new shape whose data is given by applying this AffineTransform + * to the specified shape. + * + * @param src + * the original shape whose data is to be transformed. + * @return the new shape found by applying this AffineTransform to the + * original shape. + */ + public Shape createTransformedShape(Shape src) { + if (src == null) { + return null; + } + if (src instanceof GeneralPath) { + return ((GeneralPath)src).createTransformedShape(this); + } + PathIterator path = src.getPathIterator(this); + GeneralPath dst = new GeneralPath(path.getWindingRule()); + dst.append(path, false); + return dst; + } + + @Override + public String toString() { + return getClass().getName() + "[[" + m00 + ", " + m01 + ", " + m02 + "], [" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + + m10 + ", " + m11 + ", " + m12 + "]]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } + + @Override + public int hashCode() { + HashCode hash = new HashCode(); + hash.append(m00); + hash.append(m01); + hash.append(m02); + hash.append(m10); + hash.append(m11); + hash.append(m12); + return hash.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof AffineTransform) { + AffineTransform t = (AffineTransform)obj; + return m00 == t.m00 && m01 == t.m01 && m02 == t.m02 && m10 == t.m10 && m11 == t.m11 + && m12 == t.m12; + } + return false; + } + + /** + * Writes the AffineTrassform object to the output steam. + * + * @param stream + * - the output stream. + * @throws IOException + * - if there are I/O errors while writing to the output stream. + */ + private void writeObject(java.io.ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + } + + /** + * Read the AffineTransform object from the input stream. + * + * @param stream + * - the input stream. + * @throws IOException + * - if there are I/O errors while reading from the input + * stream. + * @throws ClassNotFoundException + * - if class could not be found. + */ + private void readObject(java.io.ObjectInputStream stream) throws IOException, + ClassNotFoundException { + stream.defaultReadObject(); + type = TYPE_UNKNOWN; + } + +} diff --git a/awt/java/awt/geom/Arc2D.java b/awt/java/awt/geom/Arc2D.java new file mode 100644 index 000000000..56f5cd392 --- /dev/null +++ b/awt/java/awt/geom/Arc2D.java @@ -0,0 +1,1157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class Arc2D represents a segment of a curve inscribed in a rectangle. The + * curve is defined by a start angle and an extent angle (the end angle minus + * the start angle) as a pie wedge whose point is in the center of the + * rectangle. The Arc2D as a shape may be either OPEN (including nothing but the + * curved arc segment itself), CHORD (the curved arc segment closed by a + * connecting segment from the end to the beginning of the arc, or PIE (the + * segments from the end of the arc to the center of the rectangle and from the + * center of the rectangle back to the arc's start point are included). + * + * @since Android 1.0 + */ +public abstract class Arc2D extends RectangularShape { + + /** + * The arc type OPEN indicates that the shape includes only the curved arc + * segment. + */ + public final static int OPEN = 0; + + /** + * The arc type CHORD indicates that as a shape the connecting segment from + * the end point of the curved arc to the beginning point is included. + */ + public final static int CHORD = 1; + + /** + * The arc type PIE indicates that as a shape the two segments from the + * arc's endpoint to the center of the rectangle and from the center of the + * rectangle to the arc's endpoint are included. + */ + public final static int PIE = 2; + + /** + * The Class Float is a subclass of Arc2D in which all of the data values + * are given as floats. + * + * @see Arc2D.Double + * @since Android 1.0 + */ + public static class Float extends Arc2D { + + /** + * The x coordinate of the upper left corner of the rectangle that + * contains the arc. + */ + public float x; + + /** + * The y coordinate of the upper left corner of the rectangle that + * contains the arc. + */ + public float y; + + /** + * The width of the rectangle that contains the arc. + */ + public float width; + + /** + * The height of the rectangle that contains the arc. + */ + public float height; + + /** + * The start angle of the arc in degrees. + */ + public float start; + + /** + * The width angle of the arc in degrees. + */ + public float extent; + + /** + * Instantiates a new Arc2D of type OPEN with float values. + */ + public Float() { + super(OPEN); + } + + /** + * Instantiates a new Arc2D of the specified type with float values. + * + * @param type + * the type of the new Arc2D, either {@link Arc2D#OPEN}, + * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}. + */ + public Float(int type) { + super(type); + } + + /** + * Instantiates a Arc2D with the specified float-valued data. + * + * @param x + * the x coordinate of the upper left corner of the rectangle + * that contains the arc. + * @param y + * the y coordinate of the upper left corner of the rectangle + * that contains the arc. + * @param width + * the width of the rectangle that contains the arc. + * @param height + * the height of the rectangle that contains the arc. + * @param start + * the start angle of the arc in degrees. + * @param extent + * the width angle of the arc in degrees. + * @param type + * the type of the new Arc2D, either {@link Arc2D#OPEN}, + * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}. + */ + public Float(float x, float y, float width, float height, float start, float extent, + int type) { + super(type); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.start = start; + this.extent = extent; + } + + /** + * Instantiates a new Angle2D with the specified float-valued data and + * the bounding rectangle given by the parameter bounds. + * + * @param bounds + * the bounding rectangle of the Angle2D. + * @param start + * the start angle of the arc in degrees. + * @param extent + * the width angle of the arc in degrees. + * @param type + * the type of the new Arc2D, either {@link Arc2D#OPEN}, + * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}. + */ + public Float(Rectangle2D bounds, float start, float extent, int type) { + super(type); + this.x = (float)bounds.getX(); + this.y = (float)bounds.getY(); + this.width = (float)bounds.getWidth(); + this.height = (float)bounds.getHeight(); + this.start = start; + this.extent = extent; + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public double getWidth() { + return width; + } + + @Override + public double getHeight() { + return height; + } + + @Override + public double getAngleStart() { + return start; + } + + @Override + public double getAngleExtent() { + return extent; + } + + @Override + public boolean isEmpty() { + return width <= 0.0f || height <= 0.0f; + } + + @Override + public void setArc(double x, double y, double width, double height, double start, + double extent, int type) { + this.setArcType(type); + this.x = (float)x; + this.y = (float)y; + this.width = (float)width; + this.height = (float)height; + this.start = (float)start; + this.extent = (float)extent; + } + + @Override + public void setAngleStart(double start) { + this.start = (float)start; + } + + @Override + public void setAngleExtent(double extent) { + this.extent = (float)extent; + } + + @Override + protected Rectangle2D makeBounds(double x, double y, double width, double height) { + return new Rectangle2D.Float((float)x, (float)y, (float)width, (float)height); + } + + } + + /** + * The Class Double is a subclass of Arc2D in which all of the data values + * are given as doubles. + * + * @see Arc2D.Float + * @since Android 1.0 + */ + public static class Double extends Arc2D { + + /** + * The x coordinate of the upper left corner of the rectangle that + * contains the arc. + */ + public double x; + + /** + * The y coordinate of the upper left corner of the rectangle that + * contains the arc. + */ + public double y; + + /** + * The width of the rectangle that contains the arc. + */ + public double width; + + /** + * The height of the rectangle that contains the arc. + */ + public double height; + + /** + * The start angle of the arc in degrees. + */ + public double start; + + /** + * The width angle of the arc in degrees. + */ + public double extent; + + /** + * Instantiates a new Arc2D of type OPEN with double values. + */ + public Double() { + super(OPEN); + } + + /** + * Instantiates a new Arc2D of the specified type with double values. + * + * @param type + * the type of the new Arc2D, either {@link Arc2D#OPEN}, + * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}. + */ + public Double(int type) { + super(type); + } + + /** + * Instantiates a Arc2D with the specified double-valued data. + * + * @param x + * the x coordinate of the upper left corner of the rectangle + * that contains the arc. + * @param y + * the y coordinate of the upper left corner of the rectangle + * that contains the arc. + * @param width + * the width of the rectangle that contains the arc. + * @param height + * the height of the rectangle that contains the arc. + * @param start + * the start angle of the arc in degrees. + * @param extent + * the width angle of the arc in degrees. + * @param type + * the type of the new Arc2D, either {@link Arc2D#OPEN}, + * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}. + */ + public Double(double x, double y, double width, double height, double start, double extent, + int type) { + super(type); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.start = start; + this.extent = extent; + } + + /** + * Instantiates a new Angle2D with the specified float-valued data and + * the bounding rectangle given by the parameter bounds. + * + * @param bounds + * the bounding rectangle of the Angle2D. + * @param start + * the start angle of the arc in degrees. + * @param extent + * the width angle of the arc in degrees. + * @param type + * the type of the new Arc2D, either {@link Arc2D#OPEN}, + * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}. + */ + public Double(Rectangle2D bounds, double start, double extent, int type) { + super(type); + this.x = bounds.getX(); + this.y = bounds.getY(); + this.width = bounds.getWidth(); + this.height = bounds.getHeight(); + this.start = start; + this.extent = extent; + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public double getWidth() { + return width; + } + + @Override + public double getHeight() { + return height; + } + + @Override + public double getAngleStart() { + return start; + } + + @Override + public double getAngleExtent() { + return extent; + } + + @Override + public boolean isEmpty() { + return width <= 0.0 || height <= 0.0; + } + + @Override + public void setArc(double x, double y, double width, double height, double start, + double extent, int type) { + this.setArcType(type); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.start = start; + this.extent = extent; + } + + @Override + public void setAngleStart(double start) { + this.start = start; + } + + @Override + public void setAngleExtent(double extent) { + this.extent = extent; + } + + @Override + protected Rectangle2D makeBounds(double x, double y, double width, double height) { + return new Rectangle2D.Double(x, y, width, height); + } + + } + + /** + * The Class Iterator is the subclass of PathIterator that is used to + * traverse the boundary of a shape of type Arc2D. + */ + class Iterator implements PathIterator { + + /** + * The x coordinate of the center of the arc's bounding rectangle. + */ + double x; + + /** + * The y coordinate of the center of the arc's bounding rectangle. + */ + double y; + + /** + * Half of the width of the arc's bounding rectangle (the radius in the + * case of a circular arc). + */ + double width; + + /** + * Half of the height of the arc's bounding rectangle (the radius in the + * case of a circular arc). + */ + double height; + + /** + * The start angle of the arc in degrees. + */ + double angle; + + /** + * The angle extent in degrees. + */ + double extent; + + /** + * The closure type of the arc. + */ + int type; + + /** + * The path iterator transformation. + */ + AffineTransform t; + + /** + * The current segment index. + */ + int index; + + /** + * The number of arc segments the source arc subdivided to be + * approximated by Bezier curves. Depends on extent value. + */ + int arcCount; + + /** + * The number of line segments. Depends on closure type. + */ + int lineCount; + + /** + * The step to calculate next arc subdivision point. + */ + double step; + + /** + * The temporary value of cosinus of the current angle. + */ + double cos; + + /** + * The temporary value of sinus of the current angle. + */ + double sin; + + /** The coefficient to calculate control points of Bezier curves. */ + double k; + + /** + * The temporary value of x coordinate of the Bezier curve control + * vector. + */ + double kx; + + /** + * The temporary value of y coordinate of the Bezier curve control + * vector. + */ + double ky; + + /** + * The x coordinate of the first path point (MOVE_TO). + */ + double mx; + + /** + * The y coordinate of the first path point (MOVE_TO). + */ + double my; + + /** + * Constructs a new Arc2D.Iterator for given line and transformation + * + * @param a + * the source Arc2D object. + * @param t + * the AffineTransformation. + */ + Iterator(Arc2D a, AffineTransform t) { + if (width < 0 || height < 0) { + arcCount = 0; + lineCount = 0; + index = 1; + return; + } + + this.width = a.getWidth() / 2.0; + this.height = a.getHeight() / 2.0; + this.x = a.getX() + width; + this.y = a.getY() + height; + this.angle = -Math.toRadians(a.getAngleStart()); + this.extent = -a.getAngleExtent(); + this.type = a.getArcType(); + this.t = t; + + if (Math.abs(extent) >= 360.0) { + arcCount = 4; + k = 4.0 / 3.0 * (Math.sqrt(2.0) - 1.0); + step = Math.PI / 2.0; + if (extent < 0.0) { + step = -step; + k = -k; + } + } else { + arcCount = (int)Math.rint(Math.abs(extent) / 90.0); + step = Math.toRadians(extent / arcCount); + k = 4.0 / 3.0 * (1.0 - Math.cos(step / 2.0)) / Math.sin(step / 2.0); + } + + lineCount = 0; + if (type == Arc2D.CHORD) { + lineCount++; + } else if (type == Arc2D.PIE) { + lineCount += 2; + } + } + + public int getWindingRule() { + return WIND_NON_ZERO; + } + + public boolean isDone() { + return index > arcCount + lineCount; + } + + public void next() { + index++; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type; + int count; + if (index == 0) { + type = SEG_MOVETO; + count = 1; + cos = Math.cos(angle); + sin = Math.sin(angle); + kx = k * width * sin; + ky = k * height * cos; + coords[0] = mx = x + cos * width; + coords[1] = my = y + sin * height; + } else if (index <= arcCount) { + type = SEG_CUBICTO; + count = 3; + coords[0] = mx - kx; + coords[1] = my + ky; + angle += step; + cos = Math.cos(angle); + sin = Math.sin(angle); + kx = k * width * sin; + ky = k * height * cos; + coords[4] = mx = x + cos * width; + coords[5] = my = y + sin * height; + coords[2] = mx + kx; + coords[3] = my - ky; + } else if (index == arcCount + lineCount) { + type = SEG_CLOSE; + count = 0; + } else { + type = SEG_LINETO; + count = 1; + coords[0] = x; + coords[1] = y; + } + if (t != null) { + t.transform(coords, 0, coords, 0, count); + } + return type; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type; + int count; + if (index == 0) { + type = SEG_MOVETO; + count = 1; + cos = Math.cos(angle); + sin = Math.sin(angle); + kx = k * width * sin; + ky = k * height * cos; + coords[0] = (float)(mx = x + cos * width); + coords[1] = (float)(my = y + sin * height); + } else if (index <= arcCount) { + type = SEG_CUBICTO; + count = 3; + coords[0] = (float)(mx - kx); + coords[1] = (float)(my + ky); + angle += step; + cos = Math.cos(angle); + sin = Math.sin(angle); + kx = k * width * sin; + ky = k * height * cos; + coords[4] = (float)(mx = x + cos * width); + coords[5] = (float)(my = y + sin * height); + coords[2] = (float)(mx + kx); + coords[3] = (float)(my - ky); + } else if (index == arcCount + lineCount) { + type = SEG_CLOSE; + count = 0; + } else { + type = SEG_LINETO; + count = 1; + coords[0] = (float)x; + coords[1] = (float)y; + } + if (t != null) { + t.transform(coords, 0, coords, 0, count); + } + return type; + } + + } + + /** + * The closure type of the arc. + */ + private int type; + + /** + * Instantiates a new arc2D. + * + * @param type + * the closure type. + */ + protected Arc2D(int type) { + setArcType(type); + } + + /** + * Takes the double-valued data and creates the corresponding Rectangle2D + * object with values either of type float or of type double depending on + * whether this Arc2D instance is of type Float or Double. + * + * @param x + * the x coordinate of the upper left corner of the bounding + * rectangle. + * @param y + * the y coordinate of the upper left corner of the bounding + * rectangle. + * @param width + * the width of the bounding rectangle. + * @param height + * the height of the bounding rectangle. + * @return the corresponding Rectangle2D object. + */ + protected abstract Rectangle2D makeBounds(double x, double y, double width, double height); + + /** + * Gets the start angle. + * + * @return the start angle. + */ + public abstract double getAngleStart(); + + /** + * Gets the width angle. + * + * @return the width angle. + */ + public abstract double getAngleExtent(); + + /** + * Sets the start angle. + * + * @param start + * the new start angle. + */ + public abstract void setAngleStart(double start); + + /** + * Sets the width angle. + * + * @param extent + * the new width angle. + */ + public abstract void setAngleExtent(double extent); + + /** + * Sets the data values that define the arc. + * + * @param x + * the x coordinate of the upper left corner of the rectangle + * that contains the arc. + * @param y + * the y coordinate of the upper left corner of the rectangle + * that contains the arc. + * @param width + * the width of the rectangle that contains the arc. + * @param height + * the height of the rectangle that contains the arc. + * @param start + * the start angle of the arc in degrees. + * @param extent + * the width angle of the arc in degrees. + * @param type + * the type of the new Arc2D, either {@link Arc2D#OPEN}, + * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}. + */ + public abstract void setArc(double x, double y, double width, double height, double start, + double extent, int type); + + /** + * Gets the arc type, either {@link Arc2D#OPEN}, {@link Arc2D#CHORD}, or + * {@link Arc2D#PIE}. + * + * @return the arc type. + */ + public int getArcType() { + return type; + } + + /** + * Sets the arc type, either {@link Arc2D#OPEN}, {@link Arc2D#CHORD}, or + * {@link Arc2D#PIE}. + * + * @param type + * the new arc type. + */ + public void setArcType(int type) { + if (type != OPEN && type != CHORD && type != PIE) { + // awt.205=Invalid type of Arc: {0} + throw new IllegalArgumentException(Messages.getString("awt.205", type)); //$NON-NLS-1$ + } + this.type = type; + } + + /** + * Gets the start point of the arc as a Point2D. + * + * @return the start point of the curved arc segment. + */ + public Point2D getStartPoint() { + double a = Math.toRadians(getAngleStart()); + return new Point2D.Double(getX() + (1.0 + Math.cos(a)) * getWidth() / 2.0, getY() + + (1.0 - Math.sin(a)) * getHeight() / 2.0); + } + + /** + * Gets the end point of the arc as a Point2D. + * + * @return the end point of the curved arc segment. + */ + public Point2D getEndPoint() { + double a = Math.toRadians(getAngleStart() + getAngleExtent()); + return new Point2D.Double(getX() + (1.0 + Math.cos(a)) * getWidth() / 2.0, getY() + + (1.0 - Math.sin(a)) * getHeight() / 2.0); + } + + public Rectangle2D getBounds2D() { + if (isEmpty()) { + return makeBounds(getX(), getY(), getWidth(), getHeight()); + } + double rx1 = getX(); + double ry1 = getY(); + double rx2 = rx1 + getWidth(); + double ry2 = ry1 + getHeight(); + + Point2D p1 = getStartPoint(); + Point2D p2 = getEndPoint(); + + double bx1 = containsAngle(180.0) ? rx1 : Math.min(p1.getX(), p2.getX()); + double by1 = containsAngle(90.0) ? ry1 : Math.min(p1.getY(), p2.getY()); + double bx2 = containsAngle(0.0) ? rx2 : Math.max(p1.getX(), p2.getX()); + double by2 = containsAngle(270.0) ? ry2 : Math.max(p1.getY(), p2.getY()); + + if (type == PIE) { + double cx = getCenterX(); + double cy = getCenterY(); + bx1 = Math.min(bx1, cx); + by1 = Math.min(by1, cy); + bx2 = Math.max(bx2, cx); + by2 = Math.max(by2, cy); + } + return makeBounds(bx1, by1, bx2 - bx1, by2 - by1); + } + + @Override + public void setFrame(double x, double y, double width, double height) { + setArc(x, y, width, height, getAngleStart(), getAngleExtent(), type); + } + + /** + * Sets the data that defines the arc. + * + * @param point + * the upper left corner of the bounding rectangle. + * @param size + * the size of the bounding rectangle. + * @param start + * the start angle of the arc in degrees. + * @param extent + * the angle width of the arc in degrees. + * @param type + * the closure type, either {@link Arc2D#OPEN}, + * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}. + */ + public void setArc(Point2D point, Dimension2D size, double start, double extent, int type) { + setArc(point.getX(), point.getY(), size.getWidth(), size.getHeight(), start, extent, type); + } + + /** + * Sets the data that defines the arc. + * + * @param rect + * the arc's bounding rectangle. + * @param start + * the start angle of the arc in degrees. + * @param extent + * the angle width of the arc in degrees. + * @param type + * the closure type, either {@link Arc2D#OPEN}, + * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}. + */ + public void setArc(Rectangle2D rect, double start, double extent, int type) { + setArc(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight(), start, extent, type); + } + + /** + * Sets the data that defines the arc by copying it from another Arc2D. + * + * @param arc + * the arc whose data is copied into this arc. + */ + public void setArc(Arc2D arc) { + setArc(arc.getX(), arc.getY(), arc.getWidth(), arc.getHeight(), arc.getAngleStart(), arc + .getAngleExtent(), arc.getArcType()); + } + + /** + * Sets the data for a circular arc by giving its center and radius. + * + * @param x + * the x coordinate of the center of the circle. + * @param y + * the y coordinate of the center of the circle. + * @param radius + * the radius of the circle. + * @param start + * the start angle of the arc in degrees. + * @param extent + * the angle width of the arc in degrees. + * @param type + * the closure type, either {@link Arc2D#OPEN}, + * {@link Arc2D#CHORD}, or {@link Arc2D#PIE}. + */ + public void setArcByCenter(double x, double y, double radius, double start, double extent, + int type) { + setArc(x - radius, y - radius, radius * 2.0, radius * 2.0, start, extent, type); + } + + /** + * Sets the arc data for a circular arc based on two tangent lines and the + * radius. The two tangent lines are the lines from p1 to p2 and from p2 to + * p3, which determine a unique circle with the given radius. The start and + * end points of the arc are the points where the circle touches the two + * lines, and the arc itself is the shorter of the two circle segments + * determined by the two points (in other words, it is the piece of the + * circle that is closer to the lines' intersection point p2 and forms a + * concave shape with the segments from p1 to p2 and from p2 to p3). + * + * @param p1 + * a point which determines one of the two tangent lines (with + * p2). + * @param p2 + * the point of intersection of the two tangent lines. + * @param p3 + * a point which determines one of the two tangent lines (with + * p2). + * @param radius + * the radius of the circular arc. + */ + public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, double radius) { + // Used simple geometric calculations of arc center, radius and angles + // by tangents + double a1 = -Math.atan2(p1.getY() - p2.getY(), p1.getX() - p2.getX()); + double a2 = -Math.atan2(p3.getY() - p2.getY(), p3.getX() - p2.getX()); + double am = (a1 + a2) / 2.0; + double ah = a1 - am; + double d = radius / Math.abs(Math.sin(ah)); + double x = p2.getX() + d * Math.cos(am); + double y = p2.getY() - d * Math.sin(am); + ah = ah >= 0.0 ? Math.PI * 1.5 - ah : Math.PI * 0.5 - ah; + a1 = getNormAngle(Math.toDegrees(am - ah)); + a2 = getNormAngle(Math.toDegrees(am + ah)); + double delta = a2 - a1; + if (delta <= 0.0) { + delta += 360.0; + } + setArcByCenter(x, y, radius, a1, delta, type); + } + + /** + * Sets a new start angle to be the angle given by the the vector from the + * current center point to the specified point. + * + * @param point + * the point that determines the new start angle. + */ + public void setAngleStart(Point2D point) { + double angle = Math.atan2(point.getY() - getCenterY(), point.getX() - getCenterX()); + setAngleStart(getNormAngle(-Math.toDegrees(angle))); + } + + /** + * Sets the angles in terms of vectors from the current arc center to the + * points (x1, y1) and (x2, y2). The start angle is given by the vector from + * the current center to the point (x1, y1) and the end angle is given by + * the vector from the center to the point (x2, y2). + * + * @param x1 + * the x coordinate of the point whose vector from the center + * point determines the new start angle of the arc. + * @param y1 + * the y coordinate of the point whose vector from the center + * point determines the new start angle of the arc. + * @param x2 + * the x coordinate of the point whose vector from the center + * point determines the new end angle of the arc. + * @param y2 + * the y coordinate of the point whose vector from the center + * point determines the new end angle of the arc. + */ + public void setAngles(double x1, double y1, double x2, double y2) { + double cx = getCenterX(); + double cy = getCenterY(); + double a1 = getNormAngle(-Math.toDegrees(Math.atan2(y1 - cy, x1 - cx))); + double a2 = getNormAngle(-Math.toDegrees(Math.atan2(y2 - cy, x2 - cx))); + a2 -= a1; + if (a2 <= 0.0) { + a2 += 360.0; + } + setAngleStart(a1); + setAngleExtent(a2); + } + + /** + * Sets the angles in terms of vectors from the current arc center to the + * points p1 and p2. The start angle is given by the vector from the current + * center to the point p1 and the end angle is given by the vector from the + * center to the point p2. + * + * @param p1 + * the point whose vector from the center point determines the + * new start angle of the arc. + * @param p2 + * the point whose vector from the center point determines the + * new end angle of the arc. + */ + public void setAngles(Point2D p1, Point2D p2) { + setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Normalizes the angle by removing extra winding (past 360 degrees) and + * placing it in the positive degree range. + * + * @param angle + * the source angle in degrees. + * @return an angle between 0 and 360 degrees which corresponds to the same + * direction vector as the source angle. + */ + double getNormAngle(double angle) { + double n = Math.floor(angle / 360.0); + return angle - n * 360.0; + } + + /** + * Determines whether the given angle is contained in the span of the arc. + * + * @param angle + * the angle to test in degrees. + * @return true, if the given angle is between the start angle and the end + * angle of the arc. + */ + public boolean containsAngle(double angle) { + double extent = getAngleExtent(); + if (extent >= 360.0) { + return true; + } + angle = getNormAngle(angle); + double a1 = getNormAngle(getAngleStart()); + double a2 = a1 + extent; + if (a2 > 360.0) { + return angle >= a1 || angle <= a2 - 360.0; + } + if (a2 < 0.0) { + return angle >= a2 + 360.0 || angle <= a1; + } + return extent > 0.0 ? a1 <= angle && angle <= a2 : a2 <= angle && angle <= a1; + } + + public boolean contains(double px, double py) { + // Normalize point + double nx = (px - getX()) / getWidth() - 0.5; + double ny = (py - getY()) / getHeight() - 0.5; + + if ((nx * nx + ny * ny) > 0.25) { + return false; + } + + double extent = getAngleExtent(); + double absExtent = Math.abs(extent); + if (absExtent >= 360.0) { + return true; + } + + boolean containsAngle = containsAngle(Math.toDegrees(-Math.atan2(ny, nx))); + if (type == PIE) { + return containsAngle; + } + if (absExtent <= 180.0 && !containsAngle) { + return false; + } + + Line2D l = new Line2D.Double(getStartPoint(), getEndPoint()); + int ccw1 = l.relativeCCW(px, py); + int ccw2 = l.relativeCCW(getCenterX(), getCenterY()); + return ccw1 == 0 || ccw2 == 0 || ((ccw1 + ccw2) == 0 ^ absExtent > 180.0); + } + + public boolean contains(double rx, double ry, double rw, double rh) { + + if (!(contains(rx, ry) && contains(rx + rw, ry) && contains(rx + rw, ry + rh) && contains( + rx, ry + rh))) { + return false; + } + + double absExtent = Math.abs(getAngleExtent()); + if (type != PIE || absExtent <= 180.0 || absExtent >= 360.0) { + return true; + } + + Rectangle2D r = new Rectangle2D.Double(rx, ry, rw, rh); + + double cx = getCenterX(); + double cy = getCenterY(); + if (r.contains(cx, cy)) { + return false; + } + + Point2D p1 = getStartPoint(); + Point2D p2 = getEndPoint(); + + return !r.intersectsLine(cx, cy, p1.getX(), p1.getY()) + && !r.intersectsLine(cx, cy, p2.getX(), p2.getY()); + } + + @Override + public boolean contains(Rectangle2D rect) { + return contains(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + } + + public boolean intersects(double rx, double ry, double rw, double rh) { + + if (isEmpty() || rw <= 0.0 || rh <= 0.0) { + return false; + } + + // Check: Does arc contain rectangle's points + if (contains(rx, ry) || contains(rx + rw, ry) || contains(rx, ry + rh) + || contains(rx + rw, ry + rh)) { + return true; + } + + double cx = getCenterX(); + double cy = getCenterY(); + Point2D p1 = getStartPoint(); + Point2D p2 = getEndPoint(); + Rectangle2D r = new Rectangle2D.Double(rx, ry, rw, rh); + + // Check: Does rectangle contain arc's points + if (r.contains(p1) || r.contains(p2) || (type == PIE && r.contains(cx, cy))) { + return true; + } + + if (type == PIE) { + if (r.intersectsLine(p1.getX(), p1.getY(), cx, cy) + || r.intersectsLine(p2.getX(), p2.getY(), cx, cy)) { + return true; + } + } else { + if (r.intersectsLine(p1.getX(), p1.getY(), p2.getX(), p2.getY())) { + return true; + } + } + + // Nearest rectangle point + double nx = cx < rx ? rx : (cx > rx + rw ? rx + rw : cx); + double ny = cy < ry ? ry : (cy > ry + rh ? ry + rh : cy); + return contains(nx, ny); + } + + public PathIterator getPathIterator(AffineTransform at) { + return new Iterator(this, at); + } + +} diff --git a/awt/java/awt/geom/Area.java b/awt/java/awt/geom/Area.java new file mode 100644 index 000000000..e6619e349 --- /dev/null +++ b/awt/java/awt/geom/Area.java @@ -0,0 +1,330 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.luni.util.NotImplementedException; + +/** + * The Class Area provides a minimal implementation for a generic shape. + * + * @since Android 1.0 + */ +public class Area implements Shape, Cloneable { + + /** + * The source Shape object. + */ + Shape s; + + /** + * The Class NullIterator. + */ + private static class NullIterator implements PathIterator { + + /** + * Instantiates a new null iterator. + */ + NullIterator() { + } + + public int getWindingRule() { + return WIND_NON_ZERO; + } + + public boolean isDone() { + return true; + } + + public void next() { + // nothing + } + + public int currentSegment(double[] coords) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + + public int currentSegment(float[] coords) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + + } + + /** + * Instantiates a new area with no data. + */ + public Area() { + } + + /** + * Instantiates a new area with data given by the specified shape. + * + * @param s + * the shape that gives the data for this Area. + */ + public Area(Shape s) { + if (s == null) { + throw new NullPointerException(); + } + this.s = s; + } + + public boolean contains(double x, double y) { + return s == null ? false : s.contains(x, y); + } + + public boolean contains(double x, double y, double width, double height) { + return s == null ? false : s.contains(x, y, width, height); + } + + public boolean contains(Point2D p) { + if (p == null) { + throw new NullPointerException(); + } + return s == null ? false : s.contains(p); + } + + public boolean contains(Rectangle2D r) { + if (r == null) { + throw new NullPointerException(); + } + return s == null ? false : s.contains(r); + } + + /** + * Tests whether the object is equal to this Area. + * + * @param obj + * the object to compare. + * @return true, if successful. + * @throws NotImplementedException + * if this method is not implemented. + */ + public boolean equals(Area obj) throws org.apache.harmony.luni.util.NotImplementedException { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + public boolean intersects(double x, double y, double width, double height) { + return s == null ? false : s.intersects(x, y, width, height); + } + + public boolean intersects(Rectangle2D r) { + if (r == null) { + throw new NullPointerException(); + } + return s == null ? false : s.intersects(r); + } + + public Rectangle getBounds() { + return s == null ? new Rectangle() : s.getBounds(); + } + + public Rectangle2D getBounds2D() { + return s == null ? new Rectangle2D.Double() : s.getBounds2D(); + } + + public PathIterator getPathIterator(AffineTransform t) { + return s == null ? new NullIterator() : s.getPathIterator(t); + } + + public PathIterator getPathIterator(AffineTransform t, double flatness) { + return s == null ? new NullIterator() : s.getPathIterator(t, flatness); + } + + /** + * Adds the specified area to this area. + * + * @param area + * the area to add to this area. + * @throws NotImplementedException + * if this method is not implemented. + */ + public void add(Area area) throws org.apache.harmony.luni.util.NotImplementedException { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + /** + * Performs an exclusive or operation between this shape and the specified + * shape. + * + * @param area + * the area to XOR against this area. + * @throws NotImplementedException + * if this method is not implemented. + */ + public void exclusiveOr(Area area) throws org.apache.harmony.luni.util.NotImplementedException { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + /** + * Extracts a Rectangle2D from the source shape if the underlying shape data + * describes a rectangle. + * + * @return a Rectangle2D object if the source shape is rectangle, or null if + * shape is empty or not rectangle. + */ + Rectangle2D extractRectangle() { + if (s == null) { + return null; + } + float[] points = new float[12]; + int count = 0; + PathIterator p = s.getPathIterator(null); + float[] coords = new float[6]; + while (!p.isDone()) { + int type = p.currentSegment(coords); + if (count > 12 || type == PathIterator.SEG_QUADTO || type == PathIterator.SEG_CUBICTO) { + return null; + } + points[count++] = coords[0]; + points[count++] = coords[1]; + p.next(); + } + if (points[0] == points[6] && points[6] == points[8] && points[2] == points[4] + && points[1] == points[3] && points[3] == points[9] && points[5] == points[7]) { + return new Rectangle2D.Float(points[0], points[1], points[2] - points[0], points[7] + - points[1]); + } + return null; + } + + /** + * Reduces the size of this Area by intersecting it with the specified Area + * if they are both rectangles. + * + * @see java.awt.geom.Rectangle2D#intersect(Rectangle2D, Rectangle2D, + * Rectangle2D) + * @param area + * the area. + */ + public void intersect(Area area) { + Rectangle2D src1 = extractRectangle(); + Rectangle2D src2 = area.extractRectangle(); + if (src1 != null && src2 != null) { + Rectangle2D.intersect(src1, src2, (Rectangle2D)s); + } + } + + /** + * Subtract the specified area from this area. + * + * @param area + * the area to subtract. + * @throws NotImplementedException + * if this method is not implemented. + */ + public void subtract(Area area) throws org.apache.harmony.luni.util.NotImplementedException { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + /** + * Checks if this Area is empty. + * + * @return true, if this Area is empty. + * @throws NotImplementedException + * if this method is not implemented. + */ + public boolean isEmpty() throws org.apache.harmony.luni.util.NotImplementedException { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + /** + * Checks if this Area is polygonal. + * + * @return true, if this Area is polygonal. + * @throws NotImplementedException + * if this method is not implemented. + */ + public boolean isPolygonal() throws org.apache.harmony.luni.util.NotImplementedException { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + /** + * Checks if this Area is rectangular. + * + * @return true, if this Area is rectangular. + * @throws NotImplementedException + * if this method is not implemented. + */ + public boolean isRectangular() throws org.apache.harmony.luni.util.NotImplementedException { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + /** + * Checks if this Area is singular. + * + * @return true, if this Area is singular. + * @throws NotImplementedException + * if this method is not implemented. + */ + public boolean isSingular() throws org.apache.harmony.luni.util.NotImplementedException { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + /** + * Resets the data of this Area. + * + * @throws NotImplementedException + * if this method is not implemented. + */ + public void reset() throws org.apache.harmony.luni.util.NotImplementedException { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + /** + * Transforms the data of this Area according to the specified + * AffineTransform. + * + * @param t + * the transform to use to transform the data. + */ + public void transform(AffineTransform t) { + s = t.createTransformedShape(s); + } + + /** + * Creates a new Area that is the result of transforming the data of this + * Area according to the specified AffineTransform. + * + * @param t + * the transform to use to transform the data. + * @return the new Area that is the result of transforming the data of this + * Area according to the specified AffineTransform. + */ + public Area createTransformedArea(AffineTransform t) { + return s == null ? new Area() : new Area(t.createTransformedShape(s)); + } + + @Override + public Object clone() { + return new Area(this); + } + +} diff --git a/awt/java/awt/geom/CubicCurve2D.java b/awt/java/awt/geom/CubicCurve2D.java new file mode 100644 index 000000000..1ddedf39a --- /dev/null +++ b/awt/java/awt/geom/CubicCurve2D.java @@ -0,0 +1,1047 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.gl.Crossing; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class CubicCurve2D is a Shape that represents a segment of a quadratic + * (Bezier) curve. The curved segment is determined by four points: a start + * point, an end point, and two control points. The control points give + * information about the tangent and next derivative at the endpoints according + * to the standard theory of Bezier curves. For more information on Bezier + * curves, see this + * article. + * + * @since Android 1.0 + */ +public abstract class CubicCurve2D implements Shape, Cloneable { + + /** + * The Class Float is the subclass of CubicCurve2D that has all of its data + * values stored with float-level precision. + * + * @since Android 1.0 + */ + public static class Float extends CubicCurve2D { + + /** + * The x coordinate of the starting point. + */ + public float x1; + + /** + * The y coordinate of the starting point. + */ + public float y1; + + /** + * The x coordinate of the first control point. + */ + public float ctrlx1; + + /** + * The y coordinate of the first control point. + */ + public float ctrly1; + + /** + * The x coordinate of the second control point. + */ + public float ctrlx2; + + /** + * The y coordinate of the second control point. + */ + public float ctrly2; + + /** + * The x coordinate of the end point. + */ + public float x2; + + /** + * The y coordinate of the end point. + */ + public float y2; + + /** + * Instantiates a new float-valued CubicCurve2D with all coordinate + * values set to zero. + */ + public Float() { + } + + /** + * Instantiates a new float-valued CubicCurve2D with the specified + * coordinate values. + * + * @param x1 + * the x coordinate of the starting point. + * @param y1 + * the y coordinate of the starting point. + * @param ctrlx1 + * the x coordinate of the first control point. + * @param ctrly1 + * the y coordinate of the first control point. + * @param ctrlx2 + * the x coordinate of the second control point. + * @param ctrly2 + * the y coordinate of the second control point. + * @param x2 + * the x coordinate of the end point. + * @param y2 + * the y coordinate of the end point. + */ + public Float(float x1, float y1, float ctrlx1, float ctrly1, float ctrlx2, float ctrly2, + float x2, float y2) { + setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2); + } + + @Override + public double getX1() { + return x1; + } + + @Override + public double getY1() { + return y1; + } + + @Override + public double getCtrlX1() { + return ctrlx1; + } + + @Override + public double getCtrlY1() { + return ctrly1; + } + + @Override + public double getCtrlX2() { + return ctrlx2; + } + + @Override + public double getCtrlY2() { + return ctrly2; + } + + @Override + public double getX2() { + return x2; + } + + @Override + public double getY2() { + return y2; + } + + @Override + public Point2D getP1() { + return new Point2D.Float(x1, y1); + } + + @Override + public Point2D getCtrlP1() { + return new Point2D.Float(ctrlx1, ctrly1); + } + + @Override + public Point2D getCtrlP2() { + return new Point2D.Float(ctrlx2, ctrly2); + } + + @Override + public Point2D getP2() { + return new Point2D.Float(x2, y2); + } + + @Override + public void setCurve(double x1, double y1, double ctrlx1, double ctrly1, double ctrlx2, + double ctrly2, double x2, double y2) { + this.x1 = (float)x1; + this.y1 = (float)y1; + this.ctrlx1 = (float)ctrlx1; + this.ctrly1 = (float)ctrly1; + this.ctrlx2 = (float)ctrlx2; + this.ctrly2 = (float)ctrly2; + this.x2 = (float)x2; + this.y2 = (float)y2; + } + + /** + * Sets the data values of the curve. + * + * @param x1 + * the x coordinate of the starting point. + * @param y1 + * the y coordinate of the starting point. + * @param ctrlx1 + * the x coordinate of the first control point. + * @param ctrly1 + * the y coordinate of the first control point. + * @param ctrlx2 + * the x coordinate of the second control point. + * @param ctrly2 + * the y coordinate of the second control point. + * @param x2 + * the x coordinate of the end point. + * @param y2 + * the y coordinate of the end point. + */ + public void setCurve(float x1, float y1, float ctrlx1, float ctrly1, float ctrlx2, + float ctrly2, float x2, float y2) { + this.x1 = x1; + this.y1 = y1; + this.ctrlx1 = ctrlx1; + this.ctrly1 = ctrly1; + this.ctrlx2 = ctrlx2; + this.ctrly2 = ctrly2; + this.x2 = x2; + this.y2 = y2; + } + + public Rectangle2D getBounds2D() { + float rx1 = Math.min(Math.min(x1, x2), Math.min(ctrlx1, ctrlx2)); + float ry1 = Math.min(Math.min(y1, y2), Math.min(ctrly1, ctrly2)); + float rx2 = Math.max(Math.max(x1, x2), Math.max(ctrlx1, ctrlx2)); + float ry2 = Math.max(Math.max(y1, y2), Math.max(ctrly1, ctrly2)); + return new Rectangle2D.Float(rx1, ry1, rx2 - rx1, ry2 - ry1); + } + } + + /** + * The Class Double is the subclass of CubicCurve2D that has all of its data + * values stored with double-level precision. + * + * @since Android 1.0 + */ + public static class Double extends CubicCurve2D { + + /** + * The x coordinate of the starting point. + */ + public double x1; + + /** + * The y coordinate of the starting point. + */ + public double y1; + + /** + * The x coordinate of the first control point. + */ + public double ctrlx1; + + /** + * The y coordinate of the first control point. + */ + public double ctrly1; + + /** + * The x coordinate of the second control point. + */ + public double ctrlx2; + + /** + * The y coordinate of the second control point. + */ + public double ctrly2; + + /** + * The x coordinate of the end point. + */ + public double x2; + + /** + * The y coordinate of the end point. + */ + public double y2; + + /** + * Instantiates a new double-valued CubicCurve2D with all coordinate + * values set to zero. + */ + public Double() { + } + + /** + * Instantiates a new double-valued CubicCurve2D with the specified + * coordinate values. + * + * @param x1 + * the x coordinate of the starting point. + * @param y1 + * the y coordinate of the starting point. + * @param ctrlx1 + * the x coordinate of the first control point. + * @param ctrly1 + * the y coordinate of the first control point. + * @param ctrlx2 + * the x coordinate of the second control point. + * @param ctrly2 + * the y coordinate of the second control point. + * @param x2 + * the x coordinate of the end point. + * @param y2 + * the y coordinate of the end point. + */ + public Double(double x1, double y1, double ctrlx1, double ctrly1, double ctrlx2, + double ctrly2, double x2, double y2) { + setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2); + } + + @Override + public double getX1() { + return x1; + } + + @Override + public double getY1() { + return y1; + } + + @Override + public double getCtrlX1() { + return ctrlx1; + } + + @Override + public double getCtrlY1() { + return ctrly1; + } + + @Override + public double getCtrlX2() { + return ctrlx2; + } + + @Override + public double getCtrlY2() { + return ctrly2; + } + + @Override + public double getX2() { + return x2; + } + + @Override + public double getY2() { + return y2; + } + + @Override + public Point2D getP1() { + return new Point2D.Double(x1, y1); + } + + @Override + public Point2D getCtrlP1() { + return new Point2D.Double(ctrlx1, ctrly1); + } + + @Override + public Point2D getCtrlP2() { + return new Point2D.Double(ctrlx2, ctrly2); + } + + @Override + public Point2D getP2() { + return new Point2D.Double(x2, y2); + } + + @Override + public void setCurve(double x1, double y1, double ctrlx1, double ctrly1, double ctrlx2, + double ctrly2, double x2, double y2) { + this.x1 = x1; + this.y1 = y1; + this.ctrlx1 = ctrlx1; + this.ctrly1 = ctrly1; + this.ctrlx2 = ctrlx2; + this.ctrly2 = ctrly2; + this.x2 = x2; + this.y2 = y2; + } + + public Rectangle2D getBounds2D() { + double rx1 = Math.min(Math.min(x1, x2), Math.min(ctrlx1, ctrlx2)); + double ry1 = Math.min(Math.min(y1, y2), Math.min(ctrly1, ctrly2)); + double rx2 = Math.max(Math.max(x1, x2), Math.max(ctrlx1, ctrlx2)); + double ry2 = Math.max(Math.max(y1, y2), Math.max(ctrly1, ctrly2)); + return new Rectangle2D.Double(rx1, ry1, rx2 - rx1, ry2 - ry1); + } + } + + /* + * CubicCurve2D path iterator + */ + /** + * The Iterator class for the Shape CubicCurve2D. + */ + class Iterator implements PathIterator { + + /** + * The source CubicCurve2D object. + */ + CubicCurve2D c; + + /** + * The path iterator transformation. + */ + AffineTransform t; + + /** + * The current segment index. + */ + int index; + + /** + * Constructs a new CubicCurve2D.Iterator for given line and + * transformation + * + * @param c + * the source CubicCurve2D object. + * @param t + * the affine transformation object. + */ + Iterator(CubicCurve2D c, AffineTransform t) { + this.c = c; + this.t = t; + } + + public int getWindingRule() { + return WIND_NON_ZERO; + } + + public boolean isDone() { + return index > 1; + } + + public void next() { + index++; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type; + int count; + if (index == 0) { + type = SEG_MOVETO; + coords[0] = c.getX1(); + coords[1] = c.getY1(); + count = 1; + } else { + type = SEG_CUBICTO; + coords[0] = c.getCtrlX1(); + coords[1] = c.getCtrlY1(); + coords[2] = c.getCtrlX2(); + coords[3] = c.getCtrlY2(); + coords[4] = c.getX2(); + coords[5] = c.getY2(); + count = 3; + } + if (t != null) { + t.transform(coords, 0, coords, 0, count); + } + return type; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type; + int count; + if (index == 0) { + type = SEG_MOVETO; + coords[0] = (float)c.getX1(); + coords[1] = (float)c.getY1(); + count = 1; + } else { + type = SEG_CUBICTO; + coords[0] = (float)c.getCtrlX1(); + coords[1] = (float)c.getCtrlY1(); + coords[2] = (float)c.getCtrlX2(); + coords[3] = (float)c.getCtrlY2(); + coords[4] = (float)c.getX2(); + coords[5] = (float)c.getY2(); + count = 3; + } + if (t != null) { + t.transform(coords, 0, coords, 0, count); + } + return type; + } + + } + + /** + * Instantiates a new 2-D cubic curve. + */ + protected CubicCurve2D() { + } + + /** + * Gets the x coordinate of the starting point. + * + * @return the x coordinate of the starting point. + */ + public abstract double getX1(); + + /** + * Gets the y coordinate of the starting point. + * + * @return the y coordinate of the starting point. + */ + public abstract double getY1(); + + /** + * Gets the starting point. + * + * @return the starting point. + */ + public abstract Point2D getP1(); + + /** + * Gets the x coordinate of the first control point. + * + * @return the x coordinate of the first control point. + */ + public abstract double getCtrlX1(); + + /** + * Gets the y coordinate of the first control point. + * + * @return the y coordinate of the first control point. + */ + public abstract double getCtrlY1(); + + /** + * Gets the second control point. + * + * @return the second control point. + */ + public abstract Point2D getCtrlP1(); + + /** + * Gets the x coordinate of the second control point. + * + * @return the x coordinate of the second control point + */ + public abstract double getCtrlX2(); + + /** + * Gets the y coordinate of the second control point. + * + * @return the y coordinate of the second control point + */ + public abstract double getCtrlY2(); + + /** + * Gets the second control point. + * + * @return the second control point. + */ + public abstract Point2D getCtrlP2(); + + /** + * Gets the x coordinate of the end point. + * + * @return the x coordinate of the end point. + */ + public abstract double getX2(); + + /** + * Gets the y coordinate of the end point. + * + * @return the y coordinate of the end point. + */ + public abstract double getY2(); + + /** + * Gets the end point. + * + * @return the end point. + */ + public abstract Point2D getP2(); + + /** + * Sets the data of the curve. + * + * @param x1 + * the x coordinate of the starting point. + * @param y1 + * the y coordinate of the starting point. + * @param ctrlx1 + * the x coordinate of the first control point. + * @param ctrly1 + * the y coordinate of the first control point. + * @param ctrlx2 + * the x coordinate of the second control point. + * @param ctrly2 + * the y coordinate of the second control point. + * @param x2 + * the x coordinate of the end point. + * @param y2 + * the y coordinate of the end point. + */ + public abstract void setCurve(double x1, double y1, double ctrlx1, double ctrly1, + double ctrlx2, double ctrly2, double x2, double y2); + + /** + * Sets the data of the curve as point objects. + * + * @param p1 + * the starting point. + * @param cp1 + * the first control point. + * @param cp2 + * the second control point. + * @param p2 + * the end point. + * @throws NullPointerException + * if any of the points is null. + */ + public void setCurve(Point2D p1, Point2D cp1, Point2D cp2, Point2D p2) { + setCurve(p1.getX(), p1.getY(), cp1.getX(), cp1.getY(), cp2.getX(), cp2.getY(), p2.getX(), + p2.getY()); + } + + /** + * Sets the data of the curve by reading the data from an array of values. + * The values are read in the same order as the arguments of the method + * {@link CubicCurve2D#setCurve(double, double, double, double, double, double, double, double)} + * . + * + * @param coords + * the array of values containing the new coordinates. + * @param offset + * the offset of the data to read within the array. + * @throws ArrayIndexOutOfBoundsException + * if {@code coords.length} < offset + 8. + * @throws NullPointerException + * if the coordinate array is null. + */ + public void setCurve(double[] coords, int offset) { + setCurve(coords[offset + 0], coords[offset + 1], coords[offset + 2], coords[offset + 3], + coords[offset + 4], coords[offset + 5], coords[offset + 6], coords[offset + 7]); + } + + /** + * Sets the data of the curve by reading the data from an array of points. + * The values are read in the same order as the arguments of the method + * {@link CubicCurve2D#setCurve(Point2D, Point2D, Point2D, Point2D)} + * + * @param points + * the array of points containing the new coordinates. + * @param offset + * the offset of the data to read within the array. + * @throws ArrayIndexOutOfBoundsException + * if {@code points.length} < offset + . + * @throws NullPointerException + * if the point array is null. + */ + public void setCurve(Point2D[] points, int offset) { + setCurve(points[offset + 0].getX(), points[offset + 0].getY(), points[offset + 1].getX(), + points[offset + 1].getY(), points[offset + 2].getX(), points[offset + 2].getY(), + points[offset + 3].getX(), points[offset + 3].getY()); + } + + /** + * Sets the data of the curve by copying it from another CubicCurve2D. + * + * @param curve + * the curve to copy the data points from. + * @throws NullPointerException + * if the curve is null. + */ + public void setCurve(CubicCurve2D curve) { + setCurve(curve.getX1(), curve.getY1(), curve.getCtrlX1(), curve.getCtrlY1(), curve + .getCtrlX2(), curve.getCtrlY2(), curve.getX2(), curve.getY2()); + } + + /** + * Gets the square of the flatness of this curve, where the flatness is the + * maximum distance from the curves control points to the line segment + * connecting the two points. + * + * @return the square of the flatness. + */ + public double getFlatnessSq() { + return getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), getCtrlX2(), getCtrlY2(), + getX2(), getY2()); + } + + /** + * Gets the square of the flatness of the cubic curve segment defined by the + * specified values. + * + * @param x1 + * the x coordinate of the starting point. + * @param y1 + * the y coordinate of the starting point. + * @param ctrlx1 + * the x coordinate of the first control point. + * @param ctrly1 + * the y coordinate of the first control point. + * @param ctrlx2 + * the x coordinate of the second control point. + * @param ctrly2 + * the y coordinate of the second control point. + * @param x2 + * the x coordinate of the end point. + * @param y2 + * the y coordinate of the end point. + * @return the square of the flatness. + */ + public static double getFlatnessSq(double x1, double y1, double ctrlx1, double ctrly1, + double ctrlx2, double ctrly2, double x2, double y2) { + return Math.max(Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx1, ctrly1), Line2D.ptSegDistSq(x1, + y1, x2, y2, ctrlx2, ctrly2)); + } + + /** + * Gets the square of the flatness of the cubic curve segment defined by the + * specified values. The values are read in the same order as the arguments + * of the method + * {@link CubicCurve2D#getFlatnessSq(double, double, double, double, double, double, double, double)} + * . + * + * @param coords + * the array of points containing the new coordinates. + * @param offset + * the offset of the data to read within the array. + * @return the square of the flatness. + * @throws ArrayIndexOutOfBoundsException + * if points.length < offset + . + * @throws NullPointerException + * if the point array is null. + */ + public static double getFlatnessSq(double coords[], int offset) { + return getFlatnessSq(coords[offset + 0], coords[offset + 1], coords[offset + 2], + coords[offset + 3], coords[offset + 4], coords[offset + 5], coords[offset + 6], + coords[offset + 7]); + } + + /** + * Gets the flatness of this curve, where the flatness is the maximum + * distance from the curves control points to the line segment connecting + * the two points. + * + * @return the flatness of this curve. + */ + public double getFlatness() { + return getFlatness(getX1(), getY1(), getCtrlX1(), getCtrlY1(), getCtrlX2(), getCtrlY2(), + getX2(), getY2()); + } + + /** + * Gets the flatness of the cubic curve segment defined by the specified + * values. + * + * @param x1 + * the x coordinate of the starting point. + * @param y1 + * the y coordinate of the starting point. + * @param ctrlx1 + * the x coordinate of the first control point. + * @param ctrly1 + * the y coordinate of the first control point. + * @param ctrlx2 + * the x coordinate of the second control point. + * @param ctrly2 + * the y coordinate of the second control point. + * @param x2 + * the x coordinate of the end point. + * @param y2 + * the y coordinate of the end point. + * @return the flatness. + */ + public static double getFlatness(double x1, double y1, double ctrlx1, double ctrly1, + double ctrlx2, double ctrly2, double x2, double y2) { + return Math.sqrt(getFlatnessSq(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2)); + } + + /** + * Gets the flatness of the cubic curve segment defined by the specified + * values. The values are read in the same order as the arguments of the + * method + * {@link CubicCurve2D#getFlatness(double, double, double, double, double, double, double, double)} + * . + * + * @param coords + * the array of points containing the new coordinates. + * @param offset + * the offset of the data to read within the array. + * @return the flatness. + * @throws ArrayIndexOutOfBoundsException + * if points.length < offset + . + * @throws NullPointerException + * if the point array is null. + */ + public static double getFlatness(double coords[], int offset) { + return getFlatness(coords[offset + 0], coords[offset + 1], coords[offset + 2], + coords[offset + 3], coords[offset + 4], coords[offset + 5], coords[offset + 6], + coords[offset + 7]); + } + + /** + * Creates the data for two cubic curves by dividing this curve in two. The + * division point is the point on the curve that is closest to the average + * of curve's two control points. The two new control points (nearest the + * new endpoint) are computed by averaging the original control points with + * the new endpoint. The data of this curve is left unchanged. + * + * @param left + * the CubicCurve2D where the left (start) segment's data is + * written. + * @param right + * the CubicCurve2D where the right (end) segment's data is + * written. + * @throws NullPointerException + * if either curve is null. + */ + public void subdivide(CubicCurve2D left, CubicCurve2D right) { + subdivide(this, left, right); + } + + /** + * Creates the data for two cubic curves by dividing the specified curve in + * two. The division point is the point on the curve that is closest to the + * average of curve's two control points. The two new control points + * (nearest the new endpoint) are computed by averaging the original control + * points with the new endpoint. The data of the source curve is left + * unchanged. + * + * @param src + * the original curve to be divided in two. + * @param left + * the CubicCurve2D where the left (start) segment's data is + * written. + * @param right + * the CubicCurve2D where the right (end) segment's data is + * written. + * @throws NullPointerException + * if either curve is null. + */ + public static void subdivide(CubicCurve2D src, CubicCurve2D left, CubicCurve2D right) { + double x1 = src.getX1(); + double y1 = src.getY1(); + double cx1 = src.getCtrlX1(); + double cy1 = src.getCtrlY1(); + double cx2 = src.getCtrlX2(); + double cy2 = src.getCtrlY2(); + double x2 = src.getX2(); + double y2 = src.getY2(); + double cx = (cx1 + cx2) / 2.0; + double cy = (cy1 + cy2) / 2.0; + cx1 = (x1 + cx1) / 2.0; + cy1 = (y1 + cy1) / 2.0; + cx2 = (x2 + cx2) / 2.0; + cy2 = (y2 + cy2) / 2.0; + double ax = (cx1 + cx) / 2.0; + double ay = (cy1 + cy) / 2.0; + double bx = (cx2 + cx) / 2.0; + double by = (cy2 + cy) / 2.0; + cx = (ax + bx) / 2.0; + cy = (ay + by) / 2.0; + if (left != null) { + left.setCurve(x1, y1, cx1, cy1, ax, ay, cx, cy); + } + if (right != null) { + right.setCurve(cx, cy, bx, by, cx2, cy2, x2, y2); + } + } + + /** + * Creates the data for two cubic curves by dividing the specified curve in + * two. The division point is the point on the curve that is closest to the + * average of curve's two control points. The two new control points + * (nearest the new endpoint) are computed by averaging the original control + * points with the new endpoint. The data of the source curve is left + * unchanged. The data for the three curves is read/written in the usual + * order: { x1, y1, ctrlx1, ctrly1, ctrlx2, crtry2, x2, y3 } + * + * @param src + * the array that gives the data values for the source curve. + * @param srcOff + * the offset in the src array to read the values from. + * @param left + * the array where the coordinates of the start curve should be + * written. + * @param leftOff + * the offset in the left array to start writing the values. + * @param right + * the array where the coordinates of the end curve should be + * written. + * @param rightOff + * the offset in the right array to start writing the values. + * @throws ArrayIndexOutOfBoundsException + * if src.length < srcoff + 8 or if left.length < leftOff + 8 or + * if right.length < rightOff + 8. + * @throws NullPointerException + * if one of the arrays is null. + */ + public static void subdivide(double src[], int srcOff, double left[], int leftOff, + double right[], int rightOff) { + double x1 = src[srcOff + 0]; + double y1 = src[srcOff + 1]; + double cx1 = src[srcOff + 2]; + double cy1 = src[srcOff + 3]; + double cx2 = src[srcOff + 4]; + double cy2 = src[srcOff + 5]; + double x2 = src[srcOff + 6]; + double y2 = src[srcOff + 7]; + double cx = (cx1 + cx2) / 2.0; + double cy = (cy1 + cy2) / 2.0; + cx1 = (x1 + cx1) / 2.0; + cy1 = (y1 + cy1) / 2.0; + cx2 = (x2 + cx2) / 2.0; + cy2 = (y2 + cy2) / 2.0; + double ax = (cx1 + cx) / 2.0; + double ay = (cy1 + cy) / 2.0; + double bx = (cx2 + cx) / 2.0; + double by = (cy2 + cy) / 2.0; + cx = (ax + bx) / 2.0; + cy = (ay + by) / 2.0; + if (left != null) { + left[leftOff + 0] = x1; + left[leftOff + 1] = y1; + left[leftOff + 2] = cx1; + left[leftOff + 3] = cy1; + left[leftOff + 4] = ax; + left[leftOff + 5] = ay; + left[leftOff + 6] = cx; + left[leftOff + 7] = cy; + } + if (right != null) { + right[rightOff + 0] = cx; + right[rightOff + 1] = cy; + right[rightOff + 2] = bx; + right[rightOff + 3] = by; + right[rightOff + 4] = cx2; + right[rightOff + 5] = cy2; + right[rightOff + 6] = x2; + right[rightOff + 7] = y2; + } + } + + /** + * Finds the roots of the cubic polynomial. This is accomplished by finding + * the (real) values of x that solve the following equation: eqn[3]*x*x*x + + * eqn[2]*x*x + eqn[1]*x + eqn[0] = 0. The solutions are written back into + * the array eqn starting from the index 0 in the array. The return value + * tells how many array elements have been changed by this method call. + * + * @param eqn + * an array containing the coefficients of the cubic polynomial + * to solve. + * @return the number of roots of the cubic polynomial. + * @throws ArrayIndexOutOfBoundsException + * if eqn.length < 4. + * @throws NullPointerException + * if the array is null. + */ + public static int solveCubic(double eqn[]) { + return solveCubic(eqn, eqn); + } + + /** + * Finds the roots of the cubic polynomial. This is accomplished by finding + * the (real) values of x that solve the following equation: eqn[3]*x*x*x + + * eqn[2]*x*x + eqn[1]*x + eqn[0] = 0. The solutions are written into the + * array res starting from the index 0 in the array. The return value tells + * how many array elements have been changed by this method call. + * + * @param eqn + * an array containing the coefficients of the cubic polynomial + * to solve. + * @param res + * the array that this method writes the results into. + * @return the number of roots of the cubic polynomial. + * @throws ArrayIndexOutOfBoundsException + * if eqn.length < 4 or if res.length is less than the number of + * roots. + * @throws NullPointerException + * if either array is null. + */ + public static int solveCubic(double eqn[], double res[]) { + return Crossing.solveCubic(eqn, res); + } + + public boolean contains(double px, double py) { + return Crossing.isInsideEvenOdd(Crossing.crossShape(this, px, py)); + } + + public boolean contains(double rx, double ry, double rw, double rh) { + int cross = Crossing.intersectShape(this, rx, ry, rw, rh); + return cross != Crossing.CROSSING && Crossing.isInsideEvenOdd(cross); + } + + public boolean intersects(double rx, double ry, double rw, double rh) { + int cross = Crossing.intersectShape(this, rx, ry, rw, rh); + return cross == Crossing.CROSSING || Crossing.isInsideEvenOdd(cross); + } + + public boolean contains(Point2D p) { + return contains(p.getX(), p.getY()); + } + + public boolean intersects(Rectangle2D r) { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + public boolean contains(Rectangle2D r) { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + public Rectangle getBounds() { + return getBounds2D().getBounds(); + } + + public PathIterator getPathIterator(AffineTransform t) { + return new Iterator(this, t); + } + + public PathIterator getPathIterator(AffineTransform at, double flatness) { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } +} \ No newline at end of file diff --git a/awt/java/awt/geom/Dimension2D.java b/awt/java/awt/geom/Dimension2D.java new file mode 100644 index 000000000..ea081c517 --- /dev/null +++ b/awt/java/awt/geom/Dimension2D.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +/** + * The Class Dimension2D represents a size (width and height) of a geometric + * object. It stores double-valued data in order to be compatible with + * high-precision geometric operations. + * + * @since Android 1.0 + */ +public abstract class Dimension2D implements Cloneable { + + /** + * Instantiates a new dimension 2d with no data. + */ + protected Dimension2D() { + } + + /** + * Gets the width. + * + * @return the width. + */ + public abstract double getWidth(); + + /** + * Gets the height. + * + * @return the height. + */ + public abstract double getHeight(); + + /** + * Sets the width and height. + * + * @param width + * the width. + * @param height + * the height. + */ + public abstract void setSize(double width, double height); + + /** + * Sets the width and height based on the data of another Dimension2D + * object. + * + * @param d + * the Dimension2D object providing the data to copy into this + * Dimension2D object. + */ + public void setSize(Dimension2D d) { + setSize(d.getWidth(), d.getHeight()); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } +} diff --git a/awt/java/awt/geom/Ellipse2D.java b/awt/java/awt/geom/Ellipse2D.java new file mode 100644 index 000000000..89fd0d066 --- /dev/null +++ b/awt/java/awt/geom/Ellipse2D.java @@ -0,0 +1,458 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class Ellipse2D describes an ellipse defined by a rectangular area in + * which it is inscribed. + * + * @since Android 1.0 + */ +public abstract class Ellipse2D extends RectangularShape { + + /** + * The Class Float is the subclass of Ellipse2D that has all of its data + * values stored with float-level precision. + * + * @since Android 1.0 + */ + public static class Float extends Ellipse2D { + + /** + * The x coordinate of the upper left corner of the ellipse's bounding + * rectangle. + */ + public float x; + + /** + * The y coordinate of the upper left corner of the ellipse's bounding + * rectangle. + */ + public float y; + + /** + * The width of the ellipse's bounding rectangle. + */ + public float width; + + /** + * The height of the ellipse's bounding rectangle. + */ + public float height; + + /** + * Instantiates a new float-valued Ellipse2D. + */ + public Float() { + } + + /** + * Instantiates a new float-valued Ellipse2D with the specified data. + * + * @param x + * the x coordinate of the upper left corner of the ellipse's + * bounding rectangle. + * @param y + * the y coordinate of the upper left corner of the ellipse's + * bounding rectangle. + * @param width + * the width of the ellipse's bounding rectangle. + * @param height + * the height of the ellipse's bounding rectangle. + */ + public Float(float x, float y, float width, float height) { + setFrame(x, y, width, height); + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public double getWidth() { + return width; + } + + @Override + public double getHeight() { + return height; + } + + @Override + public boolean isEmpty() { + return width <= 0.0 || height <= 0.0; + } + + /** + * Sets the data of the ellipse's bounding rectangle. + * + * @param x + * the x coordinate of the upper left corner of the ellipse's + * bounding rectangle. + * @param y + * the y coordinate of the upper left corner of the ellipse's + * bounding rectangle. + * @param width + * the width of the ellipse's bounding rectangle. + * @param height + * the height of the ellipse's bounding rectangle. + */ + public void setFrame(float x, float y, float width, float height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + @Override + public void setFrame(double x, double y, double width, double height) { + this.x = (float)x; + this.y = (float)y; + this.width = (float)width; + this.height = (float)height; + } + + public Rectangle2D getBounds2D() { + return new Rectangle2D.Float(x, y, width, height); + } + } + + /** + * The Class Double is the subclass of Ellipse2D that has all of its data + * values stored with double-level precision. + * + * @since Android 1.0 + */ + public static class Double extends Ellipse2D { + + /** + * The x coordinate of the upper left corner of the ellipse's bounding + * rectangle. + */ + public double x; + + /** + * The y coordinate of the upper left corner of the ellipse's bounding + * rectangle. + */ + public double y; + + /** + * The width of the ellipse's bounding rectangle. + */ + public double width; + + /** + * The height of the ellipse's bounding rectangle. + */ + public double height; + + /** + * Instantiates a new double-valued Ellipse2D. + */ + public Double() { + } + + /** + * Instantiates a new double-valued Ellipse2D with the specified data. + * + * @param x + * the x coordinate of the upper left corner of the ellipse's + * bounding rectangle. + * @param y + * the y coordinate of the upper left corner of the ellipse's + * bounding rectangle. + * @param width + * the width of the ellipse's bounding rectangle. + * @param height + * the height of the ellipse's bounding rectangle. + */ + public Double(double x, double y, double width, double height) { + setFrame(x, y, width, height); + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public double getWidth() { + return width; + } + + @Override + public double getHeight() { + return height; + } + + @Override + public boolean isEmpty() { + return width <= 0.0 || height <= 0.0; + } + + @Override + public void setFrame(double x, double y, double width, double height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public Rectangle2D getBounds2D() { + return new Rectangle2D.Double(x, y, width, height); + } + } + + /* + * Ellipse2D path iterator + */ + /** + * The subclass of PathIterator to traverse an Ellipse2D. + */ + class Iterator implements PathIterator { + + /* + * Ellipse is subdivided into four quarters by x and y axis. Each part + * approximated by cubic Bezier curve. Arc in first quarter is started + * in (a, 0) and finished in (0, b) points. Control points for cubic + * curve wiil be (a, 0), (a, m), (n, b) and (0, b) where n and m are + * calculated based on requirement Bezier curve in point 0.5 should lay + * on the arc. + */ + + /** + * The coefficient to calculate control points of Bezier curves. + */ + final double u = 2.0 / 3.0 * (Math.sqrt(2.0) - 1.0); + + /** + * The points coordinates calculation table. + */ + final double points[][] = { + { + 1.0, 0.5 + u, 0.5 + u, 1.0, 0.5, 1.0 + }, { + 0.5 - u, 1.0, 0.0, 0.5 + u, 0.0, 0.5 + }, { + 0.0, 0.5 - u, 0.5 - u, 0.0, 0.5, 0.0 + }, { + 0.5 + u, 0.0, 1.0, 0.5 - u, 1.0, 0.5 + } + }; + + /** + * The x coordinate of left-upper corner of the ellipse bounds. + */ + double x; + + /** + * The y coordinate of left-upper corner of the ellipse bounds. + */ + double y; + + /** + * The width of the ellipse bounds. + */ + double width; + + /** + * The height of the ellipse bounds. + */ + double height; + + /** + * The path iterator transformation. + */ + AffineTransform t; + + /** + * The current segment index. + */ + int index; + + /** + * Constructs a new Ellipse2D.Iterator for given ellipse and + * transformation + * + * @param e + * the source Ellipse2D object. + * @param t + * the affine transformation object. + */ + Iterator(Ellipse2D e, AffineTransform t) { + this.x = e.getX(); + this.y = e.getY(); + this.width = e.getWidth(); + this.height = e.getHeight(); + this.t = t; + if (width < 0.0 || height < 0.0) { + index = 6; + } + } + + public int getWindingRule() { + return WIND_NON_ZERO; + } + + public boolean isDone() { + return index > 5; + } + + public void next() { + index++; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + if (index == 5) { + return SEG_CLOSE; + } + int type; + int count; + if (index == 0) { + type = SEG_MOVETO; + count = 1; + double p[] = points[3]; + coords[0] = x + p[4] * width; + coords[1] = y + p[5] * height; + } else { + type = SEG_CUBICTO; + count = 3; + double p[] = points[index - 1]; + int j = 0; + for (int i = 0; i < 3; i++) { + coords[j] = x + p[j++] * width; + coords[j] = y + p[j++] * height; + } + } + if (t != null) { + t.transform(coords, 0, coords, 0, count); + } + return type; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + if (index == 5) { + return SEG_CLOSE; + } + int type; + int count; + if (index == 0) { + type = SEG_MOVETO; + count = 1; + double p[] = points[3]; + coords[0] = (float)(x + p[4] * width); + coords[1] = (float)(y + p[5] * height); + } else { + type = SEG_CUBICTO; + count = 3; + int j = 0; + double p[] = points[index - 1]; + for (int i = 0; i < 3; i++) { + coords[j] = (float)(x + p[j++] * width); + coords[j] = (float)(y + p[j++] * height); + } + } + if (t != null) { + t.transform(coords, 0, coords, 0, count); + } + return type; + } + + } + + /** + * Instantiates a new Ellipse2D. + */ + protected Ellipse2D() { + } + + public boolean contains(double px, double py) { + if (isEmpty()) { + return false; + } + + double a = (px - getX()) / getWidth() - 0.5; + double b = (py - getY()) / getHeight() - 0.5; + + return a * a + b * b < 0.25; + } + + public boolean intersects(double rx, double ry, double rw, double rh) { + if (isEmpty() || rw <= 0.0 || rh <= 0.0) { + return false; + } + + double cx = getX() + getWidth() / 2.0; + double cy = getY() + getHeight() / 2.0; + + double rx1 = rx; + double ry1 = ry; + double rx2 = rx + rw; + double ry2 = ry + rh; + + double nx = cx < rx1 ? rx1 : (cx > rx2 ? rx2 : cx); + double ny = cy < ry1 ? ry1 : (cy > ry2 ? ry2 : cy); + + return contains(nx, ny); + } + + public boolean contains(double rx, double ry, double rw, double rh) { + if (isEmpty() || rw <= 0.0 || rh <= 0.0) { + return false; + } + + double rx1 = rx; + double ry1 = ry; + double rx2 = rx + rw; + double ry2 = ry + rh; + + return contains(rx1, ry1) && contains(rx2, ry1) && contains(rx2, ry2) && contains(rx1, ry2); + } + + public PathIterator getPathIterator(AffineTransform at) { + return new Iterator(this, at); + } +} diff --git a/awt/java/awt/geom/FlatteningPathIterator.java b/awt/java/awt/geom/FlatteningPathIterator.java new file mode 100644 index 000000000..8208f3963 --- /dev/null +++ b/awt/java/awt/geom/FlatteningPathIterator.java @@ -0,0 +1,358 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class FlatteningPathIterator takes a PathIterator for traversing a curved + * shape and flattens it by estimating the curve as a series of line segments. + * The flattening factor indicates how far the estimating line segments are + * allowed to be from the actual curve: the FlatteningPathIterator will keep + * dividing each curved segment into smaller and smaller flat segments until + * either the segments are within the flattening factor of the curve or until + * the buffer limit is reached. + * + * @since Android 1.0 + */ +public class FlatteningPathIterator implements PathIterator { + + /** + * The default points buffer size. + */ + private static final int BUFFER_SIZE = 16; + + /** + * The default curve subdivision limit. + */ + private static final int BUFFER_LIMIT = 16; + + /** + * The points buffer capacity. + */ + private static final int BUFFER_CAPACITY = 16; + + /** + * The type of current segment to be flat. + */ + int bufType; + + /** + * The curve subdivision limit. + */ + int bufLimit; + + /** + * The current points buffer size. + */ + int bufSize; + + /** + * The inner cursor position in points buffer. + */ + int bufIndex; + + /** + * The current subdivision count. + */ + int bufSubdiv; + + /** + * The points buffer. + */ + double buf[]; + + /** + * The indicator of empty points buffer. + */ + boolean bufEmpty = true; + + /** + * The source PathIterator. + */ + PathIterator p; + + /** + * The flatness of new path. + */ + double flatness; + + /** + * The square of flatness. + */ + double flatness2; + + /** + * The x coordinate of previous path segment. + */ + double px; + + /** + * The y coordinate of previous path segment. + */ + double py; + + /** + * The temporary buffer for getting points from PathIterator. + */ + double coords[] = new double[6]; + + /** + * Instantiates a new flattening path iterator given the path iterator for a + * (possibly) curved path and a flattening factor which indicates how close + * together the points on the curve should be chosen. The buffer limit + * defaults to 16 which means that each curve will be divided into no more + * than 16 segments regardless of the flattening factor. + * + * @param path + * the path iterator of the original curve. + * @param flatness + * the flattening factor that indicates how far the flat path is + * allowed to be from the actual curve in order to decide when to + * stop dividing the path into smaller and smaller segments. + * @throws IllegalArgumentException + * if the flatness is less than zero. + * @throws NullPointerException + * if the path is null. + */ + public FlatteningPathIterator(PathIterator path, double flatness) { + this(path, flatness, BUFFER_LIMIT); + } + + /** + * Instantiates a new flattening path iterator given the path iterator for a + * (possibly) curved path and a flattening factor and a buffer limit. The + * FlatteningPathIterator will keep dividing each curved segment into + * smaller and smaller flat segments until either the segments are within + * the flattening factor of the curve or until the buffer limit is reached. + * + * @param path + * the path iterator of the original curve. + * @param flatness + * the flattening factor that indicates how far the flat path is + * allowed to be from the actual curve in order to decide when to + * stop dividing the path into smaller and smaller segments. + * @param limit + * the maximum number of flat segments to divide each curve into. + * @throws IllegalArgumentException + * if the flatness or limit is less than zero. + * @throws NullPointerException + * if the path is null. + */ + public FlatteningPathIterator(PathIterator path, double flatness, int limit) { + if (flatness < 0.0) { + // awt.206=Flatness is less then zero + throw new IllegalArgumentException(Messages.getString("awt.206")); //$NON-NLS-1$ + } + if (limit < 0) { + // awt.207=Limit is less then zero + throw new IllegalArgumentException(Messages.getString("awt.207")); //$NON-NLS-1$ + } + if (path == null) { + // awt.208=Path is null + throw new NullPointerException(Messages.getString("awt.208")); //$NON-NLS-1$ + } + this.p = path; + this.flatness = flatness; + this.flatness2 = flatness * flatness; + this.bufLimit = limit; + this.bufSize = Math.min(bufLimit, BUFFER_SIZE); + this.buf = new double[bufSize]; + this.bufIndex = bufSize; + } + + /** + * Gets the flattening factor. + * + * @return the flattening factor. + */ + public double getFlatness() { + return flatness; + } + + /** + * Gets the maximum number of subdivisions per curved segment. + * + * @return the maximum number of subdivisions per curved segment. + */ + public int getRecursionLimit() { + return bufLimit; + } + + public int getWindingRule() { + return p.getWindingRule(); + } + + public boolean isDone() { + return bufEmpty && p.isDone(); + } + + /** + * Calculates flat path points for current segment of the source shape. Line + * segment is flat by itself. Flatness of quad and cubic curves evaluated by + * getFlatnessSq() method. Curves subdivided until current flatness is + * bigger than user defined and subdivision limit isn't exhausted. Single + * source segment translated to series of buffer points. The less flatness + * the bigger series. Every currentSegment() call extract one point from the + * buffer. When series completed evaluate() takes next source shape segment. + */ + void evaluate() { + if (bufEmpty) { + bufType = p.currentSegment(coords); + } + + switch (bufType) { + case SEG_MOVETO: + case SEG_LINETO: + px = coords[0]; + py = coords[1]; + break; + case SEG_QUADTO: + if (bufEmpty) { + bufIndex -= 6; + buf[bufIndex + 0] = px; + buf[bufIndex + 1] = py; + System.arraycopy(coords, 0, buf, bufIndex + 2, 4); + bufSubdiv = 0; + } + + while (bufSubdiv < bufLimit) { + if (QuadCurve2D.getFlatnessSq(buf, bufIndex) < flatness2) { + break; + } + + // Realloc buffer + if (bufIndex <= 4) { + double tmp[] = new double[bufSize + BUFFER_CAPACITY]; + System.arraycopy(buf, bufIndex, tmp, bufIndex + BUFFER_CAPACITY, bufSize + - bufIndex); + buf = tmp; + bufSize += BUFFER_CAPACITY; + bufIndex += BUFFER_CAPACITY; + } + + QuadCurve2D.subdivide(buf, bufIndex, buf, bufIndex - 4, buf, bufIndex); + + bufIndex -= 4; + bufSubdiv++; + } + + bufIndex += 4; + px = buf[bufIndex]; + py = buf[bufIndex + 1]; + + bufEmpty = (bufIndex == bufSize - 2); + if (bufEmpty) { + bufIndex = bufSize; + bufType = SEG_LINETO; + } else { + bufSubdiv--; + } + break; + case SEG_CUBICTO: + if (bufEmpty) { + bufIndex -= 8; + buf[bufIndex + 0] = px; + buf[bufIndex + 1] = py; + System.arraycopy(coords, 0, buf, bufIndex + 2, 6); + bufSubdiv = 0; + } + + while (bufSubdiv < bufLimit) { + if (CubicCurve2D.getFlatnessSq(buf, bufIndex) < flatness2) { + break; + } + + // Realloc buffer + if (bufIndex <= 6) { + double tmp[] = new double[bufSize + BUFFER_CAPACITY]; + System.arraycopy(buf, bufIndex, tmp, bufIndex + BUFFER_CAPACITY, bufSize + - bufIndex); + buf = tmp; + bufSize += BUFFER_CAPACITY; + bufIndex += BUFFER_CAPACITY; + } + + CubicCurve2D.subdivide(buf, bufIndex, buf, bufIndex - 6, buf, bufIndex); + + bufIndex -= 6; + bufSubdiv++; + } + + bufIndex += 6; + px = buf[bufIndex]; + py = buf[bufIndex + 1]; + + bufEmpty = (bufIndex == bufSize - 2); + if (bufEmpty) { + bufIndex = bufSize; + bufType = SEG_LINETO; + } else { + bufSubdiv--; + } + break; + } + + } + + public void next() { + if (bufEmpty) { + p.next(); + } + } + + public int currentSegment(float[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4Bx")); //$NON-NLS-1$ + } + evaluate(); + int type = bufType; + if (type != SEG_CLOSE) { + coords[0] = (float)px; + coords[1] = (float)py; + if (type != SEG_MOVETO) { + type = SEG_LINETO; + } + } + return type; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + evaluate(); + int type = bufType; + if (type != SEG_CLOSE) { + coords[0] = px; + coords[1] = py; + if (type != SEG_MOVETO) { + type = SEG_LINETO; + } + } + return type; + } +} diff --git a/awt/java/awt/geom/GeneralPath.java b/awt/java/awt/geom/GeneralPath.java new file mode 100644 index 000000000..0669bc77b --- /dev/null +++ b/awt/java/awt/geom/GeneralPath.java @@ -0,0 +1,624 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.gl.Crossing; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The class GeneralPath represents a shape whose outline is given by different + * types of curved and straight segments. + * + * @since Android 1.0 + */ +public final class GeneralPath implements Shape, Cloneable { + + /** + * The Constant WIND_EVEN_ODD see {@link PathIterator#WIND_EVEN_ODD}. + */ + public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; + + /** + * The Constant WIND_NON_ZERO see {@link PathIterator#WIND_NON_ZERO}. + */ + public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; + + /** + * The buffers size. + */ + private static final int BUFFER_SIZE = 10; + + /** + * The buffers capacity. + */ + private static final int BUFFER_CAPACITY = 10; + + /** + * The point's types buffer. + */ + byte[] types; + + /** + * The points buffer. + */ + float[] points; + + /** + * The point's type buffer size. + */ + int typeSize; + + /** + * The points buffer size. + */ + int pointSize; + + /** + * The path rule. + */ + int rule; + + /** + * The space amount in points buffer for different segmenet's types. + */ + static int pointShift[] = { + 2, // MOVETO + 2, // LINETO + 4, // QUADTO + 6, // CUBICTO + 0 + }; // CLOSE + + /* + * GeneralPath path iterator + */ + /** + * The Class Iterator is the subclass of Iterator for traversing the outline + * of a GeneralPath. + */ + class Iterator implements PathIterator { + + /** + * The current cursor position in types buffer. + */ + int typeIndex; + + /** + * The current cursor position in points buffer. + */ + int pointIndex; + + /** + * The source GeneralPath object. + */ + GeneralPath p; + + /** + * The path iterator transformation. + */ + AffineTransform t; + + /** + * Constructs a new GeneralPath.Iterator for given general path. + * + * @param path + * the source GeneralPath object. + */ + Iterator(GeneralPath path) { + this(path, null); + } + + /** + * Constructs a new GeneralPath.Iterator for given general path and + * transformation. + * + * @param path + * the source GeneralPath object. + * @param at + * the AffineTransform object to apply rectangle path. + */ + Iterator(GeneralPath path, AffineTransform at) { + this.p = path; + this.t = at; + } + + public int getWindingRule() { + return p.getWindingRule(); + } + + public boolean isDone() { + return typeIndex >= p.typeSize; + } + + public void next() { + typeIndex++; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type = p.types[typeIndex]; + int count = GeneralPath.pointShift[type]; + for (int i = 0; i < count; i++) { + coords[i] = p.points[pointIndex + i]; + } + if (t != null) { + t.transform(coords, 0, coords, 0, count / 2); + } + pointIndex += count; + return type; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type = p.types[typeIndex]; + int count = GeneralPath.pointShift[type]; + System.arraycopy(p.points, pointIndex, coords, 0, count); + if (t != null) { + t.transform(coords, 0, coords, 0, count / 2); + } + pointIndex += count; + return type; + } + + } + + /** + * Instantiates a new general path with the winding rule set to + * {@link PathIterator#WIND_NON_ZERO} and the initial capacity (number of + * segments) set to the default value 10. + */ + public GeneralPath() { + this(WIND_NON_ZERO, BUFFER_SIZE); + } + + /** + * Instantiates a new general path with the given winding rule and the + * initial capacity (number of segments) set to the default value 10. + * + * @param rule + * the winding rule, either {@link PathIterator#WIND_EVEN_ODD} or + * {@link PathIterator#WIND_NON_ZERO}. + */ + public GeneralPath(int rule) { + this(rule, BUFFER_SIZE); + } + + /** + * Instantiates a new general path with the given winding rule and initial + * capacity (number of segments). + * + * @param rule + * the winding rule, either {@link PathIterator#WIND_EVEN_ODD} or + * {@link PathIterator#WIND_NON_ZERO}. + * @param initialCapacity + * the number of segments the path is set to hold. + */ + public GeneralPath(int rule, int initialCapacity) { + setWindingRule(rule); + types = new byte[initialCapacity]; + points = new float[initialCapacity * 2]; + } + + /** + * Creates a new GeneralPath from the outline of the given shape. + * + * @param shape + * the shape. + */ + public GeneralPath(Shape shape) { + this(WIND_NON_ZERO, BUFFER_SIZE); + PathIterator p = shape.getPathIterator(null); + setWindingRule(p.getWindingRule()); + append(p, false); + } + + /** + * Sets the winding rule, which determines how to decide whether a point + * that isn't on the path itself is inside or outside of the shape. + * + * @param rule + * the new winding rule. + * @throws IllegalArgumentException + * if the winding rule is neither + * {@link PathIterator#WIND_EVEN_ODD} nor + * {@link PathIterator#WIND_NON_ZERO}. + */ + public void setWindingRule(int rule) { + if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) { + // awt.209=Invalid winding rule value + throw new java.lang.IllegalArgumentException(Messages.getString("awt.209")); //$NON-NLS-1$ + } + this.rule = rule; + } + + /** + * Gets the winding rule. + * + * @return the winding rule, either {@link PathIterator#WIND_EVEN_ODD} or + * {@link PathIterator#WIND_NON_ZERO}. + */ + public int getWindingRule() { + return rule; + } + + /** + * Checks the point data buffer sizes to see whether pointCount additional + * point-data elements can fit. (Note that the number of point data elements + * to add is more than one per point -- it depends on the type of point + * being added.) Reallocates the buffers to enlarge the size if necessary. + * + * @param pointCount + * the number of point data elements to be added. + * @param checkMove + * whether to check for existing points. + * @throws IllegalPathStateException + * checkMove is true and the path is currently empty. + */ + void checkBuf(int pointCount, boolean checkMove) { + if (checkMove && typeSize == 0) { + // awt.20A=First segment should be SEG_MOVETO type + throw new IllegalPathStateException(Messages.getString("awt.20A")); //$NON-NLS-1$ + } + if (typeSize == types.length) { + byte tmp[] = new byte[typeSize + BUFFER_CAPACITY]; + System.arraycopy(types, 0, tmp, 0, typeSize); + types = tmp; + } + if (pointSize + pointCount > points.length) { + float tmp[] = new float[pointSize + Math.max(BUFFER_CAPACITY * 2, pointCount)]; + System.arraycopy(points, 0, tmp, 0, pointSize); + points = tmp; + } + } + + /** + * Appends a new point to the end of this general path, disconnected from + * the existing path. + * + * @param x + * the x coordinate of the next point to append. + * @param y + * the y coordinate of the next point to append. + */ + public void moveTo(float x, float y) { + if (typeSize > 0 && types[typeSize - 1] == PathIterator.SEG_MOVETO) { + points[pointSize - 2] = x; + points[pointSize - 1] = y; + } else { + checkBuf(2, false); + types[typeSize++] = PathIterator.SEG_MOVETO; + points[pointSize++] = x; + points[pointSize++] = y; + } + } + + /** + * Appends a new segment to the end of this general path by making a + * straight line segment from the current endpoint to the given new point. + * + * @param x + * the x coordinate of the next point to append. + * @param y + * the y coordinate of the next point to append. + */ + public void lineTo(float x, float y) { + checkBuf(2, true); + types[typeSize++] = PathIterator.SEG_LINETO; + points[pointSize++] = x; + points[pointSize++] = y; + } + + /** + * Appends a new segment to the end of this general path by making a + * quadratic curve from the current endpoint to the point (x2, y2) using the + * point (x1, y1) as the quadratic curve's control point. + * + * @param x1 + * the x coordinate of the quadratic curve's control point. + * @param y1 + * the y coordinate of the quadratic curve's control point. + * @param x2 + * the x coordinate of the quadratic curve's end point. + * @param y2 + * the y coordinate of the quadratic curve's end point. + */ + public void quadTo(float x1, float y1, float x2, float y2) { + checkBuf(4, true); + types[typeSize++] = PathIterator.SEG_QUADTO; + points[pointSize++] = x1; + points[pointSize++] = y1; + points[pointSize++] = x2; + points[pointSize++] = y2; + } + + /** + * Appends a new segment to the end of this general path by making a cubic + * curve from the current endpoint to the point (x3, y3) using (x1, y1) and + * (x2, y2) as control points. + * + * @see java.awt.geom.CubicCurve2D + * @param x1 + * the x coordinate of the new cubic segment's first control + * point. + * @param y1 + * the y coordinate of the new cubic segment's first control + * point. + * @param x2 + * the x coordinate of the new cubic segment's second control + * point. + * @param y2 + * the y coordinate of the new cubic segment's second control + * point. + * @param x3 + * the x coordinate of the new cubic segment's end point. + * @param y3 + * the y coordinate of the new cubic segment's end point. + */ + public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) { + checkBuf(6, true); + types[typeSize++] = PathIterator.SEG_CUBICTO; + points[pointSize++] = x1; + points[pointSize++] = y1; + points[pointSize++] = x2; + points[pointSize++] = y2; + points[pointSize++] = x3; + points[pointSize++] = y3; + } + + /** + * Appends the type information to declare that the current endpoint closes + * the curve. + */ + public void closePath() { + if (typeSize == 0 || types[typeSize - 1] != PathIterator.SEG_CLOSE) { + checkBuf(0, true); + types[typeSize++] = PathIterator.SEG_CLOSE; + } + } + + /** + * Appends the outline of the specified shape onto the end of this + * GeneralPath. + * + * @param shape + * the shape whose outline is to be appended. + * @param connect + * true to connect this path's current endpoint to the first + * point of the shape's outline or false to append the shape's + * outline without connecting it. + * @throws NullPointerException + * if the shape parameter is null. + */ + public void append(Shape shape, boolean connect) { + PathIterator p = shape.getPathIterator(null); + append(p, connect); + } + + /** + * Appends the path defined by the specified PathIterator onto the end of + * this GeneralPath. + * + * @param path + * the PathIterator that defines the new path to append. + * @param connect + * true to connect this path's current endpoint to the first + * point of the shape's outline or false to append the shape's + * outline without connecting it. + */ + public void append(PathIterator path, boolean connect) { + while (!path.isDone()) { + float coords[] = new float[6]; + switch (path.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + if (!connect || typeSize == 0) { + moveTo(coords[0], coords[1]); + break; + } + if (types[typeSize - 1] != PathIterator.SEG_CLOSE + && points[pointSize - 2] == coords[0] + && points[pointSize - 1] == coords[1]) { + break; + } + // NO BREAK; + case PathIterator.SEG_LINETO: + lineTo(coords[0], coords[1]); + break; + case PathIterator.SEG_QUADTO: + quadTo(coords[0], coords[1], coords[2], coords[3]); + break; + case PathIterator.SEG_CUBICTO: + curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); + break; + case PathIterator.SEG_CLOSE: + closePath(); + break; + } + path.next(); + connect = false; + } + } + + /** + * Gets the current end point of the path. + * + * @return the current end point of the path. + */ + public Point2D getCurrentPoint() { + if (typeSize == 0) { + return null; + } + int j = pointSize - 2; + if (types[typeSize - 1] == PathIterator.SEG_CLOSE) { + + for (int i = typeSize - 2; i > 0; i--) { + int type = types[i]; + if (type == PathIterator.SEG_MOVETO) { + break; + } + j -= pointShift[type]; + } + } + return new Point2D.Float(points[j], points[j + 1]); + } + + /** + * Resets the GeneralPath to being an empty path. The underlying point and + * segment data is not deleted but rather the end indices of the data arrays + * are set to zero. + */ + public void reset() { + typeSize = 0; + pointSize = 0; + } + + /** + * Transform all of the coordinates of this path according to the specified + * AffineTransform. + * + * @param t + * the AffineTransform. + */ + public void transform(AffineTransform t) { + t.transform(points, 0, points, 0, pointSize / 2); + } + + /** + * Creates a new GeneralPath whose data is given by this path's data + * transformed according to the specified AffineTransform. + * + * @param t + * the AffineTransform. + * @return the new GeneralPath whose data is given by this path's data + * transformed according to the specified AffineTransform. + */ + public Shape createTransformedShape(AffineTransform t) { + GeneralPath p = (GeneralPath)clone(); + if (t != null) { + p.transform(t); + } + return p; + } + + public Rectangle2D getBounds2D() { + float rx1, ry1, rx2, ry2; + if (pointSize == 0) { + rx1 = ry1 = rx2 = ry2 = 0.0f; + } else { + int i = pointSize - 1; + ry1 = ry2 = points[i--]; + rx1 = rx2 = points[i--]; + while (i > 0) { + float y = points[i--]; + float x = points[i--]; + if (x < rx1) { + rx1 = x; + } else if (x > rx2) { + rx2 = x; + } + if (y < ry1) { + ry1 = y; + } else if (y > ry2) { + ry2 = y; + } + } + } + return new Rectangle2D.Float(rx1, ry1, rx2 - rx1, ry2 - ry1); + } + + public Rectangle getBounds() { + return getBounds2D().getBounds(); + } + + /** + * Checks the cross count (number of times a ray from the point crosses the + * shape's boundary) to determine whether the number of crossings + * corresponds to a point inside the shape or not (according to the shape's + * path rule). + * + * @param cross + * the point's cross count. + * @return true if the point is inside the path, or false otherwise. + */ + boolean isInside(int cross) { + if (rule == WIND_NON_ZERO) { + return Crossing.isInsideNonZero(cross); + } + return Crossing.isInsideEvenOdd(cross); + } + + public boolean contains(double px, double py) { + return isInside(Crossing.crossShape(this, px, py)); + } + + public boolean contains(double rx, double ry, double rw, double rh) { + int cross = Crossing.intersectShape(this, rx, ry, rw, rh); + return cross != Crossing.CROSSING && isInside(cross); + } + + public boolean intersects(double rx, double ry, double rw, double rh) { + int cross = Crossing.intersectShape(this, rx, ry, rw, rh); + return cross == Crossing.CROSSING || isInside(cross); + } + + public boolean contains(Point2D p) { + return contains(p.getX(), p.getY()); + } + + public boolean contains(Rectangle2D r) { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + public boolean intersects(Rectangle2D r) { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + public PathIterator getPathIterator(AffineTransform t) { + return new Iterator(this, t); + } + + public PathIterator getPathIterator(AffineTransform t, double flatness) { + return new FlatteningPathIterator(getPathIterator(t), flatness); + } + + @Override + public Object clone() { + try { + GeneralPath p = (GeneralPath)super.clone(); + p.types = types.clone(); + p.points = points.clone(); + return p; + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } + +} diff --git a/awt/java/awt/geom/IllegalPathStateException.java b/awt/java/awt/geom/IllegalPathStateException.java new file mode 100644 index 000000000..750ba29fe --- /dev/null +++ b/awt/java/awt/geom/IllegalPathStateException.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +/** + * The Class IllegalPathStateException indicates errors where the current state + * of a path object is incompatible with the desired action, such as performing + * non-trivial actions on an empty path. + * + * @since Android 1.0 + */ +public class IllegalPathStateException extends RuntimeException { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -5158084205220481094L; + + /** + * Instantiates a new illegal path state exception. + */ + public IllegalPathStateException() { + } + + /** + * Instantiates a new illegal path state exception with the specified detail + * message. + * + * @param s + * the details of the error. + */ + public IllegalPathStateException(String s) { + super(s); + } + +} diff --git a/awt/java/awt/geom/Line2D.java b/awt/java/awt/geom/Line2D.java new file mode 100644 index 000000000..fcd51b6dd --- /dev/null +++ b/awt/java/awt/geom/Line2D.java @@ -0,0 +1,948 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class Line2D represents a line whose data is given in high-precision + * values appropriate for graphical operations. + * + * @since Android 1.0 + */ +public abstract class Line2D implements Shape, Cloneable { + + /** + * The Class Float is the subclass of Line2D that has all of its data values + * stored with float-level precision. + * + * @since Android 1.0 + */ + public static class Float extends Line2D { + + /** + * The x coordinate of the starting point. + */ + public float x1; + + /** + * The y coordinate of the starting point. + */ + public float y1; + + /** + * The x coordinate of the end point. + */ + public float x2; + + /** + * The y coordinate of the end point. + */ + public float y2; + + /** + * Instantiates a new float-valued Line2D with its data values set to + * zero. + */ + public Float() { + } + + /** + * Instantiates a new float-valued Line2D with the specified endpoints. + * + * @param x1 + * the x coordinate of the starting point. + * @param y1 + * the y coordinate of the starting point. + * @param x2 + * the x coordinate of the end point. + * @param y2 + * the y coordinate of the end point. + */ + public Float(float x1, float y1, float x2, float y2) { + setLine(x1, y1, x2, y2); + } + + /** + * Instantiates a new float-valued Line2D with the specified endpoints. + * + * @param p1 + * the starting point. + * @param p2 + * the end point. + */ + public Float(Point2D p1, Point2D p2) { + setLine(p1, p2); + } + + @Override + public double getX1() { + return x1; + } + + @Override + public double getY1() { + return y1; + } + + @Override + public double getX2() { + return x2; + } + + @Override + public double getY2() { + return y2; + } + + @Override + public Point2D getP1() { + return new Point2D.Float(x1, y1); + } + + @Override + public Point2D getP2() { + return new Point2D.Float(x2, y2); + } + + @Override + public void setLine(double x1, double y1, double x2, double y2) { + this.x1 = (float)x1; + this.y1 = (float)y1; + this.x2 = (float)x2; + this.y2 = (float)y2; + } + + /** + * Sets the data values that define the line. + * + * @param x1 + * the x coordinate of the starting point. + * @param y1 + * the y coordinate of the starting point. + * @param x2 + * the x coordinate of the end point. + * @param y2 + * the y coordinate of the end point. + */ + public void setLine(float x1, float y1, float x2, float y2) { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + public Rectangle2D getBounds2D() { + float rx, ry, rw, rh; + if (x1 < x2) { + rx = x1; + rw = x2 - x1; + } else { + rx = x2; + rw = x1 - x2; + } + if (y1 < y2) { + ry = y1; + rh = y2 - y1; + } else { + ry = y2; + rh = y1 - y2; + } + return new Rectangle2D.Float(rx, ry, rw, rh); + } + } + + /** + * The Class Double is the subclass of Line2D that has all of its data + * values stored with double-level precision. + * + * @since Android 1.0 + */ + public static class Double extends Line2D { + + /** + * The x coordinate of the starting point. + */ + public double x1; + + /** + * The y coordinate of the starting point. + */ + public double y1; + + /** + * The x coordinate of the end point. + */ + public double x2; + + /** + * The y coordinate of the end point. + */ + public double y2; + + /** + * Instantiates a new double-valued Line2D with its data values set to + * zero. + */ + public Double() { + } + + /** + * Instantiates a new double-valued Line2D with the specified endpoints. + * + * @param x1 + * the x coordinate of the starting point. + * @param y1 + * the y coordinate of the starting point. + * @param x2 + * the x coordinate of the end point. + * @param y2 + * the y coordinate of the end point. + */ + public Double(double x1, double y1, double x2, double y2) { + setLine(x1, y1, x2, y2); + } + + /** + * Instantiates a new double-valued Line2D with the specified endpoints. + * + * @param p1 + * the starting point. + * @param p2 + * the end point. + */ + public Double(Point2D p1, Point2D p2) { + setLine(p1, p2); + } + + @Override + public double getX1() { + return x1; + } + + @Override + public double getY1() { + return y1; + } + + @Override + public double getX2() { + return x2; + } + + @Override + public double getY2() { + return y2; + } + + @Override + public Point2D getP1() { + return new Point2D.Double(x1, y1); + } + + @Override + public Point2D getP2() { + return new Point2D.Double(x2, y2); + } + + @Override + public void setLine(double x1, double y1, double x2, double y2) { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + public Rectangle2D getBounds2D() { + double rx, ry, rw, rh; + if (x1 < x2) { + rx = x1; + rw = x2 - x1; + } else { + rx = x2; + rw = x1 - x2; + } + if (y1 < y2) { + ry = y1; + rh = y2 - y1; + } else { + ry = y2; + rh = y1 - y2; + } + return new Rectangle2D.Double(rx, ry, rw, rh); + } + } + + /* + * Line2D path iterator + */ + /** + * The subclass of PathIterator to traverse a Line2D. + */ + class Iterator implements PathIterator { + + /** + * The x coordinate of the start line point. + */ + double x1; + + /** + * The y coordinate of the start line point. + */ + double y1; + + /** + * The x coordinate of the end line point. + */ + double x2; + + /** + * The y coordinate of the end line point. + */ + double y2; + + /** + * The path iterator transformation. + */ + AffineTransform t; + + /** + * The current segment index. + */ + int index; + + /** + * Constructs a new Line2D.Iterator for given line and transformation. + * + * @param l + * the source Line2D object. + * @param at + * the AffineTransform object to apply rectangle path. + */ + Iterator(Line2D l, AffineTransform at) { + this.x1 = l.getX1(); + this.y1 = l.getY1(); + this.x2 = l.getX2(); + this.y2 = l.getY2(); + this.t = at; + } + + public int getWindingRule() { + return WIND_NON_ZERO; + } + + public boolean isDone() { + return index > 1; + } + + public void next() { + index++; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type; + if (index == 0) { + type = SEG_MOVETO; + coords[0] = x1; + coords[1] = y1; + } else { + type = SEG_LINETO; + coords[0] = x2; + coords[1] = y2; + } + if (t != null) { + t.transform(coords, 0, coords, 0, 1); + } + return type; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type; + if (index == 0) { + type = SEG_MOVETO; + coords[0] = (float)x1; + coords[1] = (float)y1; + } else { + type = SEG_LINETO; + coords[0] = (float)x2; + coords[1] = (float)y2; + } + if (t != null) { + t.transform(coords, 0, coords, 0, 1); + } + return type; + } + + } + + /** + * Instantiates a new Line2D. + */ + protected Line2D() { + } + + /** + * Gets the x coordinate of the starting point. + * + * @return the x coordinate of the starting point. + */ + public abstract double getX1(); + + /** + * Gets the y coordinate of the starting point. + * + * @return the y coordinate of the starting point. + */ + public abstract double getY1(); + + /** + * Gets the x coordinate of the end point. + * + * @return the x2. + */ + public abstract double getX2(); + + /** + * Gets the y coordinate of the end point. + * + * @return the y coordinate of the end point. + */ + public abstract double getY2(); + + /** + * Gets the p the starting point. + * + * @return the p the starting point. + */ + public abstract Point2D getP1(); + + /** + * Gets the p end point. + * + * @return the p end point. + */ + public abstract Point2D getP2(); + + /** + * Sets the line's endpoints. + * + * @param x1 + * the x coordinate of the starting point. + * @param y1 + * the y coordinate of the starting point. + * @param x2 + * the x coordinate of the end point. + * @param y2 + * the y coordinate of the end point. + */ + public abstract void setLine(double x1, double y1, double x2, double y2); + + /** + * Sets the line's endpoints. + * + * @param p1 + * the starting point. + * @param p2 + * the end point. + */ + public void setLine(Point2D p1, Point2D p2) { + setLine(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Sets the line's endpoints by copying the data from another Line2D. + * + * @param line + * the Line2D to copy the endpoint data from. + */ + public void setLine(Line2D line) { + setLine(line.getX1(), line.getY1(), line.getX2(), line.getY2()); + } + + public Rectangle getBounds() { + return getBounds2D().getBounds(); + } + + /** + * Tells where the point is with respect to the line segment, given the + * orientation of the line segment. If the ray found by extending the line + * segment from its starting point is rotated, this method tells whether the + * ray should rotate in a clockwise direction or a counter-clockwise + * direction to hit the point first. The return value is 0 if the point is + * on the line segment, it's 1 if the point is on the ray or if the ray + * should rotate in a counter-clockwise direction to get to the point, and + * it's -1 if the ray should rotate in a clockwise direction to get to the + * point or if the point is on the line determined by the line segment but + * not on the ray from the segment's starting point and through its end + * point. + * + * @param x1 + * the x coordinate of the starting point of the line segment. + * @param y1 + * the y coordinate of the starting point of the line segment. + * @param x2 + * the x coordinate of the end point of the line segment. + * @param y2 + * the y coordinate of the end point of the line segment. + * @param px + * the x coordinate of the test point. + * @param py + * the p coordinate of the test point. + * @return the value that describes where the point is with respect to the + * line segment, given the orientation of the line segment. + */ + public static int relativeCCW(double x1, double y1, double x2, double y2, double px, double py) { + /* + * A = (x2-x1, y2-y1) P = (px-x1, py-y1) + */ + x2 -= x1; + y2 -= y1; + px -= x1; + py -= y1; + double t = px * y2 - py * x2; // PxA + if (t == 0.0) { + t = px * x2 + py * y2; // P*A + if (t > 0.0) { + px -= x2; // B-A + py -= y2; + t = px * x2 + py * y2; // (P-A)*A + if (t < 0.0) { + t = 0.0; + } + } + } + + return t < 0.0 ? -1 : (t > 0.0 ? 1 : 0); + } + + /** + * Tells where the point is with respect to this line segment, given the + * orientation of this line segment. If the ray found by extending the line + * segment from its starting point is rotated, this method tells whether the + * ray should rotate in a clockwise direction or a counter-clockwise + * direction to hit the point first. The return value is 0 if the point is + * on the line segment, it's 1 if the point is on the ray or if the ray + * should rotate in a counter-clockwise direction to get to the point, and + * it's -1 if the ray should rotate in a clockwise direction to get to the + * point or if the point is on the line determined by the line segment but + * not on the ray from the segment's starting point and through its end + * point. + * + * @param px + * the x coordinate of the test point. + * @param py + * the p coordinate of the test point. + * @return the value that describes where the point is with respect to this + * line segment, given the orientation of this line segment. + */ + public int relativeCCW(double px, double py) { + return relativeCCW(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Tells where the point is with respect to this line segment, given the + * orientation of this line segment. If the ray found by extending the line + * segment from its starting point is rotated, this method tells whether the + * ray should rotate in a clockwise direction or a counter-clockwise + * direction to hit the point first. The return value is 0 if the point is + * on the line segment, it's 1 if the point is on the ray or if the ray + * should rotate in a counter-clockwise direction to get to the point, and + * it's -1 if the ray should rotate in a clockwise direction to get to the + * point or if the point is on the line determined by the line segment but + * not on the ray from the segment's starting point and through its end + * point. + * + * @param p + * the test point. + * @return the value that describes where the point is with respect to this + * line segment, given the orientation of this line segment. + */ + public int relativeCCW(Point2D p) { + return relativeCCW(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Tells whether the two line segments cross. + * + * @param x1 + * the x coordinate of the starting point of the first segment. + * @param y1 + * the y coordinate of the starting point of the first segment. + * @param x2 + * the x coordinate of the end point of the first segment. + * @param y2 + * the y coordinate of the end point of the first segment. + * @param x3 + * the x coordinate of the starting point of the second segment. + * @param y3 + * the y coordinate of the starting point of the second segment. + * @param x4 + * the x coordinate of the end point of the second segment. + * @param y4 + * the y coordinate of the end point of the second segment. + * @return true, if the two line segments cross. + */ + public static boolean linesIntersect(double x1, double y1, double x2, double y2, double x3, + double y3, double x4, double y4) { + /* + * A = (x2-x1, y2-y1) B = (x3-x1, y3-y1) C = (x4-x1, y4-y1) D = (x4-x3, + * y4-y3) = C-B E = (x1-x3, y1-y3) = -B F = (x2-x3, y2-y3) = A-B Result + * is ((AxB) (AxC) <=0) and ((DxE) (DxF) <= 0) DxE = (C-B)x(-B) = + * BxB-CxB = BxC DxF = (C-B)x(A-B) = CxA-CxB-BxA+BxB = AxB+BxC-AxC + */ + + x2 -= x1; // A + y2 -= y1; + x3 -= x1; // B + y3 -= y1; + x4 -= x1; // C + y4 -= y1; + + double AvB = x2 * y3 - x3 * y2; + double AvC = x2 * y4 - x4 * y2; + + // Online + if (AvB == 0.0 && AvC == 0.0) { + if (x2 != 0.0) { + return (x4 * x3 <= 0.0) + || ((x3 * x2 >= 0.0) && (x2 > 0.0 ? x3 <= x2 || x4 <= x2 : x3 >= x2 + || x4 >= x2)); + } + if (y2 != 0.0) { + return (y4 * y3 <= 0.0) + || ((y3 * y2 >= 0.0) && (y2 > 0.0 ? y3 <= y2 || y4 <= y2 : y3 >= y2 + || y4 >= y2)); + } + return false; + } + + double BvC = x3 * y4 - x4 * y3; + + return (AvB * AvC <= 0.0) && (BvC * (AvB + BvC - AvC) <= 0.0); + } + + /** + * Tells whether the specified line segments crosses this line segment. + * + * @param x1 + * the x coordinate of the starting point of the test segment. + * @param y1 + * the y coordinate of the starting point of the test segment. + * @param x2 + * the x coordinate of the end point of the test segment. + * @param y2 + * the y coordinate of the end point of the test segment. + * @return true, if the specified line segments crosses this line segment. + */ + public boolean intersectsLine(double x1, double y1, double x2, double y2) { + return linesIntersect(x1, y1, x2, y2, getX1(), getY1(), getX2(), getY2()); + } + + /** + * Tells whether the specified line segments crosses this line segment. + * + * @param l + * the test segment. + * @return true, if the specified line segments crosses this line segment. + * @throws NullPointerException + * if l is null. + */ + public boolean intersectsLine(Line2D l) { + return linesIntersect(l.getX1(), l.getY1(), l.getX2(), l.getY2(), getX1(), getY1(), + getX2(), getY2()); + } + + /** + * Gives the square of the distance between the point and the line segment. + * + * @param x1 + * the x coordinate of the starting point of the line segment. + * @param y1 + * the y coordinate of the starting point of the line segment. + * @param x2 + * the x coordinate of the end point of the line segment. + * @param y2 + * the y coordinate of the end point of the line segment. + * @param px + * the x coordinate of the test point. + * @param py + * the y coordinate of the test point. + * @return the the square of the distance between the point and the line + * segment. + */ + public static double ptSegDistSq(double x1, double y1, double x2, double y2, double px, + double py) { + /* + * A = (x2 - x1, y2 - y1) P = (px - x1, py - y1) + */ + x2 -= x1; // A = (x2, y2) + y2 -= y1; + px -= x1; // P = (px, py) + py -= y1; + double dist; + if (px * x2 + py * y2 <= 0.0) { // P*A + dist = px * px + py * py; + } else { + px = x2 - px; // P = A - P = (x2 - px, y2 - py) + py = y2 - py; + if (px * x2 + py * y2 <= 0.0) { // P*A + dist = px * px + py * py; + } else { + dist = px * y2 - py * x2; + dist = dist * dist / (x2 * x2 + y2 * y2); // pxA/|A| + } + } + if (dist < 0) { + dist = 0; + } + return dist; + } + + /** + * Gives the distance between the point and the line segment. + * + * @param x1 + * the x coordinate of the starting point of the line segment. + * @param y1 + * the y coordinate of the starting point of the line segment. + * @param x2 + * the x coordinate of the end point of the line segment. + * @param y2 + * the y coordinate of the end point of the line segment. + * @param px + * the x coordinate of the test point. + * @param py + * the y coordinate of the test point. + * @return the the distance between the point and the line segment. + */ + public static double ptSegDist(double x1, double y1, double x2, double y2, double px, double py) { + return Math.sqrt(ptSegDistSq(x1, y1, x2, y2, px, py)); + } + + /** + * Gives the square of the distance between the point and this line segment. + * + * @param px + * the x coordinate of the test point. + * @param py + * the y coordinate of the test point. + * @return the the square of the distance between the point and this line + * segment. + */ + public double ptSegDistSq(double px, double py) { + return ptSegDistSq(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Gives the square of the distance between the point and this line segment. + * + * @param p + * the test point. + * @return the square of the distance between the point and this line + * segment. + */ + public double ptSegDistSq(Point2D p) { + return ptSegDistSq(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Gives the distance between the point and this line segment. + * + * @param px + * the x coordinate of the test point. + * @param py + * the y coordinate of the test point. + * @return the distance between the point and this line segment. + */ + public double ptSegDist(double px, double py) { + return ptSegDist(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Gives the distance between the point and this line segment. + * + * @param p + * the test point. + * @return the distance between the point and this line segment. + */ + public double ptSegDist(Point2D p) { + return ptSegDist(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Gives the square of the distance between the point and the line. + * + * @param x1 + * the x coordinate of the starting point of the line segment. + * @param y1 + * the y coordinate of the starting point of the line segment. + * @param x2 + * the x coordinate of the end point of the line segment. + * @param y2 + * the y coordinate of the end point of the line segment. + * @param px + * the x coordinate of the test point. + * @param py + * the y coordinate of the test point. + * @return the square of the distance between the point and the line. + */ + public static double ptLineDistSq(double x1, double y1, double x2, double y2, double px, + double py) { + x2 -= x1; + y2 -= y1; + px -= x1; + py -= y1; + double s = px * y2 - py * x2; + return s * s / (x2 * x2 + y2 * y2); + } + + /** + * Gives the square of the distance between the point and the line. + * + * @param x1 + * the x coordinate of the starting point of the line segment. + * @param y1 + * the y coordinate of the starting point of the line segment. + * @param x2 + * the x coordinate of the end point of the line segment. + * @param y2 + * the y coordinate of the end point of the line segment. + * @param px + * the x coordinate of the test point. + * @param py + * the y coordinate of the test point. + * @return the square of the distance between the point and the line. + */ + public static double ptLineDist(double x1, double y1, double x2, double y2, double px, double py) { + return Math.sqrt(ptLineDistSq(x1, y1, x2, y2, px, py)); + } + + /** + * Gives the square of the distance between the point and the line + * determined by this Line2D. + * + * @param px + * the x coordinate of the test point. + * @param py + * the y coordinate of the test point. + * @return the square of the distance between the point and the line + * determined by this Line2D. + */ + public double ptLineDistSq(double px, double py) { + return ptLineDistSq(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Gives the square of the distance between the point and the line + * determined by this Line2D. + * + * @param p + * the test point. + * @return the square of the distance between the point and the line + * determined by this Line2D. + */ + public double ptLineDistSq(Point2D p) { + return ptLineDistSq(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Gives the distance between the point and the line determined by this + * Line2D. + * + * @param px + * the x coordinate of the test point. + * @param py + * the y coordinate of the test point. + * @return the distance between the point and the line determined by this + * Line2D. + */ + public double ptLineDist(double px, double py) { + return ptLineDist(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Gives the distance between the point and the line determined by this + * Line2D. + * + * @param p + * the test point. + * @return the distance between the point and the line determined by this + * Line2D. + */ + public double ptLineDist(Point2D p) { + return ptLineDist(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + public boolean contains(double px, double py) { + return false; + } + + public boolean contains(Point2D p) { + return false; + } + + public boolean contains(Rectangle2D r) { + return false; + } + + public boolean contains(double rx, double ry, double rw, double rh) { + return false; + } + + public boolean intersects(double rx, double ry, double rw, double rh) { + return intersects(new Rectangle2D.Double(rx, ry, rw, rh)); + } + + public boolean intersects(Rectangle2D r) { + return r.intersectsLine(getX1(), getY1(), getX2(), getY2()); + } + + public PathIterator getPathIterator(AffineTransform at) { + return new Iterator(this, at); + } + + public PathIterator getPathIterator(AffineTransform at, double flatness) { + return new Iterator(this, at); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } + +} diff --git a/awt/java/awt/geom/NoninvertibleTransformException.java b/awt/java/awt/geom/NoninvertibleTransformException.java new file mode 100644 index 000000000..a4e6f0f85 --- /dev/null +++ b/awt/java/awt/geom/NoninvertibleTransformException.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +/** + * The Class NoninvertibleTransformException is the exception that is thrown + * when an action requires inverting an {@link AffineTransform} that is not + * invertible (has determinant 0). + * + * @since Android 1.0 + */ +public class NoninvertibleTransformException extends java.lang.Exception { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 6137225240503990466L; + + /** + * Instantiates a new non-invertible transform exception. + * + * @param s + * the error message. + */ + public NoninvertibleTransformException(String s) { + super(s); + } + +} diff --git a/awt/java/awt/geom/PathIterator.java b/awt/java/awt/geom/PathIterator.java new file mode 100644 index 000000000..2d1c0ffa6 --- /dev/null +++ b/awt/java/awt/geom/PathIterator.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +/** + * The Interface PathIterator represents an iterator object that can be used to + * traverse the outline of a {@link java.awt.Shape}. It returns points along the + * boundary of the Shape which may be actual vertices (in the case of a shape + * made of line segments) or may be points on a curved segment with the distance + * between the points determined by a chosen flattening factor. + *

+ * If the shape is closed, the outline is traversed in the counter-clockwise + * direction. That means that moving forward along the boundary is to travel in + * such a way that the interior of the shape is to the left of the outline path + * and the exterior of the shape is to the right of the outline path. The + * interior and exterior of the shape are determined by a winding rule. + *

+ * + * @since Android 1.0 + */ +public interface PathIterator { + + /** + * The Constant WIND_EVEN_ODD indicates the winding rule that says that a + * point is outside the shape if any infinite ray from the point crosses the + * outline of the shape an even number of times, otherwise it is inside. + */ + public static final int WIND_EVEN_ODD = 0; + + /** + * The Constant WIND_NON_ZERO indicates the winding rule that says that a + * point is inside the shape if every infinite ray starting from that point + * crosses the outline of the shape a non-zero number of times. + */ + public static final int WIND_NON_ZERO = 1; + + /** + * The Constant SEG_MOVETO indicates that to follow the shape's outline from + * the previous point to the current point, the cursor (traversal point) + * should be placed directly on the current point. + */ + public static final int SEG_MOVETO = 0; + + /** + * The Constant SEG_LINETO indicates that to follow the shape's outline from + * the previous point to the current point, the cursor (traversal point) + * should follow a straight line. + */ + public static final int SEG_LINETO = 1; + + /** + * The Constant SEG_QUADTO indicates that to follow the shape's outline from + * the previous point to the current point, the cursor (traversal point) + * should follow a quadratic curve. + */ + public static final int SEG_QUADTO = 2; + + /** + * The Constant SEG_CUBICTO indicates that to follow the shape's outline + * from the previous point to the current point, the cursor (traversal + * point) should follow a cubic curve. + */ + public static final int SEG_CUBICTO = 3; + + /** + * The Constant SEG_CLOSE indicates that the previous point was the end of + * the shape's outline. + */ + public static final int SEG_CLOSE = 4; + + /** + * Gets the winding rule, either {@link PathIterator#WIND_EVEN_ODD} or + * {@link PathIterator#WIND_NON_ZERO}. + * + * @return the winding rule. + */ + public int getWindingRule(); + + /** + * Checks if this PathIterator has been completely traversed. + * + * @return true, if this PathIterator has been completely traversed. + */ + public boolean isDone(); + + /** + * Tells this PathIterator to skip to the next segment. + */ + public void next(); + + /** + * Gets the coordinates of the next vertex point along the shape's outline + * and a flag that indicates what kind of segment to use in order to connect + * the previous vertex point to the current vertex point to form the current + * segment. + * + * @param coords + * the array that the coordinates of the end point of the current + * segment are written into. + * @return the flag that indicates how to follow the shape's outline from + * the previous point to the current one, chosen from the following + * constants: {@link PathIterator#SEG_MOVETO}, + * {@link PathIterator#SEG_LINETO}, {@link PathIterator#SEG_QUADTO}, + * {@link PathIterator#SEG_CUBICTO}, or + * {@link PathIterator#SEG_CLOSE}. + */ + public int currentSegment(float[] coords); + + /** + * Gets the coordinates of the next vertex point along the shape's outline + * and a flag that indicates what kind of segment to use in order to connect + * the previous vertex point to the current vertex point to form the current + * segment. + * + * @param coords + * the array that the coordinates of the end point of the current + * segment are written into. + * @return the flag that indicates how to follow the shape's outline from + * the previous point to the current one, chosen from the following + * constants: {@link PathIterator#SEG_MOVETO}, + * {@link PathIterator#SEG_LINETO}, {@link PathIterator#SEG_QUADTO}, + * {@link PathIterator#SEG_CUBICTO}, or + * {@link PathIterator#SEG_CLOSE}. + */ + public int currentSegment(double[] coords); + +} diff --git a/awt/java/awt/geom/Point2D.java b/awt/java/awt/geom/Point2D.java new file mode 100644 index 000000000..f7026c8c6 --- /dev/null +++ b/awt/java/awt/geom/Point2D.java @@ -0,0 +1,323 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import org.apache.harmony.misc.HashCode; + +/** + * The Class Point2D represents a point whose data is given in high-precision + * values appropriate for graphical operations. + * + * @since Android 1.0 + */ +public abstract class Point2D implements Cloneable { + + /** + * The Class Float is the subclass of Point2D that has all of its data + * values stored with float-level precision. + * + * @since Android 1.0 + */ + public static class Float extends Point2D { + + /** + * The x coordinate. + */ + public float x; + + /** + * The y coordinate. + */ + public float y; + + /** + * Instantiates a new float-valued Point2D with its data set to zero. + */ + public Float() { + } + + /** + * Instantiates a new float-valued Point2D with the specified + * coordinates. + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + */ + public Float(float x, float y) { + this.x = x; + this.y = y; + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + /** + * Sets the point's coordinates. + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + */ + public void setLocation(float x, float y) { + this.x = x; + this.y = y; + } + + @Override + public void setLocation(double x, double y) { + this.x = (float)x; + this.y = (float)y; + } + + @Override + public String toString() { + return getClass().getName() + "[x=" + x + ",y=" + y + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + + /** + * The Class Double is the subclass of Point2D that has all of its data + * values stored with double-level precision. + * + * @since Android 1.0 + */ + public static class Double extends Point2D { + + /** + * The x coordinate. + */ + public double x; + + /** + * The y coordinate. + */ + public double y; + + /** + * Instantiates a new double-valued Point2D with its data set to zero. + */ + public Double() { + } + + /** + * Instantiates a new double-valued Point2D with the specified + * coordinates. + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + */ + public Double(double x, double y) { + this.x = x; + this.y = y; + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public void setLocation(double x, double y) { + this.x = x; + this.y = y; + } + + @Override + public String toString() { + return getClass().getName() + "[x=" + x + ",y=" + y + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + + /** + * Instantiates a new Point2D. + */ + protected Point2D() { + } + + /** + * Gets the x coordinate. + * + * @return the x coordinate. + */ + public abstract double getX(); + + /** + * Gets the y coordinate. + * + * @return the y coordinate. + */ + public abstract double getY(); + + /** + * Sets the point's coordinates. + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + */ + public abstract void setLocation(double x, double y); + + /** + * Sets the point's coordinates by copying them from another point. + * + * @param p + * the point to copy the data from. + */ + public void setLocation(Point2D p) { + setLocation(p.getX(), p.getY()); + } + + /** + * Finds the square of the distance between the two specified points. + * + * @param x1 + * the x coordinate of the first point. + * @param y1 + * the y coordinate of the first point. + * @param x2 + * the x coordinate of the second point. + * @param y2 + * the y coordinate of the second point. + * @return the square of the distance between the two specified points. + */ + public static double distanceSq(double x1, double y1, double x2, double y2) { + x2 -= x1; + y2 -= y1; + return x2 * x2 + y2 * y2; + } + + /** + * Finds the square of the distance between this point and the specified + * point. + * + * @param px + * the x coordinate of the point. + * @param py + * the y coordinate of the point. + * @return the square of the distance between this point and the specified + * point. + */ + public double distanceSq(double px, double py) { + return Point2D.distanceSq(getX(), getY(), px, py); + } + + /** + * Finds the square of the distance between this point and the specified + * point. + * + * @param p + * the other point. + * @return the square of the distance between this point and the specified + * point. + */ + public double distanceSq(Point2D p) { + return Point2D.distanceSq(getX(), getY(), p.getX(), p.getY()); + } + + /** + * Finds the distance between the two specified points. + * + * @param x1 + * the x coordinate of the first point. + * @param y1 + * the y coordinate of the first point. + * @param x2 + * the x coordinate of the second point. + * @param y2 + * the y coordinate of the second point. + * @return the distance between the two specified points. + */ + public static double distance(double x1, double y1, double x2, double y2) { + return Math.sqrt(distanceSq(x1, y1, x2, y2)); + } + + /** + * Finds the distance between this point and the specified point. + * + * @param px + * the x coordinate of the point. + * @param py + * the y coordinate of the point. + * @return the distance between this point and the specified point. + */ + public double distance(double px, double py) { + return Math.sqrt(distanceSq(px, py)); + } + + /** + * Finds the distance between this point and the specified point. + * + * @param p + * the other point. + * @return the distance between this point and the specified point. + */ + public double distance(Point2D p) { + return Math.sqrt(distanceSq(p)); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } + + @Override + public int hashCode() { + HashCode hash = new HashCode(); + hash.append(getX()); + hash.append(getY()); + return hash.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Point2D) { + Point2D p = (Point2D)obj; + return getX() == p.getX() && getY() == p.getY(); + } + return false; + } +} diff --git a/awt/java/awt/geom/QuadCurve2D.java b/awt/java/awt/geom/QuadCurve2D.java new file mode 100644 index 000000000..7a86a4840 --- /dev/null +++ b/awt/java/awt/geom/QuadCurve2D.java @@ -0,0 +1,918 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.gl.Crossing; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class QuadCurve2D is a Shape that represents a segment of a quadratic + * (Bezier) curve. The curved segment is determined by three points: a start + * point, an end point, and a control point. The line from the control point to + * the starting point gives the tangent to the curve at the starting point, and + * the line from the control point to the end point gives the tangent to the + * curve at the end point. + * + * @since Android 1.0 + */ +public abstract class QuadCurve2D implements Shape, Cloneable { + + /** + * The Class Float is the subclass of QuadCurve2D that has all of its data + * values stored with float-level precision. + * + * @since Android 1.0 + */ + public static class Float extends QuadCurve2D { + + /** + * The x coordinate of the starting point of the curved segment. + */ + public float x1; + + /** + * The y coordinate of the starting point of the curved segment. + */ + public float y1; + + /** + * The x coordinate of the control point. + */ + public float ctrlx; + + /** + * The y coordinate of the control point. + */ + public float ctrly; + + /** + * The x coordinate of the end point of the curved segment. + */ + public float x2; + + /** + * The y coordinate of the end point of the curved segment. + */ + public float y2; + + /** + * Instantiates a new float-valued QuadCurve2D with all coordinate + * values set to zero. + */ + public Float() { + } + + /** + * Instantiates a new float-valued QuadCurve2D with the specified + * coordinate values. + * + * @param x1 + * the x coordinate of the starting point of the curved + * segment. + * @param y1 + * the y coordinate of the starting point of the curved + * segment. + * @param ctrlx + * the x coordinate of the control point. + * @param ctrly + * the y coordinate of the control point. + * @param x2 + * the x coordinate of the end point of the curved segment. + * @param y2 + * the y coordinate of the end point of the curved segment. + */ + public Float(float x1, float y1, float ctrlx, float ctrly, float x2, float y2) { + setCurve(x1, y1, ctrlx, ctrly, x2, y2); + } + + @Override + public double getX1() { + return x1; + } + + @Override + public double getY1() { + return y1; + } + + @Override + public double getCtrlX() { + return ctrlx; + } + + @Override + public double getCtrlY() { + return ctrly; + } + + @Override + public double getX2() { + return x2; + } + + @Override + public double getY2() { + return y2; + } + + @Override + public Point2D getP1() { + return new Point2D.Float(x1, y1); + } + + @Override + public Point2D getCtrlPt() { + return new Point2D.Float(ctrlx, ctrly); + } + + @Override + public Point2D getP2() { + return new Point2D.Float(x2, y2); + } + + @Override + public void setCurve(double x1, double y1, double ctrlx, double ctrly, double x2, double y2) { + this.x1 = (float)x1; + this.y1 = (float)y1; + this.ctrlx = (float)ctrlx; + this.ctrly = (float)ctrly; + this.x2 = (float)x2; + this.y2 = (float)y2; + } + + /** + * Sets the data values of the curve. + * + * @param x1 + * the x coordinate of the starting point of the curved + * segment. + * @param y1 + * the y coordinate of the starting point of the curved + * segment. + * @param ctrlx + * the x coordinate of the control point. + * @param ctrly + * the y coordinate of the control point. + * @param x2 + * the x coordinate of the end point of the curved segment. + * @param y2 + * the y coordinate of the end point of the curved segment. + */ + public void setCurve(float x1, float y1, float ctrlx, float ctrly, float x2, float y2) { + this.x1 = x1; + this.y1 = y1; + this.ctrlx = ctrlx; + this.ctrly = ctrly; + this.x2 = x2; + this.y2 = y2; + } + + public Rectangle2D getBounds2D() { + float rx0 = Math.min(Math.min(x1, x2), ctrlx); + float ry0 = Math.min(Math.min(y1, y2), ctrly); + float rx1 = Math.max(Math.max(x1, x2), ctrlx); + float ry1 = Math.max(Math.max(y1, y2), ctrly); + return new Rectangle2D.Float(rx0, ry0, rx1 - rx0, ry1 - ry0); + } + } + + /** + * The Class Double is the subclass of QuadCurve2D that has all of its data + * values stored with double-level precision. + * + * @since Android 1.0 + */ + public static class Double extends QuadCurve2D { + + /** + * The x coordinate of the starting point of the curved segment. + */ + public double x1; + + /** + * The y coordinate of the starting point of the curved segment. + */ + public double y1; + + /** + * The x coordinate of the control point. + */ + public double ctrlx; + + /** + * The y coordinate of the control point. + */ + public double ctrly; + + /** + * The x coordinate of the end point of the curved segment. + */ + public double x2; + + /** + * The y coordinate of the end point of the curved segment. + */ + public double y2; + + /** + * Instantiates a new double-valued QuadCurve2D with all coordinate + * values set to zero. + */ + public Double() { + } + + /** + * Instantiates a new double-valued QuadCurve2D with the specified + * coordinate values. + * + * @param x1 + * the x coordinate of the starting point of the curved + * segment. + * @param y1 + * the y coordinate of the starting point of the curved + * segment. + * @param ctrlx + * the x coordinate of the control point. + * @param ctrly + * the y coordinate of the control point. + * @param x2 + * the x coordinate of the end point of the curved segment. + * @param y2 + * the y coordinate of the end point of the curved segment. + */ + public Double(double x1, double y1, double ctrlx, double ctrly, double x2, double y2) { + setCurve(x1, y1, ctrlx, ctrly, x2, y2); + } + + @Override + public double getX1() { + return x1; + } + + @Override + public double getY1() { + return y1; + } + + @Override + public double getCtrlX() { + return ctrlx; + } + + @Override + public double getCtrlY() { + return ctrly; + } + + @Override + public double getX2() { + return x2; + } + + @Override + public double getY2() { + return y2; + } + + @Override + public Point2D getP1() { + return new Point2D.Double(x1, y1); + } + + @Override + public Point2D getCtrlPt() { + return new Point2D.Double(ctrlx, ctrly); + } + + @Override + public Point2D getP2() { + return new Point2D.Double(x2, y2); + } + + @Override + public void setCurve(double x1, double y1, double ctrlx, double ctrly, double x2, double y2) { + this.x1 = x1; + this.y1 = y1; + this.ctrlx = ctrlx; + this.ctrly = ctrly; + this.x2 = x2; + this.y2 = y2; + } + + public Rectangle2D getBounds2D() { + double rx0 = Math.min(Math.min(x1, x2), ctrlx); + double ry0 = Math.min(Math.min(y1, y2), ctrly); + double rx1 = Math.max(Math.max(x1, x2), ctrlx); + double ry1 = Math.max(Math.max(y1, y2), ctrly); + return new Rectangle2D.Double(rx0, ry0, rx1 - rx0, ry1 - ry0); + } + } + + /* + * QuadCurve2D path iterator + */ + /** + * The PathIterator for a Quad2D curve. + */ + class Iterator implements PathIterator { + + /** + * The source QuadCurve2D object. + */ + QuadCurve2D c; + + /** + * The path iterator transformation. + */ + AffineTransform t; + + /** + * The current segment index. + */ + int index; + + /** + * Constructs a new QuadCurve2D.Iterator for given curve and + * transformation + * + * @param q + * the source QuadCurve2D object. + * @param t + * the AffineTransform that acts on the coordinates before + * returning them (or null). + */ + Iterator(QuadCurve2D q, AffineTransform t) { + this.c = q; + this.t = t; + } + + public int getWindingRule() { + return WIND_NON_ZERO; + } + + public boolean isDone() { + return (index > 1); + } + + public void next() { + index++; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type; + int count; + if (index == 0) { + type = SEG_MOVETO; + coords[0] = c.getX1(); + coords[1] = c.getY1(); + count = 1; + } else { + type = SEG_QUADTO; + coords[0] = c.getCtrlX(); + coords[1] = c.getCtrlY(); + coords[2] = c.getX2(); + coords[3] = c.getY2(); + count = 2; + } + if (t != null) { + t.transform(coords, 0, coords, 0, count); + } + return type; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type; + int count; + if (index == 0) { + type = SEG_MOVETO; + coords[0] = (float)c.getX1(); + coords[1] = (float)c.getY1(); + count = 1; + } else { + type = SEG_QUADTO; + coords[0] = (float)c.getCtrlX(); + coords[1] = (float)c.getCtrlY(); + coords[2] = (float)c.getX2(); + coords[3] = (float)c.getY2(); + count = 2; + } + if (t != null) { + t.transform(coords, 0, coords, 0, count); + } + return type; + } + + } + + /** + * Instantiates a new quadratic curve. + */ + protected QuadCurve2D() { + } + + /** + * Gets the x coordinate of the starting point. + * + * @return the x coordinate of the starting point. + */ + public abstract double getX1(); + + /** + * Gets the y coordinate of the starting point. + * + * @return the y coordinate of the starting point. + */ + public abstract double getY1(); + + /** + * Gets the starting point. + * + * @return the starting point. + */ + public abstract Point2D getP1(); + + /** + * Gets the x coordinate of the control point. + * + * @return the x coordinate of the control point. + */ + public abstract double getCtrlX(); + + /** + * Gets the y coordinate of the control point. + * + * @return y coordinate of the control point. + */ + public abstract double getCtrlY(); + + /** + * Gets the control point. + * + * @return the control point. + */ + public abstract Point2D getCtrlPt(); + + /** + * Gets the x coordinate of the end point. + * + * @return the x coordinate of the end point. + */ + public abstract double getX2(); + + /** + * Gets the y coordinate of the end point. + * + * @return the y coordinate of the end point. + */ + public abstract double getY2(); + + /** + * Gets the end point. + * + * @return the end point. + */ + public abstract Point2D getP2(); + + /** + * Sets the data of the curve. + * + * @param x1 + * the x coordinate of the starting point of the curved segment. + * @param y1 + * the y coordinate of the starting point of the curved segment. + * @param ctrlx + * the x coordinate of the control point. + * @param ctrly + * the y coordinate of the control point. + * @param x2 + * the x coordinate of the end point of the curved segment. + * @param y2 + * the y coordinate of the end point of the curved segment. + */ + public abstract void setCurve(double x1, double y1, double ctrlx, double ctrly, double x2, + double y2); + + /** + * Sets the data of the curve. + * + * @param p1 + * the starting point of the curved segment. + * @param cp + * the control point. + * @param p2 + * the end point of the curved segment. + * @throws NullPointerException + * if any of the three points is null. + */ + public void setCurve(Point2D p1, Point2D cp, Point2D p2) { + setCurve(p1.getX(), p1.getY(), cp.getX(), cp.getY(), p2.getX(), p2.getY()); + } + + /** + * Sets the data of the curve by reading the data from an array of values. + * The values are read in the same order as the arguments of the method + * {@link QuadCurve2D#setCurve(double, double, double, double, double, double)} + * . + * + * @param coords + * the array of values containing the new coordinates. + * @param offset + * the offset of the data to read within the array. + * @throws ArrayIndexOutOfBoundsException + * if {@code coords.length} < offset + 6. + * @throws NullPointerException + * if the coordinate array is null. + */ + public void setCurve(double[] coords, int offset) { + setCurve(coords[offset + 0], coords[offset + 1], coords[offset + 2], coords[offset + 3], + coords[offset + 4], coords[offset + 5]); + } + + /** + * Sets the data of the curve by reading the data from an array of points. + * The values are read in the same order as the arguments of the method + * {@link QuadCurve2D#setCurve(Point2D, Point2D, Point2D)}. + * + * @param points + * the array of points containing the new coordinates. + * @param offset + * the offset of the data to read within the array. + * @throws ArrayIndexOutOfBoundsException + * if points.length < offset + 3. + * @throws NullPointerException + * if the point array is null. + */ + public void setCurve(Point2D[] points, int offset) { + setCurve(points[offset + 0].getX(), points[offset + 0].getY(), points[offset + 1].getX(), + points[offset + 1].getY(), points[offset + 2].getX(), points[offset + 2].getY()); + } + + /** + * Sets the data of the curve by copying it from another QuadCurve2D. + * + * @param curve + * the curve to copy the data points from. + * @throws NullPointerException + * if the curve is null. + */ + public void setCurve(QuadCurve2D curve) { + setCurve(curve.getX1(), curve.getY1(), curve.getCtrlX(), curve.getCtrlY(), curve.getX2(), + curve.getY2()); + } + + /** + * Gets the square of the distance from the control point to the straight + * line segment connecting the start point and the end point for this curve. + * + * @return the square of the distance from the control point to the straight + * line segment connecting the start point and the end point. + */ + public double getFlatnessSq() { + return Line2D.ptSegDistSq(getX1(), getY1(), getX2(), getY2(), getCtrlX(), getCtrlY()); + } + + /** + * Gets the square of the distance from the control point to the straight + * line segment connecting the start point and the end point. + * + * @param x1 + * the x coordinate of the starting point of the curved segment. + * @param y1 + * the y coordinate of the starting point of the curved segment. + * @param ctrlx + * the x coordinate of the control point. + * @param ctrly + * the y coordinate of the control point. + * @param x2 + * the x coordinate of the end point of the curved segment. + * @param y2 + * the y coordinate of the end point of the curved segment. + * @return the square of the distance from the control point to the straight + * line segment connecting the start point and the end point. + */ + public static double getFlatnessSq(double x1, double y1, double ctrlx, double ctrly, double x2, + double y2) { + return Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx, ctrly); + } + + /** + * Gets the square of the distance from the control point to the straight + * line segment connecting the start point and the end point by reading the + * coordinates of the points from an array of values. The values are read in + * the same order as the arguments of the method + * {@link QuadCurve2D#getFlatnessSq(double, double, double, double, double, double)} + * . + * + * @param coords + * the array of points containing the coordinates to use for the + * calculation + * @param offset + * the offset of the data to read within the array + * @return the square of the distance from the control point to the straight + * line segment connecting the start point and the end point. + * @throws ArrayIndexOutOfBoundsException + * if {@code coords.length} < offset + 6. + * @throws NullPointerException + * if the coordinate array is null. + */ + public static double getFlatnessSq(double coords[], int offset) { + return Line2D.ptSegDistSq(coords[offset + 0], coords[offset + 1], coords[offset + 4], + coords[offset + 5], coords[offset + 2], coords[offset + 3]); + } + + /** + * Gets the distance from the control point to the straight line segment + * connecting the start point and the end point of this QuadCurve2D. + * + * @return the the distance from the control point to the straight line + * segment connecting the start point and the end point of this + * QuadCurve2D. + */ + public double getFlatness() { + return Line2D.ptSegDist(getX1(), getY1(), getX2(), getY2(), getCtrlX(), getCtrlY()); + } + + /** + * Gets the distance from the control point to the straight line segment + * connecting the start point and the end point. + * + * @param x1 + * the x coordinate of the starting point of the curved segment. + * @param y1 + * the y coordinate of the starting point of the curved segment. + * @param ctrlx + * the x coordinate of the control point. + * @param ctrly + * the y coordinate of the control point. + * @param x2 + * the x coordinate of the end point of the curved segment. + * @param y2 + * the y coordinate of the end point of the curved segment. + * @return the the distance from the control point to the straight line + * segment connecting the start point and the end point. + */ + public static double getFlatness(double x1, double y1, double ctrlx, double ctrly, double x2, + double y2) { + return Line2D.ptSegDist(x1, y1, x2, y2, ctrlx, ctrly); + } + + /** + * Gets the the distance from the control point to the straight line segment + * connecting the start point and the end point. The values are read in the + * same order as the arguments of the method + * {@link QuadCurve2D#getFlatness(double, double, double, double, double, double)} + * . + * + * @param coords + * the array of points containing the coordinates to use for the + * calculation. + * @param offset + * the offset of the data to read within the array. + * @return the the distance from the control point to the straight line + * segment connecting the start point and the end point. + * @throws ArrayIndexOutOfBoundsException + * if {code coords.length} < offset + 6. + * @throws NullPointerException + * if the coordinate array is null. + */ + public static double getFlatness(double coords[], int offset) { + return Line2D.ptSegDist(coords[offset + 0], coords[offset + 1], coords[offset + 4], + coords[offset + 5], coords[offset + 2], coords[offset + 3]); + } + + /** + * Creates the data for two quadratic curves by dividing this curve in two. + * The division point is the point on the curve that is closest to this + * curve's control point. The data of this curve is left unchanged. + * + * @param left + * the QuadCurve2D where the left (start) segment's data is + * written. + * @param right + * the QuadCurve2D where the right (end) segment's data is + * written. + * @throws NullPointerException + * if either curve is null. + */ + public void subdivide(QuadCurve2D left, QuadCurve2D right) { + subdivide(this, left, right); + } + + /** + * Creates the data for two quadratic curves by dividing a source curve in + * two. The division point is the point on the curve that is closest to the + * source curve's control point. The data of the source curve is left + * unchanged. + * + * @param src + * the curve that provides the initial data. + * @param left + * the QuadCurve2D where the left (start) segment's data is + * written. + * @param right + * the QuadCurve2D where the right (end) segment's data is + * written. + * @throws NullPointerException + * if one of the curves is null. + */ + public static void subdivide(QuadCurve2D src, QuadCurve2D left, QuadCurve2D right) { + double x1 = src.getX1(); + double y1 = src.getY1(); + double cx = src.getCtrlX(); + double cy = src.getCtrlY(); + double x2 = src.getX2(); + double y2 = src.getY2(); + double cx1 = (x1 + cx) / 2.0; + double cy1 = (y1 + cy) / 2.0; + double cx2 = (x2 + cx) / 2.0; + double cy2 = (y2 + cy) / 2.0; + cx = (cx1 + cx2) / 2.0; + cy = (cy1 + cy2) / 2.0; + if (left != null) { + left.setCurve(x1, y1, cx1, cy1, cx, cy); + } + if (right != null) { + right.setCurve(cx, cy, cx2, cy2, x2, y2); + } + } + + /** + * Creates the data for two quadratic curves by dividing a source curve in + * two. The division point is the point on the curve that is closest to the + * source curve's control point. The data for the three curves is read and + * written from arrays of values in the usual order: x1, y1, cx, cy, x2, y2. + * + * @param src + * the array that gives the data values for the source curve. + * @param srcoff + * the offset in the src array to read the values from. + * @param left + * the array where the coordinates of the start curve should be + * written. + * @param leftOff + * the offset in the left array to start writing the values. + * @param right + * the array where the coordinates of the end curve should be + * written. + * @param rightOff + * the offset in the right array to start writing the values. + * @throws ArrayIndexOutOfBoundsException + * if {@code src.length} < srcoff + 6 or if {@code left.length} + * < leftOff + 6 or if {@code right.length} < rightOff + 6. + * @throws NullPointerException + * if one of the arrays is null. + */ + public static void subdivide(double src[], int srcoff, double left[], int leftOff, + double right[], int rightOff) { + double x1 = src[srcoff + 0]; + double y1 = src[srcoff + 1]; + double cx = src[srcoff + 2]; + double cy = src[srcoff + 3]; + double x2 = src[srcoff + 4]; + double y2 = src[srcoff + 5]; + double cx1 = (x1 + cx) / 2.0; + double cy1 = (y1 + cy) / 2.0; + double cx2 = (x2 + cx) / 2.0; + double cy2 = (y2 + cy) / 2.0; + cx = (cx1 + cx2) / 2.0; + cy = (cy1 + cy2) / 2.0; + if (left != null) { + left[leftOff + 0] = x1; + left[leftOff + 1] = y1; + left[leftOff + 2] = cx1; + left[leftOff + 3] = cy1; + left[leftOff + 4] = cx; + left[leftOff + 5] = cy; + } + if (right != null) { + right[rightOff + 0] = cx; + right[rightOff + 1] = cy; + right[rightOff + 2] = cx2; + right[rightOff + 3] = cy2; + right[rightOff + 4] = x2; + right[rightOff + 5] = y2; + } + } + + /** + * Finds the roots of the quadratic polynomial. This is accomplished by + * finding the (real) values of x that solve the following equation: + * eqn[2]*x*x + eqn[1]*x + eqn[0] = 0. The solutions are written back into + * the array eqn starting from the index 0 in the array. The return value + * tells how many array elements have been changed by this method call. + * + * @param eqn + * an array containing the coefficients of the quadratic + * polynomial to solve. + * @return the number of roots of the quadratic polynomial. + * @throws ArrayIndexOutOfBoundsException + * if {@code eqn.length} < 3. + * @throws NullPointerException + * if the array is null. + */ + public static int solveQuadratic(double eqn[]) { + return solveQuadratic(eqn, eqn); + } + + /** + * Finds the roots of the quadratic polynomial. This is accomplished by + * finding the (real) values of x that solve the following equation: + * eqn[2]*x*x + eqn[1]*x + eqn[0] = 0. The solutions are written into the + * array res starting from the index 0 in the array. The return value tells + * how many array elements have been written by this method call. + * + * @param eqn + * an array containing the coefficients of the quadratic + * polynomial to solve. + * @param res + * the array that this method writes the results into. + * @return the number of roots of the quadratic polynomial. + * @throws ArrayIndexOutOfBoundsException + * if {@code eqn.length} < 3 or if {@code res.length} is less + * than the number of roots. + * @throws NullPointerException + * if either array is null. + */ + public static int solveQuadratic(double eqn[], double res[]) { + return Crossing.solveQuad(eqn, res); + } + + public boolean contains(double px, double py) { + return Crossing.isInsideEvenOdd(Crossing.crossShape(this, px, py)); + } + + public boolean contains(double rx, double ry, double rw, double rh) { + int cross = Crossing.intersectShape(this, rx, ry, rw, rh); + return cross != Crossing.CROSSING && Crossing.isInsideEvenOdd(cross); + } + + public boolean intersects(double rx, double ry, double rw, double rh) { + int cross = Crossing.intersectShape(this, rx, ry, rw, rh); + return cross == Crossing.CROSSING || Crossing.isInsideEvenOdd(cross); + } + + public boolean contains(Point2D p) { + return contains(p.getX(), p.getY()); + } + + public boolean intersects(Rectangle2D r) { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + public boolean contains(Rectangle2D r) { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + public Rectangle getBounds() { + return getBounds2D().getBounds(); + } + + public PathIterator getPathIterator(AffineTransform t) { + return new Iterator(this, t); + } + + public PathIterator getPathIterator(AffineTransform t, double flatness) { + return new FlatteningPathIterator(getPathIterator(t), flatness); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } + +} diff --git a/awt/java/awt/geom/Rectangle2D.java b/awt/java/awt/geom/Rectangle2D.java new file mode 100644 index 000000000..8166134af --- /dev/null +++ b/awt/java/awt/geom/Rectangle2D.java @@ -0,0 +1,824 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.misc.HashCode; + +/** + * The Class Rectangle2D represents a rectangle whose coordinates are given with + * the correct precision to be used with the Graphics2D classes. + * + * @since Android 1.0 + */ +public abstract class Rectangle2D extends RectangularShape { + + /** + * The Constant OUT_LEFT is a mask that is used to indicate that a given + * point is outside the rectangle and to its left. + */ + public static final int OUT_LEFT = 1; + + /** + * The Constant OUT_TOP is a mask that is used to indicate that a given + * point is outside the rectangle and above it. + */ + public static final int OUT_TOP = 2; + + /** + * The Constant OUT_RIGHT is a mask that is used to indicate that a given + * point is outside the rectangle and to its right. + */ + public static final int OUT_RIGHT = 4; + + /** + * The Constant OUT_BOTTOM is a mask that is used to indicate that a given + * point is outside the rectangle and above it. + */ + public static final int OUT_BOTTOM = 8; + + /** + * The Class Float is the subclass of Rectangle2D that represents a + * rectangle whose data values are given as floats (with float-level + * precision). + * + * @since Android 1.0 + */ + public static class Float extends Rectangle2D { + + /** + * The x coordinate of the rectangle's upper left corner. + */ + public float x; + + /** + * The y coordinate of the rectangle's upper left corner. + */ + public float y; + + /** + * The width of the rectangle. + */ + public float width; + + /** + * The height of the rectangle. + */ + public float height; + + /** + * Instantiates a new empty rectangle with float-precision data fields. + */ + public Float() { + } + + /** + * Instantiates a new rectangle with the specified float-precision data. + * + * @param x + * the x coordinate of the rectangle's upper left corner. + * @param y + * the y coordinate of the rectangle's upper left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + */ + public Float(float x, float y, float width, float height) { + setRect(x, y, width, height); + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public double getWidth() { + return width; + } + + @Override + public double getHeight() { + return height; + } + + @Override + public boolean isEmpty() { + return width <= 0.0f || height <= 0.0f; + } + + /** + * Sets the rectangle's data to the given values. + * + * @param x + * the x coordinate of the rectangle's upper left corner. + * @param y + * the y coordinate of the rectangle's upper left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + */ + public void setRect(float x, float y, float width, float height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + @Override + public void setRect(double x, double y, double width, double height) { + this.x = (float)x; + this.y = (float)y; + this.width = (float)width; + this.height = (float)height; + } + + @Override + public void setRect(Rectangle2D r) { + this.x = (float)r.getX(); + this.y = (float)r.getY(); + this.width = (float)r.getWidth(); + this.height = (float)r.getHeight(); + } + + @Override + public int outcode(double px, double py) { + int code = 0; + + if (width <= 0.0f) { + code |= OUT_LEFT | OUT_RIGHT; + } else if (px < x) { + code |= OUT_LEFT; + } else if (px > x + width) { + code |= OUT_RIGHT; + } + + if (height <= 0.0f) { + code |= OUT_TOP | OUT_BOTTOM; + } else if (py < y) { + code |= OUT_TOP; + } else if (py > y + height) { + code |= OUT_BOTTOM; + } + + return code; + } + + @Override + public Rectangle2D getBounds2D() { + return new Float(x, y, width, height); + } + + @Override + public Rectangle2D createIntersection(Rectangle2D r) { + Rectangle2D dst; + if (r instanceof Double) { + dst = new Rectangle2D.Double(); + } else { + dst = new Rectangle2D.Float(); + } + Rectangle2D.intersect(this, r, dst); + return dst; + } + + @Override + public Rectangle2D createUnion(Rectangle2D r) { + Rectangle2D dst; + if (r instanceof Double) { + dst = new Rectangle2D.Double(); + } else { + dst = new Rectangle2D.Float(); + } + Rectangle2D.union(this, r, dst); + return dst; + } + + @Override + public String toString() { + // The output format based on 1.5 release behaviour. It could be + // obtained in the following way + // System.out.println(new Rectangle2D.Float().toString()) + return getClass().getName() + + "[x=" + x + ",y=" + y + ",width=" + width + ",height=" + height + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + } + } + + /** + * The Class Double is the subclass of Rectangle2D that represents a + * rectangle whose data values are given as doubles (with + * double-precision-level precision). + * + * @since Android 1.0 + */ + public static class Double extends Rectangle2D { + + /** + * The x coordinate of the rectangle's upper left corner. + */ + public double x; + + /** + * The y coordinate of the rectangle's upper left corner. + */ + public double y; + + /** + * The width of the rectangle. + */ + public double width; + + /** + * The height of the rectangle. + */ + public double height; + + /** + * Instantiates a new empty rectangle with double-precision data fields. + */ + public Double() { + } + + /** + * Instantiates a new rectangle with the given double values. + * + * @param x + * the x coordinate of the rectangle's upper left corner. + * @param y + * the y coordinate of the rectangle's upper left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + */ + public Double(double x, double y, double width, double height) { + setRect(x, y, width, height); + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public double getWidth() { + return width; + } + + @Override + public double getHeight() { + return height; + } + + @Override + public boolean isEmpty() { + return width <= 0.0 || height <= 0.0; + } + + @Override + public void setRect(double x, double y, double width, double height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + @Override + public void setRect(Rectangle2D r) { + this.x = r.getX(); + this.y = r.getY(); + this.width = r.getWidth(); + this.height = r.getHeight(); + } + + @Override + public int outcode(double px, double py) { + int code = 0; + + if (width <= 0.0) { + code |= OUT_LEFT | OUT_RIGHT; + } else if (px < x) { + code |= OUT_LEFT; + } else if (px > x + width) { + code |= OUT_RIGHT; + } + + if (height <= 0.0) { + code |= OUT_TOP | OUT_BOTTOM; + } else if (py < y) { + code |= OUT_TOP; + } else if (py > y + height) { + code |= OUT_BOTTOM; + } + + return code; + } + + @Override + public Rectangle2D getBounds2D() { + return new Double(x, y, width, height); + } + + @Override + public Rectangle2D createIntersection(Rectangle2D r) { + Rectangle2D dst = new Rectangle2D.Double(); + Rectangle2D.intersect(this, r, dst); + return dst; + } + + @Override + public Rectangle2D createUnion(Rectangle2D r) { + Rectangle2D dest = new Rectangle2D.Double(); + Rectangle2D.union(this, r, dest); + return dest; + } + + @Override + public String toString() { + // The output format based on 1.5 release behaviour. It could be + // obtained in the following way + // System.out.println(new Rectangle2D.Double().toString()) + return getClass().getName() + + "[x=" + x + ",y=" + y + ",width=" + width + ",height=" + height + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + } + } + + /** + * The Class Iterator provides access to the coordinates of the + * Rectangle2D's boundary modified by an AffineTransform. + */ + class Iterator implements PathIterator { + + /** + * The x coordinate of the rectangle's upper left corner. + */ + double x; + + /** + * The y coordinate of the rectangle's upper left corner. + */ + double y; + + /** + * The width of the rectangle. + */ + double width; + + /** + * The height of the rectangle. + */ + double height; + + /** + * The AffineTransform that is used to modify the coordinates that are + * returned by the path iterator. + */ + AffineTransform t; + + /** + * The current segment index. + */ + int index; + + /** + * Constructs a new Rectangle2D.Iterator for given rectangle and + * transformation. + * + * @param r + * the source Rectangle2D object. + * @param at + * the AffineTransform object to apply to the coordinates + * before returning them. + */ + Iterator(Rectangle2D r, AffineTransform at) { + this.x = r.getX(); + this.y = r.getY(); + this.width = r.getWidth(); + this.height = r.getHeight(); + this.t = at; + if (width < 0.0 || height < 0.0) { + index = 6; + } + } + + public int getWindingRule() { + return WIND_NON_ZERO; + } + + public boolean isDone() { + return index > 5; + } + + public void next() { + index++; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + if (index == 5) { + return SEG_CLOSE; + } + int type; + if (index == 0) { + type = SEG_MOVETO; + coords[0] = x; + coords[1] = y; + } else { + type = SEG_LINETO; + switch (index) { + case 1: + coords[0] = x + width; + coords[1] = y; + break; + case 2: + coords[0] = x + width; + coords[1] = y + height; + break; + case 3: + coords[0] = x; + coords[1] = y + height; + break; + case 4: + coords[0] = x; + coords[1] = y; + break; + } + } + if (t != null) { + t.transform(coords, 0, coords, 0, 1); + } + return type; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + if (index == 5) { + return SEG_CLOSE; + } + int type; + if (index == 0) { + coords[0] = (float)x; + coords[1] = (float)y; + type = SEG_MOVETO; + } else { + type = SEG_LINETO; + switch (index) { + case 1: + coords[0] = (float)(x + width); + coords[1] = (float)y; + break; + case 2: + coords[0] = (float)(x + width); + coords[1] = (float)(y + height); + break; + case 3: + coords[0] = (float)x; + coords[1] = (float)(y + height); + break; + case 4: + coords[0] = (float)x; + coords[1] = (float)y; + break; + } + } + if (t != null) { + t.transform(coords, 0, coords, 0, 1); + } + return type; + } + + } + + /** + * Instantiates a new Rectangle2D. + */ + protected Rectangle2D() { + } + + /** + * Sets the rectangle's location and dimension. + * + * @param x + * the x coordinate of the rectangle's upper left corner. + * @param y + * the y coordinate of the rectangle's upper left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + */ + public abstract void setRect(double x, double y, double width, double height); + + /** + * Gets the location of the point with respect to the rectangle and packs + * the information into a single integer using the bitmasks + * {@link Rectangle2D#OUT_LEFT}, {@link Rectangle2D#OUT_RIGHT}, + * {@link Rectangle2D#OUT_TOP}, and {@link Rectangle2D#OUT_BOTTOM}. If the + * rectangle has zero or negative width, then every point is regarded as + * being both to the left and to the right of the rectangle. Similarly, if + * the height is zero or negative then all points are considered to be both + * both above and below it. + * + * @param x + * the x coordinate of the point to check. + * @param y + * the y coordinate of the point to check. + * @return the point's location with respect to the rectangle. + */ + public abstract int outcode(double x, double y); + + /** + * Creates an new rectangle that is the intersection of this rectangle with + * the given rectangle. The resulting rectangle may be empty. The data of + * this rectangle is left unchanged. + * + * @param r + * the rectangle to intersect with this rectangle. + * @return the new rectangle given by intersection. + */ + public abstract Rectangle2D createIntersection(Rectangle2D r); + + /** + * Creates an new rectangle that is the union of this rectangle with the + * given rectangle. The new rectangle is the smallest rectangle which + * contains both this rectangle and the rectangle specified as a parameter. + * The data of this rectangle is left unchanged. + * + * @param r + * the rectangle to combine with this rectangle. + * @return the new rectangle given by union. + */ + public abstract Rectangle2D createUnion(Rectangle2D r); + + /** + * Sets the data of this rectangle to match the data of the given rectangle. + * + * @param r + * the rectangle whose data is to be copied into this rectangle's + * fields. + */ + public void setRect(Rectangle2D r) { + setRect(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + @Override + public void setFrame(double x, double y, double width, double height) { + setRect(x, y, width, height); + } + + public Rectangle2D getBounds2D() { + return (Rectangle2D)clone(); + } + + /** + * Determines whether any part of the line segment between (and including) + * the two given points touches any part of the rectangle, including its + * boundary. + * + * @param x1 + * the x coordinate of one of the points that determines the line + * segment to test. + * @param y1 + * the y coordinate of one of the points that determines the line + * segment to test. + * @param x2 + * the x coordinate of one of the points that determines the line + * segment to test. + * @param y2 + * the y coordinate of one of the points that determines the line + * segment to test. + * @return true, if at least one point of the line segment between the two + * points matches any point of the interior of the rectangle or the + * rectangle's boundary. + */ + public boolean intersectsLine(double x1, double y1, double x2, double y2) { + double rx1 = getX(); + double ry1 = getY(); + double rx2 = rx1 + getWidth(); + double ry2 = ry1 + getHeight(); + return (rx1 <= x1 && x1 <= rx2 && ry1 <= y1 && y1 <= ry2) + || (rx1 <= x2 && x2 <= rx2 && ry1 <= y2 && y2 <= ry2) + || Line2D.linesIntersect(rx1, ry1, rx2, ry2, x1, y1, x2, y2) + || Line2D.linesIntersect(rx2, ry1, rx1, ry2, x1, y1, x2, y2); + } + + /** + * Determines whether any part of the specified line segment touches any + * part of the rectangle, including its boundary. + * + * @param l + * the line segment to test. + * @return true, if at least one point of the given line segment matches any + * point of the interior of the rectangle or the rectangle's + * boundary. + */ + public boolean intersectsLine(Line2D l) { + return intersectsLine(l.getX1(), l.getY1(), l.getX2(), l.getY2()); + } + + /** + * Gets the location of the point with respect to the rectangle and packs + * the information into a single integer using the bitmasks + * {@link Rectangle2D#OUT_LEFT}, {@link Rectangle2D#OUT_RIGHT}, + * {@link Rectangle2D#OUT_TOP}, and {@link Rectangle2D#OUT_BOTTOM}. If the + * rectangle has zero or negative width, then every point is regarded as + * being both to the left and to the right of the rectangle. Similarly, if + * the height is zero or negative then all points are considered to be both + * both above and below it. + * + * @param p + * the point to check. + * @return the point's location with respect to the rectangle. + */ + public int outcode(Point2D p) { + return outcode(p.getX(), p.getY()); + } + + public boolean contains(double x, double y) { + if (isEmpty()) { + return false; + } + + double x1 = getX(); + double y1 = getY(); + double x2 = x1 + getWidth(); + double y2 = y1 + getHeight(); + + return x1 <= x && x < x2 && y1 <= y && y < y2; + } + + public boolean intersects(double x, double y, double width, double height) { + if (isEmpty() || width <= 0.0 || height <= 0.0) { + return false; + } + + double x1 = getX(); + double y1 = getY(); + double x2 = x1 + getWidth(); + double y2 = y1 + getHeight(); + + return x + width > x1 && x < x2 && y + height > y1 && y < y2; + } + + public boolean contains(double x, double y, double width, double height) { + if (isEmpty() || width <= 0.0 || height <= 0.0) { + return false; + } + + double x1 = getX(); + double y1 = getY(); + double x2 = x1 + getWidth(); + double y2 = y1 + getHeight(); + + return x1 <= x && x + width <= x2 && y1 <= y && y + height <= y2; + } + + /** + * Changes the data values of the destination rectangle to match the + * intersection of the two source rectangles, leaving the two source + * rectangles unchanged. The resulting rectangle may be empty. + * + * @param src1 + * one of the two source rectangles giving the data to intersect. + * @param src2 + * one of the two source rectangles giving the data to intersect. + * @param dst + * the destination object where the data of the intersection is + * written. + */ + public static void intersect(Rectangle2D src1, Rectangle2D src2, Rectangle2D dst) { + double x1 = Math.max(src1.getMinX(), src2.getMinX()); + double y1 = Math.max(src1.getMinY(), src2.getMinY()); + double x2 = Math.min(src1.getMaxX(), src2.getMaxX()); + double y2 = Math.min(src1.getMaxY(), src2.getMaxY()); + dst.setFrame(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Changes the data values of the destination rectangle to match the union + * of the two source rectangles, leaving the two source rectangles + * unchanged. The union is the smallest rectangle that completely covers the + * two source rectangles. + * + * @param src1 + * one of the two source rectangles giving the data. + * @param src2 + * one of the two source rectangles giving the data. + * @param dst + * the destination object where the data of the union is written. + */ + public static void union(Rectangle2D src1, Rectangle2D src2, Rectangle2D dst) { + double x1 = Math.min(src1.getMinX(), src2.getMinX()); + double y1 = Math.min(src1.getMinY(), src2.getMinY()); + double x2 = Math.max(src1.getMaxX(), src2.getMaxX()); + double y2 = Math.max(src1.getMaxY(), src2.getMaxY()); + dst.setFrame(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Enlarges the rectangle so that it includes the given point. + * + * @param x + * the x coordinate of the new point to be covered by the + * rectangle. + * @param y + * the y coordinate of the new point to be covered by the + * rectangle. + */ + public void add(double x, double y) { + double x1 = Math.min(getMinX(), x); + double y1 = Math.min(getMinY(), y); + double x2 = Math.max(getMaxX(), x); + double y2 = Math.max(getMaxY(), y); + setRect(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Enlarges the rectangle so that it includes the given point. + * + * @param p + * the new point to be covered by the rectangle. + */ + public void add(Point2D p) { + add(p.getX(), p.getY()); + } + + /** + * Enlarges the rectangle so that it covers the given rectangle. + * + * @param r + * the new rectangle to be covered by this rectangle. + */ + public void add(Rectangle2D r) { + union(this, r, this); + } + + public PathIterator getPathIterator(AffineTransform t) { + return new Iterator(this, t); + } + + @Override + public PathIterator getPathIterator(AffineTransform t, double flatness) { + return new Iterator(this, t); + } + + @Override + public int hashCode() { + HashCode hash = new HashCode(); + hash.append(getX()); + hash.append(getY()); + hash.append(getWidth()); + hash.append(getHeight()); + return hash.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Rectangle2D) { + Rectangle2D r = (Rectangle2D)obj; + return getX() == r.getX() && getY() == r.getY() && getWidth() == r.getWidth() + && getHeight() == r.getHeight(); + } + return false; + } + +} diff --git a/awt/java/awt/geom/RectangularShape.java b/awt/java/awt/geom/RectangularShape.java new file mode 100644 index 000000000..0b0d05cad --- /dev/null +++ b/awt/java/awt/geom/RectangularShape.java @@ -0,0 +1,297 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; + +/** + * The Class RectangularShape represents a Shape whose data is (at least + * partially) described by a rectangular frame. This includes shapes which are + * obviously rectangular (such as Rectangle2D) as well as shapes like Arc2D + * which are largely determined by the rectangle they fit inside. + * + * @since Android 1.0 + */ +public abstract class RectangularShape implements Shape, Cloneable { + + /** + * Instantiates a new rectangular shape. + */ + protected RectangularShape() { + } + + /** + * Gets the x coordinate of the upper left corner of the rectangle. + * + * @return the x coordinate of the upper left corner of the rectangle. + */ + public abstract double getX(); + + /** + * Gets the y coordinate of the upper left corner of the rectangle. + * + * @return the y coordinate of the upper left corner of the rectangle. + */ + public abstract double getY(); + + /** + * Gets the width of the rectangle. + * + * @return the width of the rectangle. + */ + public abstract double getWidth(); + + /** + * Gets the height of the rectangle. + * + * @return the height of the rectangle. + */ + public abstract double getHeight(); + + /** + * Checks if this is an empty rectangle: one with zero as its width or + * height. + * + * @return true, if the width or height is empty. + */ + public abstract boolean isEmpty(); + + /** + * Sets the data for the bounding rectangle in terms of double values. + * + * @param x + * the x coordinate of the upper left corner of the rectangle. + * @param y + * the y coordinate of the upper left corner of the rectangle. + * @param w + * the width of the rectangle. + * @param h + * the height of the rectangle. + */ + public abstract void setFrame(double x, double y, double w, double h); + + /** + * Gets the minimum x value of the bounding rectangle (the x coordinate of + * the upper left corner of the rectangle). + * + * @return the minimum x value of the bounding rectangle. + */ + public double getMinX() { + return getX(); + } + + /** + * Gets the minimum y value of the bounding rectangle (the y coordinate of + * the upper left corner of the rectangle). + * + * @return the minimum y value of the bounding rectangle. + */ + public double getMinY() { + return getY(); + } + + /** + * Gets the maximum x value of the bounding rectangle (the x coordinate of + * the upper left corner of the rectangle plus the rectangle's width). + * + * @return the maximum x value of the bounding rectangle. + */ + public double getMaxX() { + return getX() + getWidth(); + } + + /** + * Gets the maximum y value of the bounding rectangle (the y coordinate of + * the upper left corner of the rectangle plus the rectangle's height). + * + * @return the maximum y value of the bounding rectangle. + */ + public double getMaxY() { + return getY() + getHeight(); + } + + /** + * Gets the x coordinate of the center of the rectangle. + * + * @return the x coordinate of the center of the rectangle. + */ + public double getCenterX() { + return getX() + getWidth() / 2.0; + } + + /** + * Gets the y coordinate of the center of the rectangle. + * + * @return the y coordinate of the center of the rectangle. + */ + public double getCenterY() { + return getY() + getHeight() / 2.0; + } + + /** + * Places the rectangle's size and location data in a new Rectangle2D object + * and returns it. + * + * @return the bounding rectangle as a new Rectangle2D object. + */ + public Rectangle2D getFrame() { + return new Rectangle2D.Double(getX(), getY(), getWidth(), getHeight()); + } + + /** + * Sets the bounding rectangle in terms of a Point2D which gives its upper + * left corner and a Dimension2D object giving its width and height. + * + * @param loc + * the new upper left corner coordinate. + * @param size + * the new size dimensions. + */ + public void setFrame(Point2D loc, Dimension2D size) { + setFrame(loc.getX(), loc.getY(), size.getWidth(), size.getHeight()); + } + + /** + * Sets the bounding rectangle to match the data contained in the specified + * Rectangle2D. + * + * @param r + * the rectangle that gives the new frame data. + */ + public void setFrame(Rectangle2D r) { + setFrame(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Sets the framing rectangle given two opposite corners. Any two corners + * may be used in any order as long as they are diagonally opposite one + * another. + * + * @param x1 + * the x coordinate of one of the corner points. + * @param y1 + * the y coordinate of one of the corner points. + * @param x2 + * the x coordinate of the other corner point. + * @param y2 + * the y coordinate of the other corner point. + */ + public void setFrameFromDiagonal(double x1, double y1, double x2, double y2) { + double rx, ry, rw, rh; + if (x1 < x2) { + rx = x1; + rw = x2 - x1; + } else { + rx = x2; + rw = x1 - x2; + } + if (y1 < y2) { + ry = y1; + rh = y2 - y1; + } else { + ry = y2; + rh = y1 - y2; + } + setFrame(rx, ry, rw, rh); + } + + /** + * Sets the framing rectangle given two opposite corners. Any two corners + * may be used in any order as long as they are diagonally opposite one + * another. + * + * @param p1 + * one of the corner points. + * @param p2 + * the other corner point. + */ + public void setFrameFromDiagonal(Point2D p1, Point2D p2) { + setFrameFromDiagonal(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Sets the framing rectangle given the center point and one corner. Any + * corner may be used. + * + * @param centerX + * the x coordinate of the center point. + * @param centerY + * the y coordinate of the center point. + * @param cornerX + * the x coordinate of one of the corner points. + * @param cornerY + * the y coordinate of one of the corner points. + */ + public void setFrameFromCenter(double centerX, double centerY, double cornerX, double cornerY) { + double width = Math.abs(cornerX - centerX); + double height = Math.abs(cornerY - centerY); + setFrame(centerX - width, centerY - height, width * 2.0, height * 2.0); + } + + /** + * Sets the framing rectangle given the center point and one corner. Any + * corner may be used. + * + * @param center + * the center point. + * @param corner + * a corner point. + */ + public void setFrameFromCenter(Point2D center, Point2D corner) { + setFrameFromCenter(center.getX(), center.getY(), corner.getX(), corner.getY()); + } + + public boolean contains(Point2D point) { + return contains(point.getX(), point.getY()); + } + + public boolean intersects(Rectangle2D rect) { + return intersects(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + } + + public boolean contains(Rectangle2D rect) { + return contains(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + } + + public Rectangle getBounds() { + int x1 = (int)Math.floor(getMinX()); + int y1 = (int)Math.floor(getMinY()); + int x2 = (int)Math.ceil(getMaxX()); + int y2 = (int)Math.ceil(getMaxY()); + return new Rectangle(x1, y1, x2 - x1, y2 - y1); + } + + public PathIterator getPathIterator(AffineTransform t, double flatness) { + return new FlatteningPathIterator(getPathIterator(t), flatness); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } + +} diff --git a/awt/java/awt/geom/RoundRectangle2D.java b/awt/java/awt/geom/RoundRectangle2D.java new file mode 100644 index 000000000..8fbddd64d --- /dev/null +++ b/awt/java/awt/geom/RoundRectangle2D.java @@ -0,0 +1,635 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.geom; + +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class RoundRectangle2D describes a rectangle with rounded corners with + * high-precision data that is appropriate for geometric operations. + * + * @since Android 1.0 + */ +public abstract class RoundRectangle2D extends RectangularShape { + + /** + * The Class Float is the subclass of RoundRectangle2D that has all of its + * data values stored with float-level precision. + * + * @since Android 1.0 + */ + public static class Float extends RoundRectangle2D { + + /** + * The x coordinate of the rectangle's upper left corner. + */ + public float x; + + /** + * The y coordinate of the rectangle's upper left corner. + */ + public float y; + + /** + * The width of the rectangle. + */ + public float width; + + /** + * The height of the rectangle. + */ + public float height; + + /** + * The arc width of the rounded corners. + */ + public float arcwidth; + + /** + * The arc height of the rounded corners. + */ + public float archeight; + + /** + * Instantiates a new float-valued RoundRectangle2D with its data-values + * set to zero. + */ + public Float() { + } + + /** + * Instantiates a new float-valued RoundRectangle2D with the specified + * data values. + * + * @param x + * the x coordinate of the rectangle's upper left corner. + * @param y + * the y coordinate of the rectangle's upper left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + * @param arcwidth + * the arc width of the rounded corners. + * @param archeight + * the arc height of the rounded corners. + */ + public Float(float x, float y, float width, float height, float arcwidth, float archeight) { + setRoundRect(x, y, width, height, arcwidth, archeight); + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public double getWidth() { + return width; + } + + @Override + public double getHeight() { + return height; + } + + @Override + public double getArcWidth() { + return arcwidth; + } + + @Override + public double getArcHeight() { + return archeight; + } + + @Override + public boolean isEmpty() { + return width <= 0.0f || height <= 0.0f; + } + + /** + * Sets the data of the round rectangle. + * + * @param x + * the x coordinate of the rectangle's upper left corner. + * @param y + * the y coordinate of the rectangle's upper left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + * @param arcwidth + * the arc width of the rounded corners. + * @param archeight + * the arc height of the rounded corners. + */ + public void setRoundRect(float x, float y, float width, float height, float arcwidth, + float archeight) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.arcwidth = arcwidth; + this.archeight = archeight; + } + + @Override + public void setRoundRect(double x, double y, double width, double height, double arcwidth, + double archeight) { + this.x = (float)x; + this.y = (float)y; + this.width = (float)width; + this.height = (float)height; + this.arcwidth = (float)arcwidth; + this.archeight = (float)archeight; + } + + @Override + public void setRoundRect(RoundRectangle2D rr) { + this.x = (float)rr.getX(); + this.y = (float)rr.getY(); + this.width = (float)rr.getWidth(); + this.height = (float)rr.getHeight(); + this.arcwidth = (float)rr.getArcWidth(); + this.archeight = (float)rr.getArcHeight(); + } + + public Rectangle2D getBounds2D() { + return new Rectangle2D.Float(x, y, width, height); + } + } + + /** + * The Class Double is the subclass of RoundRectangle2D that has all of its + * data values stored with double-level precision. + * + * @since Android 1.0 + */ + public static class Double extends RoundRectangle2D { + + /** + * The x coordinate of the rectangle's upper left corner. + */ + public double x; + + /** + * The y coordinate of the rectangle's upper left corner. + */ + public double y; + + /** + * The width of the rectangle. + */ + public double width; + + /** + * The height of the rectangle. + */ + public double height; + + /** + * The arc width of the rounded corners. + */ + public double arcwidth; + + /** + * The arc height of the rounded corners. + */ + public double archeight; + + /** + * Instantiates a new double-valued RoundRectangle2D with its + * data-values set to zero. + */ + public Double() { + } + + /** + * Instantiates a new double-valued RoundRectangle2D with the specified + * data values. + * + * @param x + * the x coordinate of the rectangle's upper left corner. + * @param y + * the y coordinate of the rectangle's upper left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + * @param arcwidth + * the arc width of the rounded corners. + * @param archeight + * the arc height of the rounded corners. + */ + public Double(double x, double y, double width, double height, double arcwidth, + double archeight) { + setRoundRect(x, y, width, height, arcwidth, archeight); + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public double getWidth() { + return width; + } + + @Override + public double getHeight() { + return height; + } + + @Override + public double getArcWidth() { + return arcwidth; + } + + @Override + public double getArcHeight() { + return archeight; + } + + @Override + public boolean isEmpty() { + return width <= 0.0 || height <= 0.0; + } + + @Override + public void setRoundRect(double x, double y, double width, double height, double arcwidth, + double archeight) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.arcwidth = arcwidth; + this.archeight = archeight; + } + + @Override + public void setRoundRect(RoundRectangle2D rr) { + this.x = rr.getX(); + this.y = rr.getY(); + this.width = rr.getWidth(); + this.height = rr.getHeight(); + this.arcwidth = rr.getArcWidth(); + this.archeight = rr.getArcHeight(); + } + + public Rectangle2D getBounds2D() { + return new Rectangle2D.Double(x, y, width, height); + } + } + + /* + * RoundRectangle2D path iterator + */ + /** + * The subclass of PathIterator to traverse a RoundRectangle2D. + */ + class Iterator implements PathIterator { + + /* + * Path for round corners generated the same way as Ellipse2D + */ + + /** + * The coefficient to calculate control points of Bezier curves. + */ + double u = 0.5 - 2.0 / 3.0 * (Math.sqrt(2.0) - 1.0); + + /** + * The points coordinates calculation table. + */ + double points[][] = { + { + 0.0, 0.5, 0.0, 0.0 + }, // MOVETO + { + 1.0, -0.5, 0.0, 0.0 + }, // LINETO + { + 1.0, -u, 0.0, 0.0, // CUBICTO + 1.0, 0.0, 0.0, u, 1.0, 0.0, 0.0, 0.5 + }, { + 1.0, 0.0, 1.0, -0.5 + }, // LINETO + { + 1.0, 0.0, 1.0, -u, // CUBICTO + 1.0, -u, 1.0, 0.0, 1.0, -0.5, 1.0, 0.0 + }, { + 0.0, 0.5, 1.0, 0.0 + }, // LINETO + { + 0.0, u, 1.0, 0.0, // CUBICTO + 0.0, 0.0, 1.0, -u, 0.0, 0.0, 1.0, -0.5 + }, { + 0.0, 0.0, 0.0, 0.5 + }, // LINETO + { + 0.0, 0.0, 0.0, u, // CUBICTO + 0.0, u, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0 + } + }; + + /** + * The segment types correspond to points array. + */ + int types[] = { + SEG_MOVETO, SEG_LINETO, SEG_CUBICTO, SEG_LINETO, SEG_CUBICTO, SEG_LINETO, + SEG_CUBICTO, SEG_LINETO, SEG_CUBICTO + }; + + /** + * The x coordinate of left-upper corner of the round rectangle bounds. + */ + double x; + + /** + * The y coordinate of left-upper corner of the round rectangle bounds. + */ + double y; + + /** + * The width of the round rectangle bounds. + */ + double width; + + /** + * The height of the round rectangle bounds. + */ + double height; + + /** + * The width of arc corners of the round rectangle. + */ + double aw; + + /** + * The height of arc corners of the round rectangle. + */ + double ah; + + /** + * The path iterator transformation. + */ + AffineTransform t; + + /** + * The current segment index. + */ + int index; + + /** + * Constructs a new RoundRectangle2D.Iterator for given round rectangle + * and transformation. + * + * @param rr + * - the source RoundRectangle2D object + * @param at + * - the AffineTransform object to apply rectangle path + */ + Iterator(RoundRectangle2D rr, AffineTransform at) { + this.x = rr.getX(); + this.y = rr.getY(); + this.width = rr.getWidth(); + this.height = rr.getHeight(); + this.aw = Math.min(width, rr.getArcWidth()); + this.ah = Math.min(height, rr.getArcHeight()); + this.t = at; + if (width < 0.0 || height < 0.0 || aw < 0.0 || ah < 0.0) { + index = points.length; + } + } + + public int getWindingRule() { + return WIND_NON_ZERO; + } + + public boolean isDone() { + return index > points.length; + } + + public void next() { + index++; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + if (index == points.length) { + return SEG_CLOSE; + } + int j = 0; + double p[] = points[index]; + for (int i = 0; i < p.length; i += 4) { + coords[j++] = x + p[i + 0] * width + p[i + 1] * aw; + coords[j++] = y + p[i + 2] * height + p[i + 3] * ah; + } + if (t != null) { + t.transform(coords, 0, coords, 0, j / 2); + } + return types[index]; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + // awt.4B=Iterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + if (index == points.length) { + return SEG_CLOSE; + } + int j = 0; + double p[] = points[index]; + for (int i = 0; i < p.length; i += 4) { + coords[j++] = (float)(x + p[i + 0] * width + p[i + 1] * aw); + coords[j++] = (float)(y + p[i + 2] * height + p[i + 3] * ah); + } + if (t != null) { + t.transform(coords, 0, coords, 0, j / 2); + } + return types[index]; + } + + } + + /** + * Instantiates a new RoundRectangle2D. + */ + protected RoundRectangle2D() { + } + + /** + * Gets the arc width. + * + * @return the arc width. + */ + public abstract double getArcWidth(); + + /** + * Gets the arc height. + * + * @return the arc height. + */ + public abstract double getArcHeight(); + + /** + * Sets the data of the RoundRectangle2D. + * + * @param x + * the x coordinate of the rectangle's upper left corner. + * @param y + * the y coordinate of the rectangle's upper left corner. + * @param width + * the width of the rectangle. + * @param height + * the height of the rectangle. + * @param arcWidth + * the arc width of the rounded corners. + * @param arcHeight + * the arc height of the rounded corners. + */ + public abstract void setRoundRect(double x, double y, double width, double height, + double arcWidth, double arcHeight); + + /** + * Sets the data of the RoundRectangle2D by copying the values from an + * existing RoundRectangle2D. + * + * @param rr + * the round rectangle to copy the data from. + * @throws NullPointerException + * if rr is null. + */ + public void setRoundRect(RoundRectangle2D rr) { + setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(), rr.getArcWidth(), rr + .getArcHeight()); + } + + @Override + public void setFrame(double x, double y, double width, double height) { + setRoundRect(x, y, width, height, getArcWidth(), getArcHeight()); + } + + public boolean contains(double px, double py) { + if (isEmpty()) { + return false; + } + + double rx1 = getX(); + double ry1 = getY(); + double rx2 = rx1 + getWidth(); + double ry2 = ry1 + getHeight(); + + if (px < rx1 || px >= rx2 || py < ry1 || py >= ry2) { + return false; + } + + double aw = getArcWidth() / 2.0; + double ah = getArcHeight() / 2.0; + + double cx, cy; + + if (px < rx1 + aw) { + cx = rx1 + aw; + } else if (px > rx2 - aw) { + cx = rx2 - aw; + } else { + return true; + } + + if (py < ry1 + ah) { + cy = ry1 + ah; + } else if (py > ry2 - ah) { + cy = ry2 - ah; + } else { + return true; + } + + px = (px - cx) / aw; + py = (py - cy) / ah; + return px * px + py * py <= 1.0; + } + + public boolean intersects(double rx, double ry, double rw, double rh) { + if (isEmpty() || rw <= 0.0 || rh <= 0.0) { + return false; + } + + double x1 = getX(); + double y1 = getY(); + double x2 = x1 + getWidth(); + double y2 = y1 + getHeight(); + + double rx1 = rx; + double ry1 = ry; + double rx2 = rx + rw; + double ry2 = ry + rh; + + if (rx2 < x1 || x2 < rx1 || ry2 < y1 || y2 < ry1) { + return false; + } + + double cx = (x1 + x2) / 2.0; + double cy = (y1 + y2) / 2.0; + + double nx = cx < rx1 ? rx1 : (cx > rx2 ? rx2 : cx); + double ny = cy < ry1 ? ry1 : (cy > ry2 ? ry2 : cy); + + return contains(nx, ny); + } + + public boolean contains(double rx, double ry, double rw, double rh) { + if (isEmpty() || rw <= 0.0 || rh <= 0.0) { + return false; + } + + double rx1 = rx; + double ry1 = ry; + double rx2 = rx + rw; + double ry2 = ry + rh; + + return contains(rx1, ry1) && contains(rx2, ry1) && contains(rx2, ry2) && contains(rx1, ry2); + } + + public PathIterator getPathIterator(AffineTransform at) { + return new Iterator(this, at); + } + +} diff --git a/awt/java/awt/geom/package.html b/awt/java/awt/geom/package.html new file mode 100644 index 000000000..e3a236ee3 --- /dev/null +++ b/awt/java/awt/geom/package.html @@ -0,0 +1,8 @@ + + +

+ This package contains classes and interfaces related to Java2D shapes and geometry. +

+ @since Android 1.0 + + diff --git a/awt/java/awt/im/InputContext.java b/awt/java/awt/im/InputContext.java new file mode 100644 index 000000000..cce514842 --- /dev/null +++ b/awt/java/awt/im/InputContext.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.im; + +import java.awt.AWTEvent; +//???AWT: import java.awt.Component; +import java.util.Locale; + +import org.apache.harmony.awt.im.InputMethodContext; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class InputContext { + protected InputContext() { + } + + public static InputContext getInstance() { + return new InputMethodContext(); + } + + public void dispatchEvent(AWTEvent event) { + } + + public void dispose() { + } + + public void endComposition() { + } + + public Object getInputMethodControlObject() { + return null; + } + + public Locale getLocale() { + return null; + } + + public boolean isCompositionEnabled() { + return false; + } + + public void reconvert() { + } + + //???AWT + /* + public void removeNotify(Component client) { + } + */ + + public boolean selectInputMethod(Locale locale) { + return false; + } + + public void setCharacterSubsets(Character.Subset[] subsets) { + } + + public void setCompositionEnabled(boolean enable) { + } +} + diff --git a/awt/java/awt/im/InputMethodHighlight.java b/awt/java/awt/im/InputMethodHighlight.java new file mode 100644 index 000000000..865d47c80 --- /dev/null +++ b/awt/java/awt/im/InputMethodHighlight.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ +package java.awt.im; + +import java.util.Map; +import java.awt.font.TextAttribute; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public class InputMethodHighlight { + + public static final int RAW_TEXT = 0; + + public static final int CONVERTED_TEXT = 1; + + public static final InputMethodHighlight + UNSELECTED_RAW_TEXT_HIGHLIGHT = new InputMethodHighlight(false, RAW_TEXT); + + public static final InputMethodHighlight + SELECTED_RAW_TEXT_HIGHLIGHT = new InputMethodHighlight(true, RAW_TEXT); + + public static final InputMethodHighlight + UNSELECTED_CONVERTED_TEXT_HIGHLIGHT = + new InputMethodHighlight(false, CONVERTED_TEXT); + + public static final InputMethodHighlight + SELECTED_CONVERTED_TEXT_HIGHLIGHT = + new InputMethodHighlight(true, CONVERTED_TEXT); + + private boolean selected; + private int state; + private int variation; + private Map style; + + public InputMethodHighlight(boolean selected, int state, int variation) { + this(selected, state, variation, null); + } + + public InputMethodHighlight(boolean selected, int state, + int variation, Map style) { + if ((state != RAW_TEXT) && (state != CONVERTED_TEXT)) { + // awt.20B=unknown input method highlight state + throw new IllegalArgumentException(Messages.getString("awt.20B")); //$NON-NLS-1$ + } + this.selected = selected; + this.state = state; + this.variation = variation; + this.style = style; + } + + public InputMethodHighlight(boolean selected, int state) { + this(selected, state, 0, null); + } + + public int getState() { + return state; + } + + public Map getStyle() { + return style; + } + + public int getVariation() { + return variation; + } + + public boolean isSelected() { + return selected; + } +} + diff --git a/awt/java/awt/im/InputMethodRequests.java b/awt/java/awt/im/InputMethodRequests.java new file mode 100644 index 000000000..b12d397b5 --- /dev/null +++ b/awt/java/awt/im/InputMethodRequests.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.im; + +import java.awt.Rectangle; +import java.awt.font.TextHitInfo; +import java.text.AttributedCharacterIterator; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface InputMethodRequests { + + public AttributedCharacterIterator cancelLatestCommittedText(AttributedCharacterIterator.Attribute[] attributes); + + public AttributedCharacterIterator getCommittedText(int beginIndex, int endIndex, AttributedCharacterIterator.Attribute[] attributes); + + public int getCommittedTextLength(); + + public int getInsertPositionOffset(); + + public TextHitInfo getLocationOffset(int x, int y); + + public AttributedCharacterIterator getSelectedText(AttributedCharacterIterator.Attribute[] attributes); + + public Rectangle getTextLocation(TextHitInfo offset); +} + diff --git a/awt/java/awt/im/InputSubset.java b/awt/java/awt/im/InputSubset.java new file mode 100644 index 000000000..708881eae --- /dev/null +++ b/awt/java/awt/im/InputSubset.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ +package java.awt.im; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public final class InputSubset extends Character.Subset { + + public static final InputSubset LATIN = new InputSubset("LATIN"); //$NON-NLS-1$ + + public static final InputSubset + LATIN_DIGITS = new InputSubset("LATIN_DIGITS"); //$NON-NLS-1$ + + public static final InputSubset + TRADITIONAL_HANZI = new InputSubset("TRADITIONAL_HANZI"); //$NON-NLS-1$ + + public static final InputSubset + SIMPLIFIED_HANZI = new InputSubset("SIMPLIFIED_HANZI"); //$NON-NLS-1$ + + public static final InputSubset KANJI = new InputSubset("KANJI"); //$NON-NLS-1$ + + public static final InputSubset HANJA = new InputSubset("HANJA"); //$NON-NLS-1$ + + public static final InputSubset + HALFWIDTH_KATAKANA = new InputSubset("HALFWIDTH_KATAKANA"); //$NON-NLS-1$ + + public static final InputSubset + FULLWIDTH_LATIN = new InputSubset("FULLWIDTH_LATIN"); //$NON-NLS-1$ + + public static final InputSubset + FULLWIDTH_DIGITS = new InputSubset("FULLWIDTH_DIGITS"); //$NON-NLS-1$ + + private InputSubset(String name) { + super(name); + } +} + diff --git a/awt/java/awt/im/spi/InputMethod.java b/awt/java/awt/im/spi/InputMethod.java new file mode 100644 index 000000000..67a88346c --- /dev/null +++ b/awt/java/awt/im/spi/InputMethod.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.im.spi; + +import java.awt.AWTEvent; +import java.awt.Rectangle; +import java.util.Locale; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface InputMethod { + + public void activate(); + + public void deactivate(boolean isTemporary); + + public void dispatchEvent(AWTEvent event); + + public void dispose(); + + public void endComposition(); + + public Object getControlObject(); + + public Locale getLocale(); + + public void hideWindows(); + + public boolean isCompositionEnabled(); + + public void notifyClientWindowChange(Rectangle bounds); + + public void reconvert(); + + public void removeNotify(); + + public void setCharacterSubsets(Character.Subset[] subsets); + + public void setCompositionEnabled(boolean enable); + + public void setInputMethodContext(InputMethodContext context); + + public boolean setLocale(Locale locale); +} + diff --git a/awt/java/awt/im/spi/InputMethodContext.java b/awt/java/awt/im/spi/InputMethodContext.java new file mode 100644 index 000000000..bfc773cf0 --- /dev/null +++ b/awt/java/awt/im/spi/InputMethodContext.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.im.spi; + +//???AWT: import java.awt.Window; +import java.awt.font.TextHitInfo; +import java.awt.im.InputMethodRequests; +import java.text.AttributedCharacterIterator; +//???AWT: import javax.swing.JFrame; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface InputMethodContext extends InputMethodRequests { + +// ???AWT: public JFrame createInputMethodJFrame(String title, boolean attachToInputContext); + +// ???AWT: public Window createInputMethodWindow(String title, boolean attachToInputContext); + + public void dispatchInputMethodEvent(int id, AttributedCharacterIterator text, int committedCharacterCount, TextHitInfo caret, TextHitInfo visiblePosition); + + public void enableClientWindowNotification(InputMethod inputMethod, boolean enable); + +} + diff --git a/awt/java/awt/im/spi/InputMethodDescriptor.java b/awt/java/awt/im/spi/InputMethodDescriptor.java new file mode 100644 index 000000000..c800bc179 --- /dev/null +++ b/awt/java/awt/im/spi/InputMethodDescriptor.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.im.spi; + +import java.awt.AWTException; +import java.awt.Image; +import java.util.Locale; + +/** + * This class is not supported in Android 1.0. It is merely provided to maintain + * interface compatibility with desktop Java implementations. + * + * @since Android 1.0 + */ +public interface InputMethodDescriptor { + + public Locale[] getAvailableLocales() throws AWTException; + + public InputMethod createInputMethod() throws Exception; + + public String getInputMethodDisplayName(Locale inputLocale, Locale displayLanguage); + + public Image getInputMethodIcon(Locale inputLocale); + + public boolean hasDynamicLocaleList(); + +} + diff --git a/awt/java/awt/image/AffineTransformOp.java b/awt/java/awt/image/AffineTransformOp.java new file mode 100644 index 000000000..db25e1aec --- /dev/null +++ b/awt/java/awt/image/AffineTransformOp.java @@ -0,0 +1,618 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky, Denis M. Kishenko + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Point2D; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.*; +import java.util.Arrays; + +import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The AffineTransform class translates coordinates from 2D coordinates in the + * source image or Raster to 2D coordinates in the destination image or Raster + * using affine transformation. The number of bands in the source Raster should + * equal to the number of bands in the destination Raster. + * + * @since Android 1.0 + */ +public class AffineTransformOp implements BufferedImageOp, RasterOp { + + /** + * The Constant TYPE_NEAREST_NEIGHBOR indicates nearest-neighbor + * interpolation type. + */ + public static final int TYPE_NEAREST_NEIGHBOR = 1; + + /** + * The Constant TYPE_BILINEAR indicates bilinear interpolation type. + */ + public static final int TYPE_BILINEAR = 2; + + /** + * The Constant TYPE_BICUBIC indicates bi-cubic interpolation type. + */ + public static final int TYPE_BICUBIC = 3; + + /** + * The i type. + */ + private int iType; // interpolation type + + /** + * The at. + */ + private AffineTransform at; + + /** + * The hints. + */ + private RenderingHints hints; + + static { + // TODO - uncomment + // System.loadLibrary("imageops"); + } + + /** + * Instantiates a new AffineTransformOp with the specified AffineTransform + * and RenderingHints object which defines the interpolation type. + * + * @param xform + * the AffineTransform. + * @param hints + * the RenderingHints object which defines the interpolation + * type. + */ + public AffineTransformOp(AffineTransform xform, RenderingHints hints) { + this(xform, TYPE_NEAREST_NEIGHBOR); + this.hints = hints; + + if (hints != null) { + Object hint = hints.get(RenderingHints.KEY_INTERPOLATION); + if (hint != null) { + // Nearest neighbor is default + if (hint == RenderingHints.VALUE_INTERPOLATION_BILINEAR) { + this.iType = TYPE_BILINEAR; + } else if (hint == RenderingHints.VALUE_INTERPOLATION_BICUBIC) { + this.iType = TYPE_BICUBIC; + } + } else { + hint = hints.get(RenderingHints.KEY_RENDERING); + // Determine from rendering quality + if (hint == RenderingHints.VALUE_RENDER_QUALITY) { + this.iType = TYPE_BILINEAR; + // For speed use nearest neighbor + } + } + } + } + + /** + * Instantiates a new AffineTransformOp with the specified AffineTransform + * and a specified interpolation type from the list of predefined + * interpolation types. + * + * @param xform + * the AffineTransform. + * @param interp + * the one of predefined interpolation types: + * TYPE_NEAREST_NEIGHBOR, TYPE_BILINEAR, or TYPE_BICUBIC. + */ + public AffineTransformOp(AffineTransform xform, int interp) { + if (Math.abs(xform.getDeterminant()) <= Double.MIN_VALUE) { + // awt.24F=Unable to invert transform {0} + throw new ImagingOpException(Messages.getString("awt.24F", xform)); //$NON-NLS-1$ + } + + this.at = (AffineTransform)xform.clone(); + + if (interp != TYPE_NEAREST_NEIGHBOR && interp != TYPE_BILINEAR && interp != TYPE_BICUBIC) { + // awt.250=Unknown interpolation type: {0} + throw new IllegalArgumentException(Messages.getString("awt.250", interp)); //$NON-NLS-1$ + } + + this.iType = interp; + } + + /** + * Gets the interpolation type. + * + * @return the interpolation type. + */ + public final int getInterpolationType() { + return iType; + } + + public final RenderingHints getRenderingHints() { + if (hints == null) { + Object value = null; + + switch (iType) { + case TYPE_NEAREST_NEIGHBOR: + value = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; + break; + case TYPE_BILINEAR: + value = RenderingHints.VALUE_INTERPOLATION_BILINEAR; + break; + case TYPE_BICUBIC: + value = RenderingHints.VALUE_INTERPOLATION_BICUBIC; + break; + default: + value = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; + } + + hints = new RenderingHints(RenderingHints.KEY_INTERPOLATION, value); + } + + return hints; + } + + /** + * Gets the affine transform associated with this AffineTransformOp. + * + * @return the AffineTransform. + */ + public final AffineTransform getTransform() { + return (AffineTransform)at.clone(); + } + + public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { + return at.transform(srcPt, dstPt); + } + + public final Rectangle2D getBounds2D(BufferedImage src) { + return getBounds2D(src.getRaster()); + } + + public final Rectangle2D getBounds2D(Raster src) { + // We position source raster to (0,0) even if it is translated child + // raster. + // This means that we need only width and height of the src + int width = src.getWidth(); + int height = src.getHeight(); + + float[] corners = { + 0, 0, width, 0, width, height, 0, height + }; + + at.transform(corners, 0, corners, 0, 4); + + Rectangle2D.Float bounds = new Rectangle2D.Float(corners[0], corners[1], 0, 0); + bounds.add(corners[2], corners[3]); + bounds.add(corners[4], corners[5]); + bounds.add(corners[6], corners[7]); + + return bounds; + } + + public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) { + Rectangle2D newBounds = getBounds2D(src); + + // Destination image should include (0,0) + positive part + // of the area bounded by newBounds (in source coordinate system). + double dstWidth = newBounds.getX() + newBounds.getWidth(); + double dstHeight = newBounds.getY() + newBounds.getHeight(); + + if (dstWidth <= 0 || dstHeight <= 0) { + // awt.251=Transformed width ({0}) and height ({1}) should be + // greater than 0 + throw new RasterFormatException(Messages.getString("awt.251", dstWidth, dstHeight)); //$NON-NLS-1$ + } + + if (destCM != null) { + return new BufferedImage(destCM, destCM.createCompatibleWritableRaster((int)dstWidth, + (int)dstHeight), destCM.isAlphaPremultiplied(), null); + } + + ColorModel cm = src.getColorModel(); + + // Interpolation other than NN doesn't make any sense for index color + if (iType != TYPE_NEAREST_NEIGHBOR && cm instanceof IndexColorModel) { + return new BufferedImage((int)dstWidth, (int)dstHeight, BufferedImage.TYPE_INT_ARGB); + } + + // OK, we can get source color model + return new BufferedImage(cm, src.getRaster().createCompatibleWritableRaster((int)dstWidth, + (int)dstHeight), cm.isAlphaPremultiplied(), null); + } + + public WritableRaster createCompatibleDestRaster(Raster src) { + // Here approach is other then in createCompatibleDestImage - + // destination should include only + // transformed image, but not (0,0) in source coordinate system + + Rectangle2D newBounds = getBounds2D(src); + return src.createCompatibleWritableRaster((int)newBounds.getX(), (int)newBounds.getY(), + (int)newBounds.getWidth(), (int)newBounds.getHeight()); + } + + public final BufferedImage filter(BufferedImage src, BufferedImage dst) { + if (src == dst) { + // awt.252=Source can't be same as the destination + throw new IllegalArgumentException(Messages.getString("awt.252")); //$NON-NLS-1$ + } + + ColorModel srcCM = src.getColorModel(); + BufferedImage finalDst = null; + + if (srcCM instanceof IndexColorModel + && (iType != TYPE_NEAREST_NEIGHBOR || srcCM.getPixelSize() % 8 != 0)) { + src = ((IndexColorModel)srcCM).convertToIntDiscrete(src.getRaster(), true); + srcCM = src.getColorModel(); + } + + if (dst == null) { + dst = createCompatibleDestImage(src, srcCM); + } else { + if (!srcCM.equals(dst.getColorModel())) { + // Treat BufferedImage.TYPE_INT_RGB and + // BufferedImage.TYPE_INT_ARGB as same + if (!((src.getType() == BufferedImage.TYPE_INT_RGB || src.getType() == BufferedImage.TYPE_INT_ARGB) && (dst + .getType() == BufferedImage.TYPE_INT_RGB || dst.getType() == BufferedImage.TYPE_INT_ARGB))) { + finalDst = dst; + dst = createCompatibleDestImage(src, srcCM); + } + } + } + + // Skip alpha channel for TYPE_INT_RGB images + if (slowFilter(src.getRaster(), dst.getRaster()) != 0) { + // awt.21F=Unable to transform source + throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$ + // TODO - uncomment + // if (ippFilter(src.getRaster(), dst.getRaster(), src.getType()) != + // 0) + // throw new ImagingOpException ("Unable to transform source"); + } + + if (finalDst != null) { + Graphics2D g = finalDst.createGraphics(); + g.setComposite(AlphaComposite.Src); + g.drawImage(dst, 0, 0, null); + } else { + finalDst = dst; + } + + return finalDst; + } + + public final WritableRaster filter(Raster src, WritableRaster dst) { + if (src == dst) { + // awt.252=Source can't be same as the destination + throw new IllegalArgumentException(Messages.getString("awt.252")); //$NON-NLS-1$ + } + + if (dst == null) { + dst = createCompatibleDestRaster(src); + } else if (src.getNumBands() != dst.getNumBands()) { + // awt.253=Different number of bands in source and destination + throw new IllegalArgumentException(Messages.getString("awt.253")); //$NON-NLS-1$ + } + + if (slowFilter(src, dst) != 0) { + // awt.21F=Unable to transform source + throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$ + // TODO - uncomment + // if (ippFilter(src, dst, BufferedImage.TYPE_CUSTOM) != 0) + // throw new ImagingOpException("Unable to transform source"); + } + + return dst; + } + + // TODO remove when method is used + /** + * Ipp filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @param imageType + * the image type. + * @return the int. + */ + @SuppressWarnings("unused") + private int ippFilter(Raster src, WritableRaster dst, int imageType) { + int srcStride, dstStride; + boolean skipChannel = false; + int channels; + int offsets[] = null; + + switch (imageType) { + case BufferedImage.TYPE_INT_RGB: + case BufferedImage.TYPE_INT_BGR: { + channels = 4; + srcStride = src.getWidth() * 4; + dstStride = dst.getWidth() * 4; + skipChannel = true; + break; + } + + case BufferedImage.TYPE_INT_ARGB: + case BufferedImage.TYPE_INT_ARGB_PRE: + case BufferedImage.TYPE_4BYTE_ABGR: + case BufferedImage.TYPE_4BYTE_ABGR_PRE: { + channels = 4; + srcStride = src.getWidth() * 4; + dstStride = dst.getWidth() * 4; + break; + } + + case BufferedImage.TYPE_BYTE_GRAY: + case BufferedImage.TYPE_BYTE_INDEXED: { + channels = 1; + srcStride = src.getWidth(); + dstStride = dst.getWidth(); + break; + } + + case BufferedImage.TYPE_3BYTE_BGR: { + channels = 3; + srcStride = src.getWidth() * 3; + dstStride = dst.getWidth() * 3; + break; + } + + case BufferedImage.TYPE_USHORT_GRAY: // TODO - could be done in + // native code? + case BufferedImage.TYPE_USHORT_565_RGB: + case BufferedImage.TYPE_USHORT_555_RGB: + case BufferedImage.TYPE_BYTE_BINARY: { + return slowFilter(src, dst); + } + + default: { + SampleModel srcSM = src.getSampleModel(); + SampleModel dstSM = dst.getSampleModel(); + + if (srcSM instanceof PixelInterleavedSampleModel + && dstSM instanceof PixelInterleavedSampleModel) { + // Check PixelInterleavedSampleModel + if (srcSM.getDataType() != DataBuffer.TYPE_BYTE + || dstSM.getDataType() != DataBuffer.TYPE_BYTE) { + return slowFilter(src, dst); + } + + channels = srcSM.getNumBands(); // Have IPP functions for 1, + // 3 and 4 channels + if (channels != 1 && channels != 3 && channels != 4) { + return slowFilter(src, dst); + } + + int dataTypeSize = DataBuffer.getDataTypeSize(srcSM.getDataType()) / 8; + + srcStride = ((ComponentSampleModel)srcSM).getScanlineStride() * dataTypeSize; + dstStride = ((ComponentSampleModel)dstSM).getScanlineStride() * dataTypeSize; + } else if (srcSM instanceof SinglePixelPackedSampleModel + && dstSM instanceof SinglePixelPackedSampleModel) { + // Check SinglePixelPackedSampleModel + SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel)srcSM; + SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel)dstSM; + + // No IPP function for this type + if (sppsm1.getDataType() == DataBuffer.TYPE_USHORT) { + return slowFilter(src, dst); + } + + channels = sppsm1.getNumBands(); + // Have IPP functions for 1, 3 and 4 channels + if (channels != 1 && channels != 3 && channels != 4) { + return slowFilter(src, dst); + } + + // Check compatibility of sample models + if (sppsm1.getDataType() != sppsm2.getDataType() + || !Arrays.equals(sppsm1.getBitOffsets(), sppsm2.getBitOffsets()) + || !Arrays.equals(sppsm1.getBitMasks(), sppsm2.getBitMasks())) { + return slowFilter(src, dst); + } + + for (int i = 0; i < channels; i++) { + if (sppsm1.getSampleSize(i) != 8) { + return slowFilter(src, dst); + } + } + + if (channels == 3) { + channels = 4; + } + + int dataTypeSize = DataBuffer.getDataTypeSize(sppsm1.getDataType()) / 8; + + srcStride = sppsm1.getScanlineStride() * dataTypeSize; + dstStride = sppsm2.getScanlineStride() * dataTypeSize; + } else { + return slowFilter(src, dst); + } + + // Fill offsets if there's a child raster + if (src.getParent() != null || dst.getParent() != null) { + if (src.getSampleModelTranslateX() != 0 || src.getSampleModelTranslateY() != 0 + || dst.getSampleModelTranslateX() != 0 + || dst.getSampleModelTranslateY() != 0) { + offsets = new int[4]; + offsets[0] = -src.getSampleModelTranslateX() + src.getMinX(); + offsets[1] = -src.getSampleModelTranslateY() + src.getMinY(); + offsets[2] = -dst.getSampleModelTranslateX() + dst.getMinX(); + offsets[3] = -dst.getSampleModelTranslateY() + dst.getMinY(); + } + } + } + } + + double m00 = at.getScaleX(); + double m01 = at.getShearX(); + double m02 = at.getTranslateX(); + double m10 = at.getShearY(); + double m11 = at.getScaleY(); + double m12 = at.getTranslateY(); + + Object srcData, dstData; + AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance(); + try { + srcData = dbAccess.getData(src.getDataBuffer()); + dstData = dbAccess.getData(dst.getDataBuffer()); + } catch (IllegalArgumentException e) { + return -1; // Unknown data buffer type + } + + return ippAffineTransform(m00, m01, m02, m10, m11, m12, srcData, src.getWidth(), src + .getHeight(), srcStride, dstData, dst.getWidth(), dst.getHeight(), dstStride, + iType, channels, skipChannel, offsets); + } + + /** + * Slow filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @return the int. + */ + private int slowFilter(Raster src, WritableRaster dst) { + // TODO: make correct interpolation + // TODO: what if there are different data types? + + Rectangle srcBounds = src.getBounds(); + Rectangle dstBounds = dst.getBounds(); + Rectangle normDstBounds = new Rectangle(0, 0, dstBounds.width, dstBounds.height); + Rectangle bounds = getBounds2D(src).getBounds().intersection(normDstBounds); + + AffineTransform inv = null; + try { + inv = at.createInverse(); + } catch (NoninvertibleTransformException e) { + return -1; + } + + double[] m = new double[6]; + inv.getMatrix(m); + + int minSrcX = srcBounds.x; + int minSrcY = srcBounds.y; + int maxSrcX = srcBounds.x + srcBounds.width; + int maxSrcY = srcBounds.y + srcBounds.height; + + int minX = bounds.x + dstBounds.x; + int minY = bounds.y + dstBounds.y; + int maxX = minX + bounds.width; + int maxY = minY + bounds.height; + + int hx = (int)(m[0] * 256); + int hy = (int)(m[1] * 256); + int vx = (int)(m[2] * 256); + int vy = (int)(m[3] * 256); + int sx = (int)(m[4] * 256) + hx * bounds.x + vx * bounds.y + (srcBounds.x) * 256; + int sy = (int)(m[5] * 256) + hy * bounds.x + vy * bounds.y + (srcBounds.y) * 256; + + vx -= hx * bounds.width; + vy -= hy * bounds.width; + + if (src.getTransferType() == dst.getTransferType()) { + for (int y = minY; y < maxY; y++) { + for (int x = minX; x < maxX; x++) { + int px = sx >> 8; + int py = sy >> 8; + if (px >= minSrcX && py >= minSrcY && px < maxSrcX && py < maxSrcY) { + Object val = src.getDataElements(px, py, null); + dst.setDataElements(x, y, val); + } + sx += hx; + sy += hy; + } + sx += vx; + sy += vy; + } + } else { + float pixel[] = null; + for (int y = minY; y < maxY; y++) { + for (int x = minX; x < maxX; x++) { + int px = sx >> 8; + int py = sy >> 8; + if (px >= minSrcX && py >= minSrcY && px < maxSrcX && py < maxSrcY) { + pixel = src.getPixel(px, py, pixel); + dst.setPixel(x, y, pixel); + } + sx += hx; + sy += hy; + } + sx += vx; + sy += vy; + } + } + + return 0; + } + + /** + * Ipp affine transform. + * + * @param m00 + * the m00. + * @param m01 + * the m01. + * @param m02 + * the m02. + * @param m10 + * the m10. + * @param m11 + * the m11. + * @param m12 + * the m12. + * @param src + * the src. + * @param srcWidth + * the src width. + * @param srcHeight + * the src height. + * @param srcStride + * the src stride. + * @param dst + * the dst. + * @param dstWidth + * the dst width. + * @param dstHeight + * the dst height. + * @param dstStride + * the dst stride. + * @param iType + * the i type. + * @param channels + * the channels. + * @param skipChannel + * the skip channel. + * @param offsets + * the offsets. + * @return the int. + */ + private native int ippAffineTransform(double m00, double m01, double m02, double m10, + double m11, double m12, Object src, int srcWidth, int srcHeight, int srcStride, + Object dst, int dstWidth, int dstHeight, int dstStride, int iType, int channels, + boolean skipChannel, int offsets[]); +} \ No newline at end of file diff --git a/awt/java/awt/image/AreaAveragingScaleFilter.java b/awt/java/awt/image/AreaAveragingScaleFilter.java new file mode 100644 index 000000000..7fb138e7f --- /dev/null +++ b/awt/java/awt/image/AreaAveragingScaleFilter.java @@ -0,0 +1,288 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.util.Arrays; + +/** + * The AreaAveragingScaleFilter class scales the source image using area + * averaging algorithm. This algorithm provides a source image with a new image + * containing the resampled image. + * + * @since Android 1.0 + */ +public class AreaAveragingScaleFilter extends ReplicateScaleFilter { + + /** + * The Constant rgbCM. + */ + private static final ColorModel rgbCM = ColorModel.getRGBdefault(); + + /** + * The Constant averagingFlags. + */ + private static final int averagingFlags = (ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES); + + /** + * The reset. + */ + private boolean reset = true; // Flag for used superclass filter + + /** + * The inited. + */ + private boolean inited = false; // All data inited + + /** + * The sum_r. + */ + private int sum_r[]; // Array for average Red samples + + /** + * The sum_g. + */ + private int sum_g[]; // Array for average Green samples + + /** + * The sum_b. + */ + private int sum_b[]; // Array for average Blue samples + + /** + * The sum_a. + */ + private int sum_a[]; // Array for average Alpha samples + + /** + * The buff. + */ + private int buff[]; // Stride buffer + + /** + * The avg factor. + */ + private int avgFactor; // Global averaging factor + + /** + * The cached dy. + */ + private int cachedDY; // Cached number of the destination scanline + + /** + * The cached dv rest. + */ + private int cachedDVRest; // Cached value of rest src scanlines for sum + + // pixel samples + // Because data if transferring by whole scanlines + // we are caching only Y coordinate values + + /** + * Instantiates a new AreaAveragingScaleFilter object which scales a source + * image with the specified width and height. + * + * @param width + * the scaled width of the image. + * @param height + * the scaled height of the image. + */ + public AreaAveragingScaleFilter(int width, int height) { + super(width, height); + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, + int scansize) { + if (reset) { + super.setPixels(x, y, w, h, model, pixels, off, scansize); + } else { + setFilteredPixels(x, y, w, h, model, pixels, off, scansize); + } + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, + int scansize) { + if (reset) { + super.setPixels(x, y, w, h, model, pixels, off, scansize); + } else { + setFilteredPixels(x, y, w, h, model, pixels, off, scansize); + } + } + + @Override + public void setHints(int hints) { + super.setHints(hints); + reset = ((hints & averagingFlags) != averagingFlags); + } + + /** + * This method implements the Area Averaging Scale filter. The description + * of algorithm is presented in Java API Specification. Arrays sum_r, sum_g, + * sum_b, sum_a have length equals width of destination image. In each + * array's element is accumulating pixel's component values, proportional to + * the area which source pixels will occupy in destination image. Then that + * values will divide by Global averaging factor (area of the destination + * image) for receiving average values of destination pixels. + * + * @param x + * the source pixels X coordinate. + * @param y + * the source pixels Y coordinate. + * @param w + * the width of the area of the source pixels. + * @param h + * the height of the area of the source pixels. + * @param model + * the color model of the source pixels. + * @param pixels + * the array of source pixels. + * @param off + * the offset into the source pixels array. + * @param scansize + * the length of scanline in the pixels array. + */ + private void setFilteredPixels(int x, int y, int w, int h, ColorModel model, Object pixels, + int off, int scansize) { + if (!inited) { + initialize(); + } + + int srcX, srcY, dx, dy; + int svRest, dvRest, shRest, dhRest, vDif, hDif; + + if (y == 0) { + dy = 0; + dvRest = srcHeight; + } else { + dy = cachedDY; + dvRest = cachedDVRest; + } + + srcY = y; + svRest = destHeight; + + int srcOff = off; + while (srcY < y + h) { + if (svRest < dvRest) { + vDif = svRest; + } else { + vDif = dvRest; + } + + srcX = 0; + dx = 0; + shRest = destWidth; + dhRest = srcWidth; + while (srcX < w) { + if (shRest < dhRest) { + hDif = shRest; + } else { + hDif = dhRest; + } + int avg = hDif * vDif; // calculation of contribution factor + + int rgb, pix; + if (pixels instanceof int[]) { + pix = ((int[])pixels)[srcOff + srcX]; + } else { + pix = ((byte[])pixels)[srcOff + srcX] & 0xff; + } + + rgb = model.getRGB(pix); + int a = rgb >>> 24; + int r = (rgb >> 16) & 0xff; + int g = (rgb >> 8) & 0xff; + int b = rgb & 0xff; + + // accumulating pixel's component values + sum_a[dx] += a * avg; + sum_r[dx] += r * avg; + sum_g[dx] += g * avg; + sum_b[dx] += b * avg; + + shRest -= hDif; + dhRest -= hDif; + + if (shRest == 0) { + srcX++; + shRest = destWidth; + } + + if (dhRest == 0) { + dx++; + dhRest = srcWidth; + } + } + + svRest -= vDif; + dvRest -= vDif; + + if (svRest == 0) { + svRest = destHeight; + srcY++; + srcOff += scansize; + } + + if (dvRest == 0) { + // averaging destination pixel's values + for (int i = 0; i < destWidth; i++) { + int a = (sum_a[i] / avgFactor) & 0xff; + int r = (sum_r[i] / avgFactor) & 0xff; + int g = (sum_g[i] / avgFactor) & 0xff; + int b = (sum_b[i] / avgFactor) & 0xff; + int frgb = (a << 24) | (r << 16) | (g << 8) | b; + buff[i] = frgb; + } + consumer.setPixels(0, dy, destWidth, 1, rgbCM, buff, 0, destWidth); + dy++; + dvRest = srcHeight; + Arrays.fill(sum_a, 0); + Arrays.fill(sum_r, 0); + Arrays.fill(sum_g, 0); + Arrays.fill(sum_b, 0); + } + + } + + cachedDY = dy; + cachedDVRest = dvRest; + + } + + /** + * Initialization of the auxiliary data. + */ + private void initialize() { + + sum_a = new int[destWidth]; + sum_r = new int[destWidth]; + sum_g = new int[destWidth]; + sum_b = new int[destWidth]; + + buff = new int[destWidth]; + outpixbuf = buff; + avgFactor = srcWidth * srcHeight; + + inited = true; + } +} diff --git a/awt/java/awt/image/AwtImageBackdoorAccessorImpl.java b/awt/java/awt/image/AwtImageBackdoorAccessorImpl.java new file mode 100644 index 000000000..6dffee88f --- /dev/null +++ b/awt/java/awt/image/AwtImageBackdoorAccessorImpl.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + * Created on 23.11.2005 + * + */ + +package java.awt.image; + +import java.awt.Image; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferDouble; +import java.awt.image.DataBufferFloat; +import java.awt.image.DataBufferInt; +import java.awt.image.DataBufferShort; +import java.awt.image.DataBufferUShort; + +import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; +import org.apache.harmony.awt.gl.GLVolatileImage; +import org.apache.harmony.awt.gl.Surface; +import org.apache.harmony.awt.gl.image.DataBufferListener; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * This class not part of public API. It useful for receiving package private + * data from other packages. + * + * @since Android 1.0 + */ +class AwtImageBackdoorAccessorImpl extends AwtImageBackdoorAccessor { + + static void init() { + inst = new AwtImageBackdoorAccessorImpl(); + } + + @Override + public Surface getImageSurface(Image image) { + if (image instanceof BufferedImage) { + return ((BufferedImage)image).getImageSurface(); + } else if (image instanceof GLVolatileImage) { + return ((GLVolatileImage)image).getImageSurface(); + } + return null; + } + + @Override + public boolean isGrayPallete(IndexColorModel icm) { + return icm.isGrayPallete(); + } + + @Override + public Object getData(DataBuffer db) { + if (db instanceof DataBufferByte) { + return ((DataBufferByte)db).getData(); + } else if (db instanceof DataBufferUShort) { + return ((DataBufferUShort)db).getData(); + } else if (db instanceof DataBufferShort) { + return ((DataBufferShort)db).getData(); + } else if (db instanceof DataBufferInt) { + return ((DataBufferInt)db).getData(); + } else if (db instanceof DataBufferFloat) { + return ((DataBufferFloat)db).getData(); + } else if (db instanceof DataBufferDouble) { + return ((DataBufferDouble)db).getData(); + } else { + // awt.235=Wrong Data Buffer type : {0} + throw new IllegalArgumentException(Messages.getString("awt.235", //$NON-NLS-1$ + db.getClass())); + } + } + + @Override + public int[] getDataInt(DataBuffer db) { + if (db instanceof DataBufferInt) { + return ((DataBufferInt)db).getData(); + } + return null; + } + + @Override + public byte[] getDataByte(DataBuffer db) { + if (db instanceof DataBufferByte) { + return ((DataBufferByte)db).getData(); + } + return null; + } + + @Override + public short[] getDataShort(DataBuffer db) { + if (db instanceof DataBufferShort) { + return ((DataBufferShort)db).getData(); + } + return null; + } + + @Override + public short[] getDataUShort(DataBuffer db) { + if (db instanceof DataBufferUShort) { + return ((DataBufferUShort)db).getData(); + } + return null; + } + + @Override + public double[] getDataDouble(DataBuffer db) { + if (db instanceof DataBufferDouble) { + return ((DataBufferDouble)db).getData(); + } + return null; + } + + @Override + public float[] getDataFloat(DataBuffer db) { + if (db instanceof DataBufferFloat) { + return ((DataBufferFloat)db).getData(); + } + return null; + } + + @Override + public void addDataBufferListener(DataBuffer db, DataBufferListener listener) { + db.addDataBufferListener(listener); + } + + @Override + public void removeDataBufferListener(DataBuffer db) { + db.removeDataBufferListener(); + } + + @Override + public void validate(DataBuffer db) { + db.validate(); + } + + @Override + public void releaseData(DataBuffer db) { + db.releaseData(); + } +} diff --git a/awt/java/awt/image/BandCombineOp.java b/awt/java/awt/image/BandCombineOp.java new file mode 100644 index 000000000..da2cc8944 --- /dev/null +++ b/awt/java/awt/image/BandCombineOp.java @@ -0,0 +1,658 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Sep 20, 2005 + */ + +package java.awt.image; + +import java.awt.*; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The BandCombineOp class translates coordinates from coordinates in the source + * Raster to coordinates in the destination Raster by an arbitrary linear + * combination of the bands in a source Raster, using a specified matrix. The + * number of bands in the matrix should equal to the number of bands in the + * source Raster plus 1. + * + * @since Android 1.0 + */ +public class BandCombineOp implements RasterOp { + + /** + * The Constant offsets3c. + */ + static final int offsets3c[] = { + 16, 8, 0 + }; + + /** + * The Constant offsets4ac. + */ + static final int offsets4ac[] = { + 16, 8, 0, 24 + }; + + /** + * The Constant masks3c. + */ + static final int masks3c[] = { + 0xFF0000, 0xFF00, 0xFF + }; + + /** + * The Constant masks4ac. + */ + static final int masks4ac[] = { + 0xFF0000, 0xFF00, 0xFF, 0xFF000000 + }; + + /** + * The Constant piOffsets. + */ + private static final int piOffsets[] = { + 0, 1, 2 + }; + + /** + * The Constant piInvOffsets. + */ + private static final int piInvOffsets[] = { + 2, 1, 0 + }; + + /** + * The Constant TYPE_BYTE3C. + */ + private static final int TYPE_BYTE3C = 0; + + /** + * The Constant TYPE_BYTE4AC. + */ + private static final int TYPE_BYTE4AC = 1; + + /** + * The Constant TYPE_USHORT3C. + */ + private static final int TYPE_USHORT3C = 2; + + /** + * The Constant TYPE_SHORT3C. + */ + private static final int TYPE_SHORT3C = 3; + + /** + * The mx width. + */ + private int mxWidth; + + /** + * The mx height. + */ + private int mxHeight; + + /** + * The matrix. + */ + private float matrix[][]; + + /** + * The r hints. + */ + private RenderingHints rHints; + + static { + // XXX - todo + // System.loadLibrary("imageops"); + } + + /** + * Instantiates a new BandCombineOp object with the specified matrix. + * + * @param matrix + * the specified matrix for band combining. + * @param hints + * the RenderingHints. + */ + public BandCombineOp(float matrix[][], RenderingHints hints) { + this.mxHeight = matrix.length; + this.mxWidth = matrix[0].length; + this.matrix = new float[mxHeight][mxWidth]; + + for (int i = 0; i < mxHeight; i++) { + System.arraycopy(matrix[i], 0, this.matrix[i], 0, mxWidth); + } + + this.rHints = hints; + } + + public final RenderingHints getRenderingHints() { + return this.rHints; + } + + /** + * Gets the matrix associated with this BandCombineOp object. + * + * @return the matrix associated with this BandCombineOp object. + */ + public final float[][] getMatrix() { + float res[][] = new float[mxHeight][mxWidth]; + + for (int i = 0; i < mxHeight; i++) { + System.arraycopy(matrix[i], 0, res[i], 0, mxWidth); + } + + return res; + } + + public final Point2D getPoint2D(Point2D srcPoint, Point2D dstPoint) { + if (dstPoint == null) { + dstPoint = new Point2D.Float(); + } + + dstPoint.setLocation(srcPoint); + return dstPoint; + } + + public final Rectangle2D getBounds2D(Raster src) { + return src.getBounds(); + } + + public WritableRaster createCompatibleDestRaster(Raster src) { + int numBands = src.getNumBands(); + if (mxWidth != numBands && mxWidth != (numBands + 1) || numBands != mxHeight) { + // awt.254=Number of bands in the source raster ({0}) is + // incompatible with the matrix [{1}x{2}] + throw new IllegalArgumentException(Messages.getString("awt.254", //$NON-NLS-1$ + new Object[] { + numBands, mxWidth, mxHeight + })); + } + + return src.createCompatibleWritableRaster(src.getWidth(), src.getHeight()); + } + + public WritableRaster filter(Raster src, WritableRaster dst) { + int numBands = src.getNumBands(); + + if (mxWidth != numBands && mxWidth != (numBands + 1)) { + // awt.254=Number of bands in the source raster ({0}) is + // incompatible with the matrix [{1}x{2}] + throw new IllegalArgumentException(Messages.getString("awt.254", //$NON-NLS-1$ + new Object[] { + numBands, mxWidth, mxHeight + })); + } + + if (dst == null) { + dst = createCompatibleDestRaster(src); + } else if (dst.getNumBands() != mxHeight) { + // awt.255=Number of bands in the destination raster ({0}) is + // incompatible with the matrix [{1}x{2}] + throw new IllegalArgumentException(Messages.getString("awt.255", //$NON-NLS-1$ + new Object[] { + dst.getNumBands(), mxWidth, mxHeight + })); + } + + // XXX - todo + // if (ippFilter(src, dst) != 0) + if (verySlowFilter(src, dst) != 0) { + // awt.21F=Unable to transform source + throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$ + } + + return dst; + } + + /** + * The Class SampleModelInfo. + */ + private static final class SampleModelInfo { + + /** + * The channels. + */ + int channels; + + /** + * The channels order. + */ + int channelsOrder[]; + + /** + * The stride. + */ + int stride; + } + + /** + * Check sample model. + * + * @param sm + * the sm. + * @return the sample model info. + */ + private final SampleModelInfo checkSampleModel(SampleModel sm) { + SampleModelInfo ret = new SampleModelInfo(); + + if (sm instanceof PixelInterleavedSampleModel) { + // Check PixelInterleavedSampleModel + if (sm.getDataType() != DataBuffer.TYPE_BYTE) { + return null; + } + + ret.channels = sm.getNumBands(); + ret.stride = ((ComponentSampleModel)sm).getScanlineStride(); + ret.channelsOrder = ((ComponentSampleModel)sm).getBandOffsets(); + + } else if (sm instanceof SinglePixelPackedSampleModel) { + // Check SinglePixelPackedSampleModel + SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel)sm; + + ret.channels = sppsm1.getNumBands(); + if (sppsm1.getDataType() != DataBuffer.TYPE_INT) { + return null; + } + + // Check sample models + for (int i = 0; i < ret.channels; i++) { + if (sppsm1.getSampleSize(i) != 8) { + return null; + } + } + + ret.channelsOrder = new int[ret.channels]; + int bitOffsets[] = sppsm1.getBitOffsets(); + for (int i = 0; i < ret.channels; i++) { + if (bitOffsets[i] % 8 != 0) { + return null; + } + + ret.channelsOrder[i] = bitOffsets[i] / 8; + } + + ret.channels = 4; + ret.stride = sppsm1.getScanlineStride() * 4; + } else { + return null; + } + + return ret; + } + + /** + * Slow filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @return the int. + */ + private final int slowFilter(Raster src, WritableRaster dst) { + int res = 0; + + SampleModelInfo srcInfo, dstInfo; + int offsets[] = null; + + srcInfo = checkSampleModel(src.getSampleModel()); + dstInfo = checkSampleModel(dst.getSampleModel()); + if (srcInfo == null || dstInfo == null) { + return verySlowFilter(src, dst); + } + + // Fill offsets if there's a child raster + if (src.getParent() != null || dst.getParent() != null) { + if (src.getSampleModelTranslateX() != 0 || src.getSampleModelTranslateY() != 0 + || dst.getSampleModelTranslateX() != 0 || dst.getSampleModelTranslateY() != 0) { + offsets = new int[4]; + offsets[0] = -src.getSampleModelTranslateX() + src.getMinX(); + offsets[1] = -src.getSampleModelTranslateY() + src.getMinY(); + offsets[2] = -dst.getSampleModelTranslateX() + dst.getMinX(); + offsets[3] = -dst.getSampleModelTranslateY() + dst.getMinY(); + } + } + + int rmxWidth = (srcInfo.channels + 1); // width of the reordered matrix + float reorderedMatrix[] = new float[rmxWidth * dstInfo.channels]; + for (int j = 0; j < dstInfo.channels; j++) { + if (j >= dstInfo.channelsOrder.length) { + continue; + } + + for (int i = 0; i < srcInfo.channels; i++) { + if (i >= srcInfo.channelsOrder.length) { + break; + } + + reorderedMatrix[dstInfo.channelsOrder[j] * rmxWidth + srcInfo.channelsOrder[i]] = matrix[j][i]; + } + if (mxWidth == rmxWidth) { + reorderedMatrix[(dstInfo.channelsOrder[j] + 1) * rmxWidth - 1] = matrix[j][mxWidth - 1]; + } + } + + Object srcData, dstData; + AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance(); + try { + srcData = dbAccess.getData(src.getDataBuffer()); + dstData = dbAccess.getData(dst.getDataBuffer()); + } catch (IllegalArgumentException e) { + return -1; // Unknown data buffer type + } + + simpleCombineBands(srcData, src.getWidth(), src.getHeight(), srcInfo.stride, + srcInfo.channels, dstData, dstInfo.stride, dstInfo.channels, reorderedMatrix, + offsets); + + return res; + } + + /** + * Very slow filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @return the int. + */ + private int verySlowFilter(Raster src, WritableRaster dst) { + int numBands = src.getNumBands(); + + int srcMinX = src.getMinX(); + int srcY = src.getMinY(); + + int dstMinX = dst.getMinX(); + int dstY = dst.getMinY(); + + int dX = src.getWidth();// < dst.getWidth() ? src.getWidth() : + // dst.getWidth(); + int dY = src.getHeight();// < dst.getHeight() ? src.getHeight() : + // dst.getHeight(); + + float sample; + int srcPixels[] = new int[numBands * dX * dY]; + int dstPixels[] = new int[mxHeight * dX * dY]; + + srcPixels = src.getPixels(srcMinX, srcY, dX, dY, srcPixels); + + if (numBands == mxWidth) { + for (int i = 0, j = 0; i < srcPixels.length; i += numBands) { + for (int dstB = 0; dstB < mxHeight; dstB++) { + sample = 0f; + for (int srcB = 0; srcB < numBands; srcB++) { + sample += matrix[dstB][srcB] * srcPixels[i + srcB]; + } + dstPixels[j++] = (int)sample; + } + } + } else { + for (int i = 0, j = 0; i < srcPixels.length; i += numBands) { + for (int dstB = 0; dstB < mxHeight; dstB++) { + sample = 0f; + for (int srcB = 0; srcB < numBands; srcB++) { + sample += matrix[dstB][srcB] * srcPixels[i + srcB]; + } + dstPixels[j++] = (int)(sample + matrix[dstB][numBands]); + } + } + } + + dst.setPixels(dstMinX, dstY, dX, dY, dstPixels); + + return 0; + } + + // TODO remove when method is used + /** + * Ipp filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @return the int. + */ + @SuppressWarnings("unused") + private int ippFilter(Raster src, WritableRaster dst) { + boolean invertChannels; + boolean inPlace = (src == dst); + int type; + int srcStride, dstStride; + int offsets[] = null; + + int srcBands = src.getNumBands(); + int dstBands = dst.getNumBands(); + + if (dstBands != 3 + || (srcBands != 3 && !(srcBands == 4 && matrix[0][3] == 0 && matrix[1][3] == 0 && matrix[2][3] == 0))) { + return slowFilter(src, dst); + } + + SampleModel srcSM = src.getSampleModel(); + SampleModel dstSM = dst.getSampleModel(); + + if (srcSM instanceof SinglePixelPackedSampleModel + && dstSM instanceof SinglePixelPackedSampleModel) { + // Check SinglePixelPackedSampleModel + SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel)srcSM; + SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel)dstSM; + + if (sppsm1.getDataType() != DataBuffer.TYPE_INT + || sppsm2.getDataType() != DataBuffer.TYPE_INT) { + return slowFilter(src, dst); + } + + // Check sample models + if (!Arrays.equals(sppsm2.getBitOffsets(), offsets3c) + || !Arrays.equals(sppsm2.getBitMasks(), masks3c)) { + return slowFilter(src, dst); + } + + if (srcBands == 3) { + if (!Arrays.equals(sppsm1.getBitOffsets(), offsets3c) + || !Arrays.equals(sppsm1.getBitMasks(), masks3c)) { + return slowFilter(src, dst); + } + } else if (srcBands == 4) { + if (!Arrays.equals(sppsm1.getBitOffsets(), offsets4ac) + || !Arrays.equals(sppsm1.getBitMasks(), masks4ac)) { + return slowFilter(src, dst); + } + } + + type = TYPE_BYTE4AC; + invertChannels = true; + + srcStride = sppsm1.getScanlineStride() * 4; + dstStride = sppsm2.getScanlineStride() * 4; + } else if (srcSM instanceof PixelInterleavedSampleModel + && dstSM instanceof PixelInterleavedSampleModel) { + if (srcBands != 3) { + return slowFilter(src, dst); + } + + int srcDataType = srcSM.getDataType(); + + switch (srcDataType) { + case DataBuffer.TYPE_BYTE: + type = TYPE_BYTE3C; + break; + case DataBuffer.TYPE_USHORT: + type = TYPE_USHORT3C; + break; + case DataBuffer.TYPE_SHORT: + type = TYPE_SHORT3C; + break; + default: + return slowFilter(src, dst); + } + + // Check PixelInterleavedSampleModel + PixelInterleavedSampleModel pism1 = (PixelInterleavedSampleModel)srcSM; + PixelInterleavedSampleModel pism2 = (PixelInterleavedSampleModel)dstSM; + + if (srcDataType != pism2.getDataType() || pism1.getPixelStride() != 3 + || pism2.getPixelStride() != 3 + || !Arrays.equals(pism1.getBandOffsets(), pism2.getBandOffsets())) { + return slowFilter(src, dst); + } + + if (Arrays.equals(pism1.getBandOffsets(), piInvOffsets)) { + invertChannels = true; + } else if (Arrays.equals(pism1.getBandOffsets(), piOffsets)) { + invertChannels = false; + } else { + return slowFilter(src, dst); + } + + int dataTypeSize = DataBuffer.getDataTypeSize(srcDataType) / 8; + + srcStride = pism1.getScanlineStride() * dataTypeSize; + dstStride = pism2.getScanlineStride() * dataTypeSize; + } else { // XXX - todo - IPP allows support for planar data also + return slowFilter(src, dst); + } + + // Fill offsets if there's a child raster + if (src.getParent() != null || dst.getParent() != null) { + if (src.getSampleModelTranslateX() != 0 || src.getSampleModelTranslateY() != 0 + || dst.getSampleModelTranslateX() != 0 || dst.getSampleModelTranslateY() != 0) { + offsets = new int[4]; + offsets[0] = -src.getSampleModelTranslateX() + src.getMinX(); + offsets[1] = -src.getSampleModelTranslateY() + src.getMinY(); + offsets[2] = -dst.getSampleModelTranslateX() + dst.getMinX(); + offsets[3] = -dst.getSampleModelTranslateY() + dst.getMinY(); + } + } + + Object srcData, dstData; + AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance(); + try { + srcData = dbAccess.getData(src.getDataBuffer()); + dstData = dbAccess.getData(dst.getDataBuffer()); + } catch (IllegalArgumentException e) { + return -1; // Unknown data buffer type + } + + float ippMatrix[] = new float[12]; + + if (invertChannels) { + // IPP treats big endian integers like BGR, so we have to + // swap columns 1 and 3 and rows 1 and 3 + for (int i = 0; i < mxHeight; i++) { + ippMatrix[i * 4] = matrix[2 - i][2]; + ippMatrix[i * 4 + 1] = matrix[2 - i][1]; + ippMatrix[i * 4 + 2] = matrix[2 - i][0]; + + if (mxWidth == 4) { + ippMatrix[i * 4 + 3] = matrix[2 - i][3]; + } else if (mxWidth == 5) { + ippMatrix[i * 4 + 3] = matrix[2 - i][4]; + } + } + } else { + for (int i = 0; i < mxHeight; i++) { + ippMatrix[i * 4] = matrix[i][0]; + ippMatrix[i * 4 + 1] = matrix[i][1]; + ippMatrix[i * 4 + 2] = matrix[i][2]; + + if (mxWidth == 4) { + ippMatrix[i * 4 + 3] = matrix[i][3]; + } else if (mxWidth == 5) { + ippMatrix[i * 4 + 3] = matrix[i][4]; + } + } + } + + return ippColorTwist(srcData, src.getWidth(), src.getHeight(), srcStride, dstData, dst + .getWidth(), dst.getHeight(), dstStride, ippMatrix, type, offsets, inPlace); + } + + /** + * Ipp color twist. + * + * @param srcData + * the src data. + * @param srcWidth + * the src width. + * @param srcHeight + * the src height. + * @param srcStride + * the src stride. + * @param dstData + * the dst data. + * @param dstWidth + * the dst width. + * @param dstHeight + * the dst height. + * @param dstStride + * the dst stride. + * @param ippMatrix + * the ipp matrix. + * @param type + * the type. + * @param offsets + * the offsets. + * @param inPlace + * the in place. + * @return the int. + */ + private final native int ippColorTwist(Object srcData, int srcWidth, int srcHeight, + int srcStride, Object dstData, int dstWidth, int dstHeight, int dstStride, + float ippMatrix[], int type, int offsets[], boolean inPlace); + + /** + * Simple combine bands. + * + * @param srcData + * the src data. + * @param srcWidth + * the src width. + * @param srcHeight + * the src height. + * @param srcStride + * the src stride. + * @param srcChannels + * the src channels. + * @param dstData + * the dst data. + * @param dstStride + * the dst stride. + * @param dstChannels + * the dst channels. + * @param m + * the m. + * @param offsets + * the offsets. + * @return the int. + */ + private final native int simpleCombineBands(Object srcData, int srcWidth, int srcHeight, + int srcStride, int srcChannels, Object dstData, int dstStride, int dstChannels, + float m[], int offsets[]); +} diff --git a/awt/java/awt/image/BandedSampleModel.java b/awt/java/awt/image/BandedSampleModel.java new file mode 100644 index 000000000..0aa30d7da --- /dev/null +++ b/awt/java/awt/image/BandedSampleModel.java @@ -0,0 +1,425 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The BandedSampleModel class provides samples of pixels in an image which is + * stored in a band interleaved method. Each pixel's sample takes one data + * element of the DataBuffer. The pixel stride for a BandedSampleModel is one. + * + * @since Android 1.0 + */ +public final class BandedSampleModel extends ComponentSampleModel { + + /** + * Creates the indices. + * + * @param numBands + * the num bands. + * @return the int[]. + */ + private static int[] createIndices(int numBands) { + int indices[] = new int[numBands]; + for (int i = 0; i < numBands; i++) { + indices[i] = i; + } + return indices; + } + + /** + * Creates the offsets. + * + * @param numBands + * the num bands. + * @return the int[]. + */ + private static int[] createOffsets(int numBands) { + int offsets[] = new int[numBands]; + for (int i = 0; i < numBands; i++) { + offsets[i] = 0; + } + return offsets; + } + + /** + * Instantiates a new BandedSampleModel object with the specified data type + * of samples, the width, height and bands number of image data. + * + * @param dataType + * the data type of samples. + * @param w + * the width of image data. + * @param h + * the height of image data. + * @param numBands + * the number of bands. + */ + public BandedSampleModel(int dataType, int w, int h, int numBands) { + this(dataType, w, h, w, BandedSampleModel.createIndices(numBands), BandedSampleModel + .createOffsets(numBands)); + } + + /** + * Instantiates a new BandedSampleModel object with the specified data type + * of samples, the width, height and bands number of image data. + * + * @param dataType + * the data type of samples. + * @param w + * the width of image data. + * @param h + * the height of image data. + * @param scanlineStride + * the scanline stride of the of the image data. + * @param bankIndices + * the array of the bank indices. + * @param bandOffsets + * the array of the band offsets. + */ + public BandedSampleModel(int dataType, int w, int h, int scanlineStride, int bankIndices[], + int bandOffsets[]) { + super(dataType, w, h, 1, scanlineStride, bankIndices, bandOffsets); + } + + @Override + public SampleModel createCompatibleSampleModel(int w, int h) { + return new BandedSampleModel(dataType, w, h, w, bankIndices, bandOffsets); + } + + @Override + public DataBuffer createDataBuffer() { + DataBuffer data = null; + int size = scanlineStride * height; + + switch (dataType) { + case DataBuffer.TYPE_BYTE: + data = new DataBufferByte(size, numBanks); + break; + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + data = new DataBufferShort(size, numBanks); + break; + case DataBuffer.TYPE_INT: + data = new DataBufferInt(size, numBanks); + break; + case DataBuffer.TYPE_FLOAT: + data = new DataBufferFloat(size, numBanks); + break; + case DataBuffer.TYPE_DOUBLE: + data = new DataBufferDouble(size, numBanks); + break; + } + + return data; + + } + + @Override + public SampleModel createSubsetSampleModel(int[] bands) { + if (bands.length > numBands) { + // awt.64=The number of the bands in the subset is greater than the + // number of bands in the sample model + throw new RasterFormatException(Messages.getString("awt.64")); //$NON-NLS-1$ + } + + int indices[] = new int[bands.length]; + int offsets[] = new int[bands.length]; + + for (int i = 0; i < bands.length; i++) { + indices[i] = bankIndices[bands[i]]; + offsets[i] = bandOffsets[bands[i]]; + } + + return new BandedSampleModel(dataType, width, height, scanlineStride, indices, offsets); + } + + @Override + public Object getDataElements(int x, int y, Object obj, DataBuffer data) { + switch (dataType) { + case DataBuffer.TYPE_BYTE: { + byte bdata[]; + + if (obj == null) { + bdata = new byte[numBands]; + } else { + bdata = (byte[])obj; + } + + for (int i = 0; i < numBands; i++) { + bdata[i] = (byte)getSample(x, y, i, data); + } + + obj = bdata; + break; + } + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: { + short sdata[]; + + if (obj == null) { + sdata = new short[numBands]; + } else { + sdata = (short[])obj; + } + + for (int i = 0; i < numBands; i++) { + sdata[i] = (short)getSample(x, y, i, data); + } + + obj = sdata; + break; + } + case DataBuffer.TYPE_INT: { + int idata[]; + + if (obj == null) { + idata = new int[numBands]; + } else { + idata = (int[])obj; + } + + for (int i = 0; i < numBands; i++) { + idata[i] = getSample(x, y, i, data); + } + + obj = idata; + break; + } + case DataBuffer.TYPE_FLOAT: { + float fdata[]; + + if (obj == null) { + fdata = new float[numBands]; + } else { + fdata = (float[])obj; + } + + for (int i = 0; i < numBands; i++) { + fdata[i] = getSampleFloat(x, y, i, data); + } + + obj = fdata; + break; + } + case DataBuffer.TYPE_DOUBLE: { + double ddata[]; + + if (obj == null) { + ddata = new double[numBands]; + } else { + ddata = (double[])obj; + } + + for (int i = 0; i < numBands; i++) { + ddata[i] = getSampleDouble(x, y, i, data); + } + + obj = ddata; + break; + } + } + + return obj; + } + + @Override + public int[] getPixel(int x, int y, int iArray[], DataBuffer data) { + int pixel[]; + if (iArray == null) { + pixel = new int[numBands]; + } else { + pixel = iArray; + } + + for (int i = 0; i < numBands; i++) { + pixel[i] = getSample(x, y, i, data); + } + + return pixel; + } + + @Override + public int getSample(int x, int y, int b, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + return data.getElem(bankIndices[b], y * scanlineStride + x + bandOffsets[b]); + } + + @Override + public double getSampleDouble(int x, int y, int b, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + return data.getElemDouble(bankIndices[b], y * scanlineStride + x + bandOffsets[b]); + } + + @Override + public float getSampleFloat(int x, int y, int b, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + return data.getElemFloat(bankIndices[b], y * scanlineStride + x + bandOffsets[b]); + } + + @Override + public int[] getSamples(int x, int y, int w, int h, int b, int iArray[], DataBuffer data) { + int samples[]; + int idx = 0; + + if (iArray == null) { + samples = new int[w * h]; + } else { + samples = iArray; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + samples[idx++] = getSample(j, i, b, data); + } + } + + return samples; + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + int tmp = hash >>> 8; + hash <<= 8; + hash |= tmp; + + return hash ^ 0x55; + } + + @Override + public void setDataElements(int x, int y, Object obj, DataBuffer data) { + switch (dataType) { + case DataBuffer.TYPE_BYTE: + byte bdata[] = (byte[])obj; + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, bdata[i] & 0xff, data); + } + break; + + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + short sdata[] = (short[])obj; + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, sdata[i] & 0xffff, data); + } + break; + + case DataBuffer.TYPE_INT: + int idata[] = (int[])obj; + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, idata[i], data); + } + break; + + case DataBuffer.TYPE_FLOAT: + float fdata[] = (float[])obj; + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, fdata[i], data); + } + break; + + case DataBuffer.TYPE_DOUBLE: + double ddata[] = (double[])obj; + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, ddata[i], data); + } + break; + } + } + + @Override + public void setPixel(int x, int y, int iArray[], DataBuffer data) { + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, iArray[i], data); + } + } + + @Override + public void setPixels(int x, int y, int w, int h, int iArray[], DataBuffer data) { + int idx = 0; + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numBands; n++) { + setSample(j, i, n, iArray[idx++], data); + } + } + } + } + + @Override + public void setSample(int x, int y, int b, double s, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + data.setElemDouble(bankIndices[b], y * scanlineStride + x + bandOffsets[b], s); + } + + @Override + public void setSample(int x, int y, int b, float s, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + data.setElemFloat(bankIndices[b], y * scanlineStride + x + bandOffsets[b], s); + } + + @Override + public void setSample(int x, int y, int b, int s, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + data.setElem(bankIndices[b], y * scanlineStride + x + bandOffsets[b], s); + } + + @Override + public void setSamples(int x, int y, int w, int h, int b, int iArray[], DataBuffer data) { + int idx = 0; + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + setSample(j, i, b, iArray[idx++], data); + } + } + + } + +} diff --git a/awt/java/awt/image/BufferStrategy.java b/awt/java/awt/image/BufferStrategy.java new file mode 100644 index 000000000..3c8779dac --- /dev/null +++ b/awt/java/awt/image/BufferStrategy.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.BufferCapabilities; +import java.awt.Graphics; + +/** + * The BufferStrategy abstract class provides an opportunity to organize the + * buffers for a Canvas or Window. The BufferStrategy implementation depends on + * hardware and software limitations. These limitations are detectable through + * the capabilities object which can be obtained by the GraphicsConfiguration of + * the Canvas or Window. + * + * @since Android 1.0 + */ +public abstract class BufferStrategy { + + /** + * Returns true if the drawing buffer was lost since the last call of + * getDrawGraphics. + * + * @return true if the drawing buffer was lost since the last call of + * getDrawGraphics, false otherwise. + */ + public abstract boolean contentsLost(); + + /** + * Returns true if the drawing buffer is restored from a lost state. + * + * @return true if the drawing buffer is restored from a lost state, false + * otherwise. + */ + public abstract boolean contentsRestored(); + + /** + * Gets the BufferCapabilities of BufferStrategy. + * + * @return the BufferCapabilities of BufferStrategy. + */ + public abstract BufferCapabilities getCapabilities(); + + /** + * Gets the Graphics object to use to draw to the buffer. + * + * @return the Graphics object to use to draw to the buffer. + */ + public abstract Graphics getDrawGraphics(); + + /** + * Shows the next available buffer. + */ + public abstract void show(); + +} diff --git a/awt/java/awt/image/BufferedImage.java b/awt/java/awt/image/BufferedImage.java new file mode 100644 index 000000000..c9d58d97f --- /dev/null +++ b/awt/java/awt/image/BufferedImage.java @@ -0,0 +1,952 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import com.android.internal.awt.AndroidGraphics2D; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.apache.harmony.awt.gl.ImageSurface; +import org.apache.harmony.awt.gl.Surface; +import org.apache.harmony.awt.gl.image.BufferedImageSource; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The BufferedImage class describes an Image which contains a buffer of image + * data and includes a ColorModel and a Raster for this data. This class + * provides methods for obtaining and setting the Raster and for manipulating + * the ColorModel parameters. + * + * @since Android 1.0 + */ +public class BufferedImage extends Image implements WritableRenderedImage, Transparency { + + /** + * The Constant TYPE_CUSTOM indicates that Image type is unknown. + */ + public static final int TYPE_CUSTOM = 0; + + /** + * The Constant TYPE_INT_RGB indicates an image with 8 bit RGB color + * components, it has a DirectColorModel without alpha. + */ + public static final int TYPE_INT_RGB = 1; + + /** + * The Constant TYPE_INT_ARGB indicates an image with 8 bit RGBA color + * components, it has a DirectColorModel with alpha. + */ + public static final int TYPE_INT_ARGB = 2; + + /** + * The Constant TYPE_INT_ARGB_PRE indicates an image with 8 bit RGBA color + * components, it has a DirectColorModel with alpha, and image data is + * pre-multiplied by alpha. + */ + public static final int TYPE_INT_ARGB_PRE = 3; + + /** + * The Constant TYPE_INT_BGR indicates an image with 8 bit RGB color + * components, BGR color model (with the colors Blue, Green, and Red). There + * is no alpha. The image has a DirectColorModel. + */ + public static final int TYPE_INT_BGR = 4; + + /** + * The Constant TYPE_3BYTE_BGR indicates an image with 8 bit RGB color + * components, BGR color model (with the colors Blue, Green, and Red stored + * in 3 bytes). There is no alpha. The image has a ComponentColorModel. + */ + public static final int TYPE_3BYTE_BGR = 5; + + /** + * The Constant TYPE_4BYTE_ABGR indicates an image with 8 bit RGBA color + * components stored in 3 bytes and 1 byte of alpha. It has a + * ComponentColorModel with alpha. + */ + public static final int TYPE_4BYTE_ABGR = 6; + + /** + * The Constant TYPE_4BYTE_ABGR_PRE indicates an image with 8 bit RGBA color + * components stored in 3 bytes and 1 byte for alpha. The image has a + * ComponentColorModel with alpha. The color data is pre-multiplied with + * alpha. + */ + public static final int TYPE_4BYTE_ABGR_PRE = 7; + + /** + * The Constant TYPE_USHORT_565_RGB indicates an image with 565 RGB color + * components (5-bits red, 6-bits green, 5-bits blue) with no alpha. This + * image has a DirectColorModel. + */ + public static final int TYPE_USHORT_565_RGB = 8; + + /** + * The Constant TYPE_USHORT_555_RGB indicates an image with 555 RGB color + * components (5-bits red, 5-bits green, 5-bits blue) with no alpha. This + * image has a DirectColorModel. + */ + public static final int TYPE_USHORT_555_RGB = 9; + + /** + * The Constant TYPE_BYTE_GRAY indicates a unsigned byte image. This image + * has a ComponentColorModel with a CS_GRAY ColorSpace. + */ + public static final int TYPE_BYTE_GRAY = 10; + + /** + * The Constant TYPE_USHORT_GRAY indicates an unsigned short image. This + * image has a ComponentColorModel with a CS_GRAY ColorSpace. + */ + public static final int TYPE_USHORT_GRAY = 11; + + /** + * The Constant TYPE_BYTE_BINARY indicates an opaque byte-packed 1, 2 or 4 + * bit image. The image has an IndexColorModel without alpha. + */ + public static final int TYPE_BYTE_BINARY = 12; + + /** + * The Constant TYPE_BYTE_INDEXED indicates an indexed byte image. + */ + public static final int TYPE_BYTE_INDEXED = 13; + + /** + * The Constant ALPHA_MASK. + */ + private static final int ALPHA_MASK = 0xff000000; + + /** + * The Constant RED_MASK. + */ + private static final int RED_MASK = 0x00ff0000; + + /** + * The Constant GREEN_MASK. + */ + private static final int GREEN_MASK = 0x0000ff00; + + /** + * The Constant BLUE_MASK. + */ + private static final int BLUE_MASK = 0x000000ff; + + /** + * The Constant RED_BGR_MASK. + */ + private static final int RED_BGR_MASK = 0x000000ff; + + /** + * The Constant GREEN_BGR_MASK. + */ + private static final int GREEN_BGR_MASK = 0x0000ff00; + + /** + * The Constant BLUE_BGR_MASK. + */ + private static final int BLUE_BGR_MASK = 0x00ff0000; + + /** + * The Constant RED_565_MASK. + */ + private static final int RED_565_MASK = 0xf800; + + /** + * The Constant GREEN_565_MASK. + */ + private static final int GREEN_565_MASK = 0x07e0; + + /** + * The Constant BLUE_565_MASK. + */ + private static final int BLUE_565_MASK = 0x001f; + + /** + * The Constant RED_555_MASK. + */ + private static final int RED_555_MASK = 0x7c00; + + /** + * The Constant GREEN_555_MASK. + */ + private static final int GREEN_555_MASK = 0x03e0; + + /** + * The Constant BLUE_555_MASK. + */ + private static final int BLUE_555_MASK = 0x001f; + + /** + * The cm. + */ + private ColorModel cm; + + /** + * The raster. + */ + private final WritableRaster raster; + + /** + * The image type. + */ + private final int imageType; + + /** + * The properties. + */ + private Hashtable properties; + + // Surface of the Buffered Image - used for blitting one Buffered Image + // on the other one or on the Component + /** + * The image surf. + */ + private final ImageSurface imageSurf; + + /** + * Instantiates a new BufferedImage with the specified ColorModel, and + * WritableRaster objects. The Raster data can be be divided or multiplied + * by alpha. It depends on the alphaPremultiplied state in the ColorModel. + * + * @param cm + * the ColorModel of the new image. + * @param raster + * the WritableRaster of the new image. + * @param isRasterPremultiplied + * if true the data of the specified Raster is pre-multiplied by + * alpha. + * @param properties + * the properties of new Image. + */ + public BufferedImage(ColorModel cm, WritableRaster raster, boolean isRasterPremultiplied, + Hashtable properties) { + if (!cm.isCompatibleRaster(raster)) { + // awt.4D=The raster is incompatible with this ColorModel + throw new IllegalArgumentException(Messages.getString("awt.4D")); //$NON-NLS-1$ + } + + if (raster.getMinX() != 0 || raster.getMinY() != 0) { + // awt.228=minX or minY of this raster not equal to zero + throw new IllegalArgumentException(Messages.getString("awt.228")); //$NON-NLS-1$ + } + + this.cm = cm; + this.raster = raster; + this.properties = properties; + + coerceData(isRasterPremultiplied); + + imageType = Surface.getType(cm, raster); + + imageSurf = createImageSurface(imageType); + } + + /** + * Instantiates a new BufferedImage with the specified width, height + * predefined image type (TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED) and the + * specified IndexColorModel. + * + * @param width + * the width of new image. + * @param height + * the height of new image. + * @param imageType + * the predefined image type. + * @param cm + * the specified IndexColorModel. + */ + public BufferedImage(int width, int height, int imageType, IndexColorModel cm) { + switch (imageType) { + case TYPE_BYTE_BINARY: + if (cm.hasAlpha()) { + // awt.227=This image type can't have alpha + throw new IllegalArgumentException(Messages.getString("awt.227")); //$NON-NLS-1$ + } + int pixel_bits = 0; + int mapSize = cm.getMapSize(); + if (mapSize <= 2) { + pixel_bits = 1; + } else if (mapSize <= 4) { + pixel_bits = 2; + } else if (mapSize <= 16) { + pixel_bits = 4; + } else { + // awt.221=The imageType is TYPE_BYTE_BINARY and the color + // map has more than 16 entries + throw new IllegalArgumentException(Messages.getString("awt.221")); //$NON-NLS-1$ + } + + raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, width, height, 1, + pixel_bits, null); + break; + + case TYPE_BYTE_INDEXED: + raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, 1, + null); + break; + + default: + // awt.222=The imageType is not TYPE_BYTE_BINARY or + // TYPE_BYTE_INDEXED + throw new IllegalArgumentException(Messages.getString("awt.222")); //$NON-NLS-1$ + + } + + if (!cm.isCompatibleRaster(raster)) { + // awt.223=The imageType is not compatible with ColorModel + throw new IllegalArgumentException(Messages.getString("awt.223")); //$NON-NLS-1$ + } + + this.cm = cm; + this.imageType = imageType; + imageSurf = createImageSurface(imageType); + + } + + /** + * Instantiates a new BufferedImage with the specified width, height and + * predefined image type. + * + * @param width + * the width of new image. + * @param height + * the height of new image. + * @param imageType + * the predefined image type. + */ + public BufferedImage(int width, int height, int imageType) { + + switch (imageType) { + case TYPE_INT_RGB: + cm = new DirectColorModel(24, RED_MASK, GREEN_MASK, BLUE_MASK); + raster = cm.createCompatibleWritableRaster(width, height); + break; + + case TYPE_INT_ARGB: + cm = ColorModel.getRGBdefault(); + raster = cm.createCompatibleWritableRaster(width, height); + break; + + case TYPE_INT_ARGB_PRE: + cm = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, RED_MASK, + GREEN_MASK, BLUE_MASK, ALPHA_MASK, true, DataBuffer.TYPE_INT); + + raster = cm.createCompatibleWritableRaster(width, height); + break; + + case TYPE_INT_BGR: + cm = new DirectColorModel(24, RED_BGR_MASK, GREEN_BGR_MASK, BLUE_BGR_MASK); + + raster = cm.createCompatibleWritableRaster(width, height); + break; + + case TYPE_3BYTE_BGR: { + int bits[] = { + 8, 8, 8 + }; + int bandOffsets[] = { + 2, 1, 0 + }; + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), bits, + false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + + raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, + width * 3, 3, bandOffsets, null); + } + break; + + case TYPE_4BYTE_ABGR: { + int bits[] = { + 8, 8, 8, 8 + }; + int bandOffsets[] = { + 3, 2, 1, 0 + }; + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), bits, + true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); + + raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, + width * 4, 4, bandOffsets, null); + } + break; + + case TYPE_4BYTE_ABGR_PRE: { + int bits[] = { + 8, 8, 8, 8 + }; + int bandOffsets[] = { + 3, 2, 1, 0 + }; + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), bits, + true, true, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); + + raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, + width * 4, 4, bandOffsets, null); + } + break; + + case TYPE_USHORT_565_RGB: + cm = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), 16, + RED_565_MASK, GREEN_565_MASK, BLUE_565_MASK, 0, false, + DataBuffer.TYPE_USHORT); + + raster = cm.createCompatibleWritableRaster(width, height); + break; + + case TYPE_USHORT_555_RGB: + cm = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), 15, + RED_555_MASK, GREEN_555_MASK, BLUE_555_MASK, 0, false, + DataBuffer.TYPE_USHORT); + + raster = cm.createCompatibleWritableRaster(width, height); + break; + + case TYPE_BYTE_GRAY: { + int bits[] = { + 8 + }; + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), bits, + false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + + raster = cm.createCompatibleWritableRaster(width, height); + } + break; + + case TYPE_USHORT_GRAY: { + int bits[] = { + 16 + }; + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), bits, + false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT); + raster = cm.createCompatibleWritableRaster(width, height); + } + break; + + case TYPE_BYTE_BINARY: { + int colorMap[] = { + 0, 0xffffff + }; + cm = new IndexColorModel(1, 2, colorMap, 0, false, -1, DataBuffer.TYPE_BYTE); + + raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, width, height, 1, 1, null); + } + break; + + case TYPE_BYTE_INDEXED: { + int colorMap[] = new int[256]; + int i = 0; + for (int r = 0; r < 256; r += 51) { + for (int g = 0; g < 256; g += 51) { + for (int b = 0; b < 256; b += 51) { + colorMap[i] = (r << 16) | (g << 8) | b; + i++; + } + } + } + + int gray = 0x12; + for (; i < 256; i++, gray += 6) { + colorMap[i] = (gray << 16) | (gray << 8) | gray; + } + cm = new IndexColorModel(8, 256, colorMap, 0, false, -1, DataBuffer.TYPE_BYTE); + raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, 1, + null); + + } + break; + default: + // awt.224=Unknown image type + throw new IllegalArgumentException(Messages.getString("awt.224")); //$NON-NLS-1$ + } + this.imageType = imageType; + imageSurf = createImageSurface(imageType); + } + + @Override + public Object getProperty(String name, ImageObserver observer) { + return getProperty(name); + } + + public Object getProperty(String name) { + if (name == null) { + // awt.225=Property name is null + throw new NullPointerException(Messages.getString("awt.225")); //$NON-NLS-1$ + } + if (properties == null) { + return Image.UndefinedProperty; + } + Object property = properties.get(name); + if (property == null) { + property = Image.UndefinedProperty; + } + return property; + } + + public WritableRaster copyData(WritableRaster outRaster) { + if (outRaster == null) { + outRaster = Raster.createWritableRaster(raster.getSampleModel(), new Point(raster + .getSampleModelTranslateX(), raster.getSampleModelTranslateY())); + } + + int w = outRaster.getWidth(); + int h = outRaster.getHeight(); + int minX = outRaster.getMinX(); + int minY = outRaster.getMinY(); + + Object data = null; + + data = raster.getDataElements(minX, minY, w, h, data); + outRaster.setDataElements(minX, minY, w, h, data); + + return outRaster; + } + + public Raster getData(Rectangle rect) { + int minX = rect.x; + int minY = rect.y; + int w = rect.width; + int h = rect.height; + + SampleModel sm = raster.getSampleModel(); + SampleModel nsm = sm.createCompatibleSampleModel(w, h); + WritableRaster outr = Raster.createWritableRaster(nsm, rect.getLocation()); + Object data = null; + + data = raster.getDataElements(minX, minY, w, h, data); + outr.setDataElements(minX, minY, w, h, data); + return outr; + } + + public Vector getSources() { + return null; + } + + public String[] getPropertyNames() { + if (properties == null) { + return null; + } + Vector v = new Vector(); + for (Enumeration e = properties.keys(); e.hasMoreElements();) { + try { + v.add((String)e.nextElement()); + } catch (ClassCastException ex) { + } + } + int size = v.size(); + if (size > 0) { + String names[] = new String[size]; + for (int i = 0; i < size; i++) { + names[i] = v.elementAt(i); + } + return names; + } + return null; + } + + /** + * Returns the string representation of this BufferedImage object. + * + * @return the string representation of this BufferedImage object. + */ + @Override + public String toString() { + return "BufferedImage@" + Integer.toHexString(hashCode()) + //$NON-NLS-1$ + ": type = " + imageType + " " + cm + " " + raster; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public WritableRaster getWritableTile(int tileX, int tileY) { + return raster; + } + + /** + * Gets the WritableRaster of this BufferedImage. + * + * @return the WritableRaster of this BufferedImage. + */ + public WritableRaster getRaster() { + return raster; + } + + /** + * Gets a WritableRaster object which contains the alpha channel of + * BufferedImage object with ColorModel objects that supports a separate + * alpha channel such as ComponentColorModel or DirectColorModel. + * + * @return the WritableRaster object which contains the alpha channel of + * this BufferedImage. + */ + public WritableRaster getAlphaRaster() { + return cm.getAlphaRaster(raster); + } + + public void removeTileObserver(TileObserver to) { + } + + public void addTileObserver(TileObserver to) { + } + + public SampleModel getSampleModel() { + return raster.getSampleModel(); + } + + public void setData(Raster r) { + + Rectangle from = r.getBounds(); + Rectangle to = raster.getBounds(); + Rectangle intersection = to.intersection(from); + + int minX = intersection.x; + int minY = intersection.y; + int w = intersection.width; + int h = intersection.height; + + Object data = null; + + data = r.getDataElements(minX, minY, w, h, data); + raster.setDataElements(minX, minY, w, h, data); + } + + public Raster getTile(int tileX, int tileY) { + if (tileX == 0 && tileY == 0) { + return raster; + } + // awt.226=Both tileX and tileY are not equal to 0 + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.226")); //$NON-NLS-1$ + } + + public Raster getData() { + int w = raster.getWidth(); + int h = raster.getHeight(); + int minX = raster.getMinX(); + int minY = raster.getMinY(); + + WritableRaster outr = Raster.createWritableRaster(raster.getSampleModel(), new Point(raster + .getSampleModelTranslateX(), raster.getSampleModelTranslateY())); + + Object data = null; + + data = raster.getDataElements(minX, minY, w, h, data); + outr.setDataElements(minX, minY, w, h, data); + + return outr; + } + + @Override + public ImageProducer getSource() { + return new BufferedImageSource(this, properties); + } + + @Override + public int getWidth(ImageObserver observer) { + return raster.getWidth(); + } + + @Override + public int getHeight(ImageObserver observer) { + return raster.getHeight(); + } + + public ColorModel getColorModel() { + return cm; + } + + /** + * Gets the rectangular area of this BufferedImage as a subimage. + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @param w + * the width of the subimage. + * @param h + * the height of the subimage. + * @return the BufferedImage. + */ + public BufferedImage getSubimage(int x, int y, int w, int h) { + WritableRaster wr = raster.createWritableChild(x, y, w, h, 0, 0, null); + return new BufferedImage(cm, wr, cm.isAlphaPremultiplied(), properties); + } + + public Point[] getWritableTileIndices() { + Point points[] = new Point[1]; + points[0] = new Point(0, 0); + return points; + } + + /** + * Creates the Graphics2D object which allows to draw into this + * BufferedImage. + * + * @return the graphics2D object. + */ + public Graphics2D createGraphics() { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + // return ge.createGraphics(this); + // ???AWT hack, FIXME + // return AndroidGraphics2D.getInstance(); + // throw new RuntimeException("Not implemented!"); + return null; + } + + @Override + public Graphics getGraphics() { + return createGraphics(); + } + + /** + * Coerces the data to achieve the state which is specified by the + * isAlphaPremultiplied variable. + * + * @param isAlphaPremultiplied + * the is alpha pre-multiplied state. + */ + public void coerceData(boolean isAlphaPremultiplied) { + if (cm.hasAlpha() && cm.isAlphaPremultiplied() != isAlphaPremultiplied) { + cm = cm.coerceData(raster, isAlphaPremultiplied); + } + } + + /** + * Gets an array of colors in the TYPE_INT_ARGB color model and default sRGB + * color space of the specified area of this BufferedImage. The result array + * is composed by the following algorithm: + *

+ * pixel = rgbArray[offset + (y-startY)*scansize + (x-startX)] + *

+ * + * @param startX + * the start X area coordinate. + * @param startY + * the start Y area coordinate. + * @param w + * the width of the area. + * @param h + * the height of the area. + * @param rgbArray + * the result array will be stored to this array. + * @param offset + * the offset of the rgbArray array. + * @param scansize + * the scanline stride for the rgbArray. + * @return an array of colors for the specified area. + */ + public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, + int scansize) { + if (rgbArray == null) { + rgbArray = new int[offset + h * scansize]; + } + + int off = offset; + for (int y = startY; y < startY + h; y++, off += scansize) { + int i = off; + for (int x = startX; x < startX + w; x++, i++) { + rgbArray[i] = cm.getRGB(raster.getDataElements(x, y, null)); + } + } + return rgbArray; + } + + /** + * Sets RGB values from the specified array to the specified BufferedImage + * area. The pixels are in the default RGB color model (TYPE_INT_ARGB) and + * default sRGB color space. + * + * @param startX + * the start X coordinate. + * @param startY + * the start Y coordinate. + * @param w + * the width of the BufferedImage area. + * @param h + * the height of the BufferedImage area. + * @param rgbArray + * the array of RGB values. + * @param offset + * the offset of the rgbArray array. + * @param scansize + * the scanline stride for the rgbArray. + */ + public void setRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, + int scansize) { + int off = offset; + for (int y = startY; y < startY + h; y++, off += scansize) { + int i = off; + for (int x = startX; x < startX + w; x++, i++) { + raster.setDataElements(x, y, cm.getDataElements(rgbArray[i], null)); + } + } + } + + /** + * Sets a the specified RGB value to the specified pixel of this + * BufferedImage. The pixel should be in the default RGB color model + * (TYPE_INT_ARGB) and default sRGB color space. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param rgb + * the RGB value to be set. + */ + public synchronized void setRGB(int x, int y, int rgb) { + raster.setDataElements(x, y, cm.getDataElements(rgb, null)); + } + + public boolean isTileWritable(int tileX, int tileY) { + if (tileX == 0 && tileY == 0) { + return true; + } + // awt.226=Both tileX and tileY are not equal to 0 + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.226")); //$NON-NLS-1$ + } + + public void releaseWritableTile(int tileX, int tileY) { + } + + /** + * Gets a color in the TYPE_INT_ARGB color model and default sRGB color + * space of the specified pixel. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @return the color of the specified pixel in the TYPE_INT_ARGB color model + * and default sRGB color space. + */ + public int getRGB(int x, int y) { + return cm.getRGB(raster.getDataElements(x, y, null)); + } + + /** + * Returns true if alpha is pre-multiplied, false if alpha is not + * pre-multiplied or there is no alpha. + * + * @return true if alpha is pre-multiplied, false if alpha is not + * pre-multiplied or there is no alpha. + */ + public boolean isAlphaPremultiplied() { + return cm.isAlphaPremultiplied(); + } + + public boolean hasTileWriters() { + return true; + } + + @Override + public void flush() { + imageSurf.dispose(); + } + + public int getWidth() { + return raster.getWidth(); + } + + /** + * Gets the image type. + * + * @return the image type. + */ + public int getType() { + return imageType; + } + + public int getTileWidth() { + return raster.getWidth(); + } + + public int getTileHeight() { + return raster.getHeight(); + } + + public int getTileGridYOffset() { + return raster.getSampleModelTranslateY(); + } + + public int getTileGridXOffset() { + return raster.getSampleModelTranslateX(); + } + + public int getNumYTiles() { + return 1; + } + + public int getNumXTiles() { + return 1; + } + + public int getMinY() { + return raster.getMinY(); + } + + public int getMinX() { + return raster.getMinX(); + } + + public int getMinTileY() { + return 0; + } + + public int getMinTileX() { + return 0; + } + + public int getHeight() { + return raster.getHeight(); + } + + /** + * Creates the image surface. + * + * @param type + * the type. + * @return the image surface. + */ + private ImageSurface createImageSurface(int type) { + return new ImageSurface(getColorModel(), getRaster(), type); + } + + /** + * Gets the image surface. + * + * @return the image surface. + */ + ImageSurface getImageSurface() { + return imageSurf; + } + + public int getTransparency() { + return cm.getTransparency(); + } +} diff --git a/awt/java/awt/image/BufferedImageFilter.java b/awt/java/awt/image/BufferedImageFilter.java new file mode 100644 index 000000000..8b6fcf046 --- /dev/null +++ b/awt/java/awt/image/BufferedImageFilter.java @@ -0,0 +1,397 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package java.awt.image; + +import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The BufferedImageFilter class provides filtering operations to the + * BufferedImage objects using operators which implement BufferedImageOp + * interface. + * + * @since Android 1.0 + */ +public class BufferedImageFilter extends ImageFilter implements Cloneable { + + /** + * The Constant accessor. + */ + private static final AwtImageBackdoorAccessor accessor = AwtImageBackdoorAccessor.getInstance(); + + /** + * The op. + */ + private BufferedImageOp op; + + /** + * The raster. + */ + private WritableRaster raster; + + /** + * The i data. + */ + private int iData[]; + + /** + * The b data. + */ + private byte bData[]; + + /** + * The width. + */ + private int width; + + /** + * The height. + */ + private int height; + + /** + * The cm. + */ + private ColorModel cm; + + /** + * The forced rgb. + */ + private boolean forcedRGB = false; + + /** + * The transfer type. + */ + private int transferType = DataBuffer.TYPE_UNDEFINED; + + /** + * Instantiates a new BufferedImageFilter with the specified BufferedImageOp + * operator. + * + * @param op + * the specified BufferedImageOp operator. + * @throws NullPointerException + * if BufferedImageOp is null. + */ + public BufferedImageFilter(BufferedImageOp op) { + if (op == null) { + throw new NullPointerException(Messages.getString("awt.05")); //$NON-NLS-1$ + } + this.op = op; + } + + /** + * Gets the BufferedImageOp operator associated with this + * BufferedImageFilter object. + * + * @return the BufferedImageOp associated with this BufferedImageFilter + * object. + */ + public BufferedImageOp getBufferedImageOp() { + return op; + } + + @Override + public void setDimensions(int width, int height) { + this.width = width; + this.height = height; + // Stop image consuming if no pixels expected. + if (width <= 0 || height <= 0) { + consumer.imageComplete(ImageConsumer.STATICIMAGEDONE); + reset(); + } + } + + @Override + public void setColorModel(ColorModel model) { + if (this.cm != null && this.cm != model && raster != null) { + forceRGB(); + } else { + this.cm = model; + } + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, + int scansize) { + setPixels(x, y, w, h, model, pixels, off, scansize, true); + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, + int scansize) { + setPixels(x, y, w, h, model, pixels, off, scansize, false); + } + + @Override + public void imageComplete(int status) { + if (status == STATICIMAGEDONE || status == SINGLEFRAMEDONE) { + BufferedImage bim = new BufferedImage(cm, raster, cm.isAlphaPremultiplied, null); + bim = op.filter(bim, null); + DataBuffer dstDb = bim.getRaster().getDataBuffer(); + ColorModel dstCm = bim.getColorModel(); + int dstW = bim.getWidth(); + int dstH = bim.getHeight(); + + consumer.setDimensions(dstW, dstH); + + if (dstDb.getDataType() == DataBuffer.TYPE_INT) { + consumer.setColorModel(dstCm); + consumer.setPixels(0, 0, dstW, dstH, dstCm, accessor.getDataInt(dstDb), 0, dstW); + } else if (dstDb.getDataType() == DataBuffer.TYPE_BYTE) { + consumer.setColorModel(dstCm); + consumer.setPixels(0, 0, dstW, dstH, dstCm, accessor.getDataByte(dstDb), 0, dstW); + } else { + int dstData[] = bim.getRGB(0, 0, dstW, dstH, null, 0, dstW); + dstCm = ColorModel.getRGBdefault(); + consumer.setColorModel(dstCm); + consumer.setPixels(0, 0, dstW, dstH, dstCm, dstData, 0, dstW); + } + } else if (status == IMAGEERROR || status == IMAGEABORTED) { + reset(); + } + + consumer.imageComplete(status); + } + + /** + * Sets the pixels. + * + * @param x + * the x. + * @param y + * the y. + * @param w + * the w. + * @param h + * the h. + * @param model + * the model. + * @param pixels + * the pixels. + * @param off + * the off. + * @param scansize + * the scansize. + * @param isByteData + * the is byte data. + */ + private void setPixels(int x, int y, int w, int h, ColorModel model, Object pixels, int off, + int scansize, boolean isByteData) { + // Check bounds + // Need to copy only the pixels that will fit into the destination area + if (x < 0) { + w -= x; + off += x; + x = 0; + } + + if (y < 0) { + h -= y; + off += y * scansize; + y = 0; + } + + if (x + w > width) { + w = width - x; + } + + if (y + h > height) { + h = height - y; + } + + if (w <= 0 || h <= 0) { + return; + } + + // Check model + if (this.cm == null) { + setColorModel(model); + } else if (model == null) { + model = this.cm; + } else if (!model.equals(this.cm)) { + forceRGB(); + } + + boolean canArraycopy; + // Process pixels + switch (transferType) { + case DataBuffer.TYPE_UNDEFINED: { + if (isByteData) { + transferType = DataBuffer.TYPE_BYTE; + createRaster(transferType); + // bData = new byte[width*height]; + canArraycopy = !forcedRGB; + break; + } + transferType = DataBuffer.TYPE_INT; + createRaster(transferType); + // iData = new int[width*height]; + canArraycopy = !forcedRGB || model.equals(ColorModel.getRGBdefault()); + break; + } // And proceed to copy the pixels + case DataBuffer.TYPE_INT: { + if (isByteData) { // There are int data already but the new data + // are bytes + forceRGB(); + canArraycopy = false; + break; + } else if (!forcedRGB || model.equals(ColorModel.getRGBdefault())) { + canArraycopy = true; + break; + } // Else fallback to the RGB conversion + } + case DataBuffer.TYPE_BYTE: { + if (isByteData && !forcedRGB) { + canArraycopy = true; + break; + } + + // RGB conversion + canArraycopy = false; + break; + } + default: { + throw new IllegalStateException(Messages.getString("awt.06")); //$NON-NLS-1$ + } + } + + off += x; + int maxOffset = off + h * scansize; + int dstOffset = x + y * width; + + if (canArraycopy) { + Object dstArray = isByteData ? (Object)bData : (Object)iData; + for (; off < maxOffset; off += scansize, dstOffset += width) { + System.arraycopy(pixels, off, dstArray, dstOffset, w); + } + } else { + // RGB conversion + for (; off < maxOffset; off += scansize, dstOffset += width) { + int srcPos = off; + int dstPos = dstOffset; + int maxDstPos = dstOffset + w; + for (; dstPos < maxDstPos; dstPos++, srcPos++) { + iData[dstPos] = model.getRGB(isByteData ? ((byte[])pixels)[srcPos] + : ((int[])pixels)[srcPos]); + } + } + } + } + + /** + * Force rgb. + */ + private void forceRGB() { + if (!forcedRGB) { + forcedRGB = true; + int size = width * height; + int rgbData[] = new int[size]; + + if (bData != null) { + for (int i = 0; i < size; i++) { + rgbData[i] = cm.getRGB(bData[i]); + } + } else if (iData != null) { + for (int i = 0; i < size; i++) { + rgbData[i] = cm.getRGB(iData[i]); + } + } + + cm = ColorModel.getRGBdefault(); + DataBufferInt db = new DataBufferInt(rgbData, size); + int masks[] = new int[] { + 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 + }; + raster = Raster.createPackedRaster(db, width, height, width, masks, null); + iData = accessor.getDataInt(db); + bData = null; + transferType = DataBuffer.TYPE_INT; + } + } + + /** + * Reset. + */ + private void reset() { + width = 0; + height = 0; + forcedRGB = false; + cm = null; + iData = null; + bData = null; + transferType = DataBuffer.TYPE_UNDEFINED; + raster = null; + } + + /** + * Creates the raster. + * + * @param dataType + * the data type. + */ + private void createRaster(int dataType) { + boolean createdValidBuffer = false; + try { + raster = cm.createCompatibleWritableRaster(width, height); + int rasterType = raster.getDataBuffer().getDataType(); + if (rasterType == dataType) { + switch (rasterType) { + case DataBuffer.TYPE_INT: { + iData = accessor.getDataInt(raster.getDataBuffer()); + if (iData != null) { + createdValidBuffer = true; + } + break; + } + case DataBuffer.TYPE_BYTE: { + bData = accessor.getDataByte(raster.getDataBuffer()); + if (bData != null) { + createdValidBuffer = true; + } + break; + } + default: + createdValidBuffer = false; + } + + if (cm == ColorModel.getRGBdefault()) { + forcedRGB = true; + } + } else { + createdValidBuffer = false; + } + } catch (Exception e) { + createdValidBuffer = false; + } + + if (createdValidBuffer == false) { + cm = ColorModel.getRGBdefault(); + raster = cm.createCompatibleWritableRaster(width, height); + iData = accessor.getDataInt(raster.getDataBuffer()); + bData = null; + forcedRGB = true; + } + } +} diff --git a/awt/java/awt/image/BufferedImageOp.java b/awt/java/awt/image/BufferedImageOp.java new file mode 100644 index 000000000..883a39d7e --- /dev/null +++ b/awt/java/awt/image/BufferedImageOp.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * The BufferedImageOp interface provides methods for performing transformations + * from source data to destination data for BufferedImage objects. An object + * implementing this interface can be passed into a BufferedImageFilter to + * operate on a BufferedImage. + * + * @since Android 1.0 + */ +public interface BufferedImageOp { + + /** + * Creates a destination image with the specified BufferedImage and + * ColorModel; this destination image is empty and has the correct size and + * number of bands. + * + * @param src + * the source BufferedImage. + * @param destCM + * the destination ColorModel. + * @return the BufferedImage. + */ + public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM); + + /** + * Performs a filter operation on the source BufferedImage and stores the + * resulting BufferedImage to the destination BufferedImage. If the + * destination BufferedImage is null, a BufferedImage with an appropriate + * ColorModel is created. + * + * @param src + * the source BufferedImage. + * @param dest + * the destination BufferedImage, where the result is stored. + * @return the filtered BufferedImage. + */ + public BufferedImage filter(BufferedImage src, BufferedImage dest); + + /** + * Gets the bounds of filtered image. + * + * @param src + * the source BufferedImage to be filtered. + * @return the rectangle bounds of filtered image. + */ + public Rectangle2D getBounds2D(BufferedImage src); + + /** + * Gets the point of the destination image which corresponds to the + * specified point in the source image. + * + * @param srcPt + * the point of the source image. + * @param dstPt + * the point where the result will be stored. + * @return the destination point. + */ + public Point2D getPoint2D(Point2D srcPt, Point2D dstPt); + + /** + * Gets the RenderingHints of the BufferedImageOp. + * + * @return the RenderingHints of the BufferedImageOp. + */ + public RenderingHints getRenderingHints(); +} diff --git a/awt/java/awt/image/ByteLookupTable.java b/awt/java/awt/image/ByteLookupTable.java new file mode 100644 index 000000000..27cee30a1 --- /dev/null +++ b/awt/java/awt/image/ByteLookupTable.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Oct 14, 2005 + */ + +package java.awt.image; + +/** + * The ByteLookupTable class provides functionality for lookup operations, and + * is defined by an input byte array for bands or components of image and an + * offset value. The offset value will be subtracted from the input values + * before indexing the input arrays. The output of a lookup operation is + * represented as an array of unsigned bytes. + * + * @since Android 1.0 + */ +public class ByteLookupTable extends LookupTable { + + /** + * The data. + */ + private byte data[][]; + + /** + * Instantiates a new ByteLookupTable with the specified offset value and + * the specified byte array which represents the lookup table for all bands. + * + * @param offset + * the offset value. + * @param data + * the data array of bytes. + */ + public ByteLookupTable(int offset, byte[] data) { + super(offset, 1); + if (data.length < 1) + throw new IllegalArgumentException("Length of data should not be less then one"); + this.data = new byte[1][data.length]; + // The data array stored as a reference + this.data[0] = data; + } + + /** + * Instantiates a new ByteLookupTable with the specified offset value and + * the specified byte array of arrays which represents the lookup table for + * each band. + * + * @param offset + * the offset value. + * @param data + * the data array of bytes array for each band. + */ + public ByteLookupTable(int offset, byte[][] data) { + super(offset, data.length); + this.data = new byte[data.length][data[0].length]; + for (int i = 0; i < data.length; i++) { + // The data array for each band stored as a reference + this.data[i] = data[i]; + } + } + + /** + * Gets the lookup table of this ByteLookupTable object. If this + * ByteLookupTable object has one byte array for all bands, the returned + * array length is one. + * + * @return the lookup table of this ByteLookupTable object. + */ + public final byte[][] getTable() { + // Returns data by reference + return data; + } + + @Override + public int[] lookupPixel(int[] src, int[] dst) { + if (dst == null) { + dst = new int[src.length]; + } + + int offset = getOffset(); + if (getNumComponents() == 1) { + for (int i = 0; i < src.length; i++) { + dst[i] = data[0][src[i] - offset]; + } + } else { + for (int i = 0; i < getNumComponents(); i++) { + dst[i] = data[i][src[i] - offset]; + } + } + + return dst; + } + + /** + * Returns a byte array which contains samples of the specified pixel which + * is translated with the lookup table of this ByteLookupTable object. The + * resulted array is stored to the dst array. + * + * @param src + * the source array. + * @param dst + * the destination array where the result can be stored. + * @return the byte array of translated samples of a pixel. + */ + public byte[] lookupPixel(byte[] src, byte[] dst) { + if (dst == null) { + dst = new byte[src.length]; + } + + int offset = getOffset(); + if (getNumComponents() == 1) { + for (int i = 0; i < src.length; i++) { + dst[i] = data[0][src[i] - offset]; + } + } else { + for (int i = 0; i < getNumComponents(); i++) { + dst[i] = data[i][src[i] - offset]; + } + } + + return dst; + } +} diff --git a/awt/java/awt/image/ColorConvertOp.java b/awt/java/awt/image/ColorConvertOp.java new file mode 100644 index 000000000..1a1700b32 --- /dev/null +++ b/awt/java/awt/image/ColorConvertOp.java @@ -0,0 +1,710 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.RenderingHints; +import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; + +import org.apache.harmony.awt.gl.color.ColorConverter; +import org.apache.harmony.awt.gl.color.ColorScaler; +import org.apache.harmony.awt.gl.color.ICC_Transform; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The ColorConvertOp class converts the pixels of the data in the source image + * with the specified ColorSpace objects or an array of ICC_Profile objects. The + * result pixels are scaled to the precision of the destination image. + * + * @since Android 1.0 + */ +public class ColorConvertOp implements BufferedImageOp, RasterOp { + // Unused but required by interfaces + /** + * The rendering hints. + */ + RenderingHints renderingHints; + + // Sequence consisting of ColorSpace and ICC_Profile elements + /** + * The conversion sequence. + */ + Object conversionSequence[] = new ICC_Profile[0]; // To eliminate checks for + + // null + + // Not null if ColorConvertOp is constructed from the array of ICC profiles + /** + * The mid profiles. + */ + private ICC_Profile midProfiles[]; + + /** + * The cc. + */ + private final ColorConverter cc = new ColorConverter(); + + /** + * The t creator. + */ + private final ICC_TransfomCreator tCreator = new ICC_TransfomCreator(); + + /** + * The is icc. + */ + private boolean isICC = true; + + // Cached ICC_Transform + /** + * The Class ICC_TransfomCreator. + */ + private class ICC_TransfomCreator { + + /** + * The transform. + */ + private ICC_Transform transform; + + /** + * The max components. + */ + private int maxComponents; + + /** + * For the full ICC case. + * + * @param src + * the src. + * @param dst + * the dst. + * @param convSeq + * the conv seq. + * @return the transform. + */ + public ICC_Transform getTransform(ICC_Profile src, ICC_Profile dst, ICC_Profile convSeq[]) { + if (transform != null && src == transform.getSrc() && dst == transform.getDst()) { + return transform; + } + + int length = convSeq.length; + int srcFlg = 0, dstFlg = 0; + + if (length == 0 || src != convSeq[0]) { + if (src != null) { + srcFlg = 1; // need src profile + } + } + if (length == 0 || dst != convSeq[length - 1]) { + if (dst != null) { + dstFlg = 1; // need dst profile + } + } + + ICC_Profile profiles[]; + int nProfiles = length + srcFlg + dstFlg; + if (nProfiles == length) { + profiles = convSeq; + } else { + profiles = new ICC_Profile[nProfiles]; + int pos = 0; + if (srcFlg != 0) { + profiles[pos++] = src; + } + for (int i = 0; i < length; i++) { + profiles[pos++] = convSeq[i]; + } + if (dstFlg != 0) { + profiles[pos++] = dst; + } + } + + return transform = new ICC_Transform(profiles); + } + + /** + * Used only when there are non-ICC color spaces. Returns sequence of + * non-ICC color spaces and ICC transforms made from src, dst and + * conversionSequence. + * + * @param src + * the src. + * @param dst + * the dst. + * @return the sequence. + */ + public Object[] getSequence(Object src, Object dst) { + ArrayList profiles = new ArrayList(10); + ArrayList sequence = new ArrayList(10); + + // We need this profile anyway + ICC_Profile xyzProfile = ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ); + + Object conversionFirst = null, conversionLast = null; + int conversionLength = conversionSequence.length; + if (conversionLength > 0) { + conversionFirst = conversionSequence[0]; + conversionLast = conversionSequence[conversionLength - 1]; + } + + boolean iccSequenceStarted = false; + + if (src != conversionFirst && src != null) { + if (src instanceof ICC_Profile) { + profiles.add(src); + iccSequenceStarted = true; + } else { + profiles.add(xyzProfile); + sequence.add(src); // Add non-ICC color space to the + // sequence + } + } else { + profiles.add(xyzProfile); + } + + for (int i = 0; i < conversionLength; i++) { + if (conversionSequence[i] instanceof ICC_Profile) { + profiles.add(conversionSequence[i]); + iccSequenceStarted = true; + } else if (iccSequenceStarted) { + profiles.add(xyzProfile); + + // Eliminate same profiles if there are any + // (e.g. xyzProfile may occur several times) + Object prev = profiles.get(0); + for (int k = 1; k < profiles.size(); k++) { + if (prev == profiles.get(k)) { + k--; + profiles.remove(k); + } + prev = profiles.get(k); + } + + // If only one profile left we skip the transform - + // it can be only CIEXYZ + if (profiles.size() > 1) { + sequence.add(new ICC_Transform(profiles.toArray(new ICC_Profile[0]))); + + // Add non-ICC color space to the sequence + sequence.add(conversionSequence[i]); + } + + profiles.clear(); + profiles.add(xyzProfile); + iccSequenceStarted = false; // Sequence of ICC profiles is + // processed + } else { // Add non-ICC color space to the sequence + sequence.add(conversionSequence[i]); + } + } + + if (dst != conversionLast && dst != null) { // Add last profile if + // needed + if (dst instanceof ICC_Profile) { + profiles.add(dst); + iccSequenceStarted = true; + } else if (iccSequenceStarted) { + profiles.add(xyzProfile); + } else { + sequence.add(dst); // Add last non-ICC color space to the + // sequence + } + } + + if (iccSequenceStarted) { // Make last transform if needed + sequence.add(new ICC_Transform(profiles.toArray(new ICC_Profile[0]))); + if (dst != null && !(dst instanceof ICC_Profile)) { + sequence.add(dst); // Add last non-ICC color space to the + // sequence + } + } + + // Calculate max number of components + // This number will be used for memory allocation + maxComponents = 0; + Object o; + for (int i = 0, size = sequence.size(); i < size; i++) { + o = sequence.get(i); + if (o instanceof ICC_Transform) { + ICC_Transform t = (ICC_Transform)o; + maxComponents = (maxComponents > t.getNumInputChannels() + 1) ? maxComponents + : t.getNumInputChannels() + 1; + maxComponents = (maxComponents > t.getNumOutputChannels() + 1) ? maxComponents + : t.getNumOutputChannels() + 1; + } else { + ColorSpace cs = (ColorSpace)o; + maxComponents = (maxComponents > cs.getNumComponents() + 1) ? maxComponents + : cs.getNumComponents() + 1; + } + } + + return sequence.toArray(); + } + } + + /** + * Instantiates a new ColorConvertOp object using two specified ColorSpace + * objects. + * + * @param srcCS + * the source ColorSpace. + * @param dstCS + * the destination ColorSpace. + * @param hints + * the RenderingHints object used for the color conversion, or + * null. + */ + public ColorConvertOp(ColorSpace srcCS, ColorSpace dstCS, RenderingHints hints) { + if (srcCS == null || dstCS == null) { + throw new NullPointerException(Messages.getString("awt.25B")); //$NON-NLS-1$ + } + + renderingHints = hints; + + boolean srcICC = srcCS instanceof ICC_ColorSpace; + boolean dstICC = dstCS instanceof ICC_ColorSpace; + + if (srcICC && dstICC) { + conversionSequence = new ICC_Profile[2]; + } else { + conversionSequence = new Object[2]; + isICC = false; + } + + if (srcICC) { + conversionSequence[0] = ((ICC_ColorSpace)srcCS).getProfile(); + } else { + conversionSequence[0] = srcCS; + } + + if (dstICC) { + conversionSequence[1] = ((ICC_ColorSpace)dstCS).getProfile(); + } else { + conversionSequence[1] = dstCS; + } + } + + /** + * Instantiates a new ColorConvertOp object from the specified ICC_Profile + * objects. + * + * @param profiles + * the array of ICC_Profile objects. + * @param hints + * the RenderingHints object used for the color conversion, or + * null. + */ + public ColorConvertOp(ICC_Profile profiles[], RenderingHints hints) { + if (profiles == null) { + throw new NullPointerException(Messages.getString("awt.25C")); //$NON-NLS-1$ + } + + renderingHints = hints; + + // This array is not used in the program logic, so don't need to copy it + // Store it only to return back + midProfiles = profiles; + + conversionSequence = new ICC_Profile[midProfiles.length]; + + // Add profiles to the conversion sequence + for (int i = 0, length = midProfiles.length; i < length; i++) { + conversionSequence[i] = midProfiles[i]; + } + } + + /** + * Instantiates a new ColorConvertOp object using the specified ColorSpace + * object. + * + * @param cs + * the destination ColorSpace or an intermediate ColorSpace. + * @param hints + * the RenderingHints object used for the color conversion, or + * null. + */ + public ColorConvertOp(ColorSpace cs, RenderingHints hints) { + if (cs == null) { + throw new NullPointerException(Messages.getString("awt.25B")); //$NON-NLS-1$ + } + + renderingHints = hints; + + if (cs instanceof ICC_ColorSpace) { + conversionSequence = new ICC_Profile[1]; + conversionSequence[0] = ((ICC_ColorSpace)cs).getProfile(); + } else { + conversionSequence = new Object[1]; + conversionSequence[0] = cs; + isICC = false; + } + } + + /** + * Instantiates a new ColorConvertOp object which converts from a source + * color space to a destination color space. + * + * @param hints + * the RenderingHints object used for the color conversion, or + * null. + */ + public ColorConvertOp(RenderingHints hints) { + renderingHints = hints; + } + + public final WritableRaster filter(Raster src, WritableRaster dst) { + if (conversionSequence.length < 2) { + throw new IllegalArgumentException(Messages.getString("awt.25D")); //$NON-NLS-1$ + } + + ICC_Profile srcPf = null, dstPf = null; // unused if isICC is false + int nSrcColorComps, nDstColorComps; + Object first = conversionSequence[0]; + Object last = conversionSequence[conversionSequence.length - 1]; + + // Get the number of input/output color components + if (isICC) { + srcPf = (ICC_Profile)first; + dstPf = (ICC_Profile)last; + nSrcColorComps = srcPf.getNumComponents(); + nDstColorComps = dstPf.getNumComponents(); + } else { + if (first instanceof ICC_Profile) { + srcPf = (ICC_Profile)first; + nSrcColorComps = srcPf.getNumComponents(); + } else { + nSrcColorComps = ((ColorSpace)first).getNumComponents(); + } + + if (last instanceof ICC_Profile) { + dstPf = (ICC_Profile)last; + nDstColorComps = dstPf.getNumComponents(); + } else { + nDstColorComps = ((ColorSpace)last).getNumComponents(); + } + } + + // Check that source and destination rasters are compatible with + // transforms and with each other + if (src.getNumBands() != nSrcColorComps) { + // awt.25E=Incorrect number of source raster bands. Should be equal + // to the number of color components of source colorspace. + throw new IllegalArgumentException(Messages.getString("awt.25E")); //$NON-NLS-1$ + } + + if (dst != null) { // Check destination raster + if (dst.getNumBands() != nDstColorComps) { + // awt.25F=Incorrect number of destination raster bands. Should + // be equal to the number of color components of destination + // colorspace. + throw new IllegalArgumentException(Messages.getString("awt.25F")); //$NON-NLS-1$ + } + + if (src.getWidth() != dst.getWidth() || src.getHeight() != dst.getHeight()) { + throw new IllegalArgumentException(Messages.getString("awt.260")); //$NON-NLS-1$ + } + + } else { + dst = createCompatibleDestRaster(src); + } + + if (isICC) { + // Create transform + ICC_Transform t = tCreator + .getTransform(srcPf, dstPf, (ICC_Profile[])conversionSequence); + cc.translateColor(t, src, dst); + } else { + Object[] sequence = tCreator.getSequence(null, null); + + // Get data from the source raster + ColorScaler scaler = new ColorScaler(); + scaler.loadScalingData(src, null); + float tmpData[][] = scaler.scaleNormalize(src); + + // Get source and destination color spaces + ColorSpace srcCS = (srcPf == null) ? (ColorSpace)first : new ICC_ColorSpace(srcPf); + ColorSpace dstCS = (dstPf == null) ? (ColorSpace)last : new ICC_ColorSpace(dstPf); + + applySequence(sequence, tmpData, srcCS, dstCS); + + scaler.loadScalingData(dst, null); + scaler.unscaleNormalized(dst, tmpData); + } + + return dst; + } + + public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) { + // If destination color model is passed only one line needed + if (destCM != null) { + return new BufferedImage(destCM, destCM.createCompatibleWritableRaster(src.getWidth(), + src.getHeight()), destCM.isAlphaPremultiplied(), null); + } + + int nSpaces = conversionSequence.length; + + if (nSpaces < 1) { + throw new IllegalArgumentException(Messages.getString("awt.261")); //$NON-NLS-1$ + } + + // Get destination color space + Object destination = conversionSequence[nSpaces - 1]; + ColorSpace dstCS = (destination instanceof ColorSpace) ? (ColorSpace)destination + : new ICC_ColorSpace((ICC_Profile)destination); + + ColorModel srcCM = src.getColorModel(); + ColorModel dstCM = new ComponentColorModel(dstCS, srcCM.hasAlpha(), srcCM + .isAlphaPremultiplied(), srcCM.getTransparency(), srcCM.getTransferType()); + + return new BufferedImage(dstCM, destCM.createCompatibleWritableRaster(src.getWidth(), src + .getHeight()), destCM.isAlphaPremultiplied(), null); + } + + public final BufferedImage filter(BufferedImage src, BufferedImage dst) { + if (dst == null && conversionSequence.length < 1) { + throw new IllegalArgumentException(Messages.getString("awt.262")); //$NON-NLS-1$ + } + + ColorModel srcCM = src.getColorModel(); + // First handle index color model + if (srcCM instanceof IndexColorModel) { + src = ((IndexColorModel)srcCM).convertToIntDiscrete(src.getRaster(), false); + } + ColorSpace srcCS = srcCM.getColorSpace(); + + BufferedImage res; + boolean isDstIndex = false; + if (dst != null) { + + if (src.getWidth() != dst.getWidth() || src.getHeight() != dst.getHeight()) { + throw new IllegalArgumentException(Messages.getString("awt.263")); //$NON-NLS-1$ + } + + if (dst.getColorModel() instanceof IndexColorModel) { + isDstIndex = true; + res = createCompatibleDestImage(src, null); + } else { + res = dst; + } + } else { + res = createCompatibleDestImage(src, null); + } + ColorModel dstCM = res.getColorModel(); + ColorSpace dstCS = dstCM.getColorSpace(); + + ICC_Profile srcPf = null, dstPf = null; + if (srcCS instanceof ICC_ColorSpace) { + srcPf = ((ICC_ColorSpace)srcCS).getProfile(); + } + if (dstCS instanceof ICC_ColorSpace) { + dstPf = ((ICC_ColorSpace)dstCS).getProfile(); + } + + boolean isFullICC = isICC && srcPf != null && dstPf != null; + + if (isFullICC) { + ICC_Transform t = tCreator + .getTransform(srcPf, dstPf, (ICC_Profile[])conversionSequence); + cc.translateColor(t, src, res); + } else { // Perform non-ICC transform + Object sequence[] = tCreator.getSequence(srcPf == null ? (Object)srcCS : srcPf, + dstPf == null ? (Object)dstCS : dstPf); + + int srcW = src.getWidth(); + int srcH = src.getHeight(); + int numPixels = srcW * srcH; + + // Load all pixel data into array tmpData + float tmpData[][] = new float[numPixels][tCreator.maxComponents]; + for (int row = 0, dataPos = 0; row < srcW; row++) { + for (int col = 0; col < srcH; col++) { + tmpData[dataPos] = srcCM.getNormalizedComponents(src.getRaster() + .getDataElements(row, col, null), tmpData[dataPos], 0); + dataPos++; + } + } + + // Copy alpha channel if needed + float alpha[] = null; + int alphaIdx = srcCM.numComponents - 1; + if (srcCM.hasAlpha() && dstCM.hasAlpha()) { + alpha = new float[numPixels]; + for (int i = 0; i < numPixels; i++) { + alpha[i] = tmpData[i][alphaIdx]; + } + } + + // Translate colors + applySequence(sequence, tmpData, srcCS, dstCS); + + // Copy alpha if needed + if (dstCM.hasAlpha()) { + alphaIdx = dstCM.numComponents - 1; + if (alpha != null) { + for (int i = 0; i < numPixels; i++) { + tmpData[i][alphaIdx] = alpha[i]; + } + } else { + for (int i = 0; i < numPixels; i++) { + tmpData[i][alphaIdx] = 1f; + } + } + } + + // Store data back to the image + for (int row = 0, dataPos = 0; row < srcW; row++) { + for (int col = 0; col < srcH; col++) { + res.getRaster().setDataElements(row, col, + dstCM.getDataElements(tmpData[dataPos++], 0, null)); + } + } + } + + if (isDstIndex) { // Convert image into indexed color + Graphics2D g2d = dst.createGraphics(); + g2d.drawImage(res, 0, 0, null); + g2d.dispose(); + return dst; + } + + return res; + } + + /** + * Apply sequence. + * + * @param sequence + * the sequence. + * @param tmpData + * the tmp data. + * @param srcCS + * the src cs. + * @param dstCS + * the dst cs. + */ + private void applySequence(Object sequence[], float tmpData[][], ColorSpace srcCS, + ColorSpace dstCS) { + ColorSpace xyzCS = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); + + int numPixels = tmpData.length; + + // First transform... + if (sequence[0] instanceof ICC_Transform) { // ICC + ICC_Transform t = (ICC_Transform)sequence[0]; + cc.translateColor(t, tmpData, srcCS, xyzCS, numPixels); + } else { // non ICC + for (int k = 0; k < numPixels; k++) { + tmpData[k] = srcCS.toCIEXYZ(tmpData[k]); + } + cc.loadScalingData(xyzCS); // prepare for scaling XYZ + } + + for (Object element : sequence) { + if (element instanceof ICC_Transform) { + ICC_Transform t = (ICC_Transform)element; + cc.translateColor(t, tmpData, null, null, numPixels); + } else { + ColorSpace cs = (ColorSpace)element; + for (int k = 0; k < numPixels; k++) { + tmpData[k] = cs.fromCIEXYZ(tmpData[k]); + tmpData[k] = cs.toCIEXYZ(tmpData[k]); + } + } + } + + // Last transform... + if (sequence[sequence.length - 1] instanceof ICC_Transform) { // ICC + ICC_Transform t = (ICC_Transform)sequence[sequence.length - 1]; + cc.translateColor(t, tmpData, xyzCS, dstCS, numPixels); + } else { // non ICC + for (int k = 0; k < numPixels; k++) { + tmpData[k] = dstCS.fromCIEXYZ(tmpData[k]); + } + } + } + + public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { + if (dstPt != null) { + dstPt.setLocation(srcPt); + return dstPt; + } + return new Point2D.Float((float)srcPt.getX(), (float)srcPt.getY()); + } + + public WritableRaster createCompatibleDestRaster(Raster src) { + int nComps = 0; + int nSpaces = conversionSequence.length; + + if (nSpaces < 2) { + throw new IllegalArgumentException(Messages.getString("awt.261")); //$NON-NLS-1$ + } + + Object lastCS = conversionSequence[nSpaces - 1]; + if (lastCS instanceof ColorSpace) { + nComps = ((ColorSpace)lastCS).getNumComponents(); + } else { + nComps = ((ICC_Profile)lastCS).getNumComponents(); + } + + // Calculate correct data type + int dstDataType = src.getDataBuffer().getDataType(); + if (dstDataType != DataBuffer.TYPE_BYTE && dstDataType != DataBuffer.TYPE_SHORT) { + dstDataType = DataBuffer.TYPE_SHORT; + } + + return Raster.createInterleavedRaster(dstDataType, src.getWidth(), src.getHeight(), nComps, + new Point(src.getMinX(), src.getMinY())); + } + + public final Rectangle2D getBounds2D(Raster src) { + return src.getBounds(); + } + + public final Rectangle2D getBounds2D(BufferedImage src) { + return src.getRaster().getBounds(); + } + + /** + * Gets an array of ICC_Profiles objects which constructs this + * ColorConvertOp object or returns null if this ColorConvertOp is not + * constructed from array of ICC_Profiles. + * + * @return an array of ICC_Profiles objects which constructs this + * ColorConvertOp object or returns null if this ColorConvertOp is + * not constructed from array of ICC_Profiles. + */ + public final ICC_Profile[] getICC_Profiles() { + if (midProfiles != null) { + return midProfiles; + } + return null; + } + + public final RenderingHints getRenderingHints() { + return renderingHints; + } +} diff --git a/awt/java/awt/image/ColorModel.java b/awt/java/awt/image/ColorModel.java new file mode 100644 index 000000000..1b084e121 --- /dev/null +++ b/awt/java/awt/image/ColorModel.java @@ -0,0 +1,964 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.util.Arrays; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The class ColorModel. + * + * @since Android 1.0 + */ +public abstract class ColorModel implements Transparency { + + /** + * The pixel_bits. + */ + protected int pixel_bits; // Pixel length in bits + + /** + * The transfer type. + */ + protected int transferType; + + /** + * The cs. + */ + ColorSpace cs; + + /** + * The has alpha. + */ + boolean hasAlpha; + + /** + * The is alpha premultiplied. + */ + boolean isAlphaPremultiplied; + + /** + * The transparency. + */ + int transparency; + + /** + * The num color components. + */ + int numColorComponents; + + /** + * The num components. + */ + int numComponents; + + /** + * The bits. + */ + int[] bits; // Array of components masks + + /** + * The max values. + */ + int[] maxValues = null; // Max values that may be represent by color + + // components + + /** + * The max bit length. + */ + int maxBitLength; // Max length color components in bits + + /** + * The RG bdefault. + */ + private static ColorModel RGBdefault; + + /** + * Instantiates a new color model with the specified values. + * + * @param pixel_bits + * the pixel length in bits. + * @param bits + * the array of component masks. + * @param cspace + * the color space. + * @param hasAlpha + * whether the color model has alpha. + * @param isAlphaPremultiplied + * whether the alpha is pre-multiplied. + * @param transparency + * the transparency strategy, @see java.awt.Transparency. + * @param transferType + * the transfer type (primitive java type to use for the + * components). + */ + protected ColorModel(int pixel_bits, int[] bits, ColorSpace cspace, boolean hasAlpha, + boolean isAlphaPremultiplied, int transparency, int transferType) { + + if (pixel_bits < 1) { + // awt.26B=The number of bits in the pixel values is less than 1 + throw new IllegalArgumentException(Messages.getString("awt.26B")); //$NON-NLS-1$ + } + + if (bits == null) { + // awt.26C=bits is null + throw new NullPointerException(Messages.getString("awt.26C")); //$NON-NLS-1$ + } + + int sum = 0; + for (int element : bits) { + if (element < 0) { + // awt.26D=The elements in bits is less than 0 + throw new IllegalArgumentException(Messages.getString("awt.26D")); //$NON-NLS-1$ + } + sum += element; + } + + if (sum < 1) { + // awt.26E=The sum of the number of bits in bits is less than 1 + throw new NullPointerException(Messages.getString("awt.26E")); //$NON-NLS-1$ + } + + if (cspace == null) { + // awt.26F=The cspace is null + throw new IllegalArgumentException(Messages.getString("awt.26F")); //$NON-NLS-1$ + } + + if (transparency < Transparency.OPAQUE || transparency > Transparency.TRANSLUCENT) { + // awt.270=The transparency is not a valid value + throw new IllegalArgumentException(Messages.getString("awt.270")); //$NON-NLS-1$ + } + + this.pixel_bits = pixel_bits; + this.bits = bits.clone(); + + maxValues = new int[bits.length]; + maxBitLength = 0; + for (int i = 0; i < maxValues.length; i++) { + maxValues[i] = (1 << bits[i]) - 1; + if (bits[i] > maxBitLength) { + maxBitLength = bits[i]; + } + } + + cs = cspace; + this.hasAlpha = hasAlpha; + this.isAlphaPremultiplied = isAlphaPremultiplied; + numColorComponents = cs.getNumComponents(); + + if (hasAlpha) { + numComponents = numColorComponents + 1; + } else { + numComponents = numColorComponents; + } + + this.transparency = transparency; + this.transferType = transferType; + + } + + /** + * Instantiates a new color model with the specified pixel bit depth. The + * transferType is chosen based on the pixel bits, and the other data fields + * are given default values. + * + * @param bits + * the array of component masks. + */ + public ColorModel(int bits) { + + if (bits < 1) { + // awt.271=The number of bits in bits is less than 1 + throw new IllegalArgumentException(Messages.getString("awt.271")); //$NON-NLS-1$ + } + + pixel_bits = bits; + transferType = getTransferType(bits); + cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); + hasAlpha = true; + isAlphaPremultiplied = false; + transparency = Transparency.TRANSLUCENT; + + numColorComponents = 3; + numComponents = 4; + + this.bits = null; + } + + /** + * Gets the data elements from the specified component array, transforming + * them according to rules of the color model. + * + * @param components + * the components. + * @param offset + * the offset in the normComponents array. + * @param obj + * the array that the result is written to: an array of values + * whose length must be the number of components used by the + * color model and whose type depends on the transfer type (based + * on the pixel bit depth), or null to have the appropriate array + * created. + * @return the array of data elements. + */ + public Object getDataElements(int[] components, int offset, Object obj) { + throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$ + "supported by this ColorModel"); //$NON-NLS-1$ + } + + /** + * Gets the data elements from the specified array of normalized components. + * + * @param normComponents + * the array normalized components. + * @param normOffset + * the offset in the normComponents array. + * @param obj + * the array that the result is written to: an array of values + * whose length must be the number of components used by the + * color model and whose type depends on the transfer type (based + * on the pixel bit depth), or null to have the appropriate array + * created. + * @return the array of data elements. + */ + public Object getDataElements(float[] normComponents, int normOffset, Object obj) { + int unnormComponents[] = getUnnormalizedComponents(normComponents, normOffset, null, 0); + return getDataElements(unnormComponents, 0, obj); + } + + /** + * Gets the data elements corresponding to the pixel determined by the RGB + * data. + * + * @param rgb + * the RGB integer value that defines the pixel. + * @param pixel + * the array that the result is written to: an array of values + * whose length must be the number of components used by the + * color model and whose type depends on the transfer type (based + * on the pixel bit depth), or null to have the appropriate array + * created. + * @return the array of data elements. + */ + public Object getDataElements(int rgb, Object pixel) { + throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$ + "supported by this ColorModel"); //$NON-NLS-1$ + } + + /** + * Gets the child raster corresponding to the alpha channel of the specified + * writable raster, or null if alpha is not supported. + * + * @param raster + * the raster. + * @return the alpha raster. + */ + public WritableRaster getAlphaRaster(WritableRaster raster) { + return null; + } + + /** + * Creates a new color model by coercing the data in the writable raster in + * accordance with the alpha strategy of this color model. + * + * @param raster + * the raster. + * @param isAlphaPremultiplied + * whether the alpha is pre-multiplied in this color model + * @return the new color model. + */ + public ColorModel coerceData(WritableRaster raster, boolean isAlphaPremultiplied) { + throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$ + "supported by this ColorModel"); //$NON-NLS-1$ + } + + @Override + public String toString() { + // The output format based on 1.5 release behavior. + // It could be reveled such way: + // ColorModel cm = new + // ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB, + // false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + // System.out.println(cm.toString()); + return "ColorModel: Color Space = " + cs.toString() + "; has alpha = " //$NON-NLS-1$ //$NON-NLS-2$ + + hasAlpha + "; is alpha premultipied = " //$NON-NLS-1$ + + isAlphaPremultiplied + "; transparency = " + transparency //$NON-NLS-1$ + + "; number color components = " + numColorComponents //$NON-NLS-1$ + + "; pixel bits = " + pixel_bits + "; transfer type = " //$NON-NLS-1$ //$NON-NLS-2$ + + transferType; + } + + /** + * Gets the components of the pixel determined by the data array. + * + * @param pixel + * the data array that defines the pixel (whose primitive type + * corresponds to the pixel length in bits. + * @see ColorModel#getTransferType() + * @param components + * the the array where the resulting components are written (or + * null to prompt the method to create the return array). + * @param offset + * the offset that tells where the results should be written in + * the return array. + * @return the array of components. + */ + public int[] getComponents(Object pixel, int[] components, int offset) { + throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$ + "supported by this ColorModel"); //$NON-NLS-1$ + } + + /** + * Gets the normalized components of the pixel determined by the data array. + * + * @param pixel + * the data array that defines the pixel (whose primitive type + * corresponds to the pixel length in bits. + * @see ColorModel#getTransferType() + * @param normComponents + * the array where the resulting normalized components are + * written (or null to prompt the method to create the return + * array). + * @param normOffset + * the offset that tells where the results should be written in + * the return array. + * @return the array of normalized components. + */ + public float[] getNormalizedComponents(Object pixel, float[] normComponents, int normOffset) { + + if (pixel == null) { + // awt.294=pixel is null + throw new NullPointerException(Messages.getString("awt.294")); //$NON-NLS-1$ + } + + int unnormComponents[] = getComponents(pixel, null, 0); + return getNormalizedComponents(unnormComponents, 0, normComponents, normOffset); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ColorModel)) { + return false; + } + ColorModel cm = (ColorModel)obj; + + return (pixel_bits == cm.getPixelSize() && transferType == cm.getTransferType() + && cs.getType() == cm.getColorSpace().getType() && hasAlpha == cm.hasAlpha() + && isAlphaPremultiplied == cm.isAlphaPremultiplied() + && transparency == cm.getTransparency() + && numColorComponents == cm.getNumColorComponents() + && numComponents == cm.getNumComponents() && Arrays.equals(bits, cm + .getComponentSize())); + } + + /** + * Gets the red component of the pixel determined by the data array. + * + * @param inData + * the data array that defines the pixel (whose primitive type + * corresponds to the pixel length in bits. + * @see ColorModel#getTransferType() + * @return the red. + */ + public int getRed(Object inData) { + return getRed(constructPixel(inData)); + } + + /** + * Gets the RGB integer value corresponding to the pixel defined by the data + * array. + * + * @param inData + * the data array that defines the pixel (whose primitive type + * corresponds to the pixel length in bits. + * @see ColorModel#getTransferType() + * @return the integer value that gives the pixel's colors in RGB format. + */ + public int getRGB(Object inData) { + return (getAlpha(inData) << 24 | getRed(inData) << 16 | getGreen(inData) << 8 | getBlue(inData)); + } + + /** + * Gets the green component of the pixel defined by the data array. + * + * @param inData + * the data array that defines the pixel (whose primitive type + * corresponds to the pixel length in bits. + * @see ColorModel#getTransferType() + * @return the green. + */ + public int getGreen(Object inData) { + return getGreen(constructPixel(inData)); + } + + /** + * Gets the blue component of the pixel defined by the data array. + * + * @param inData + * the data array that defines the pixel (whose primitive type + * corresponds to the pixel length in bits. + * @see ColorModel#getTransferType() + * @return the blue. + */ + public int getBlue(Object inData) { + return getBlue(constructPixel(inData)); + } + + /** + * Gets the alpha component of the pixel defined by the data array. + * + * @param inData + * the data array that defines the pixel (whose primitive type + * corresponds to the pixel length in bits. + * @see ColorModel#getTransferType() + * @return the alpha. + */ + public int getAlpha(Object inData) { + return getAlpha(constructPixel(inData)); + } + + /** + * Creates a compatible writable raster. + * + * @param w + * the width of the desired writable raster. + * @param h + * the height of the desired writable raster. + * @return the writable raster. + */ + public WritableRaster createCompatibleWritableRaster(int w, int h) { + throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$ + "supported by this ColorModel"); //$NON-NLS-1$ + } + + /** + * Checks if the sample model is compatible with this color model. + * + * @param sm + * the sample model. + * @return true, if the sample model is compatible with this color model. + */ + public boolean isCompatibleSampleModel(SampleModel sm) { + throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$ + "supported by this ColorModel"); //$NON-NLS-1$ + } + + /** + * Creates the compatible sample model. + * + * @param w + * the width of the desired sample model. + * @param h + * the height of the desired sample model. + * @return the sample model. + */ + public SampleModel createCompatibleSampleModel(int w, int h) { + throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$ + "supported by this ColorModel"); //$NON-NLS-1$ + } + + /** + * Checks if the specified raster is compatible with this color model. + * + * @param raster + * the raster to inspect. + * @return true, if the raster is compatible with this color model. + */ + public boolean isCompatibleRaster(Raster raster) { + throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$ + "supported by this ColorModel"); //$NON-NLS-1$ + } + + /** + * Gets the color space of this color model. + * + * @return the color space. + */ + public final ColorSpace getColorSpace() { + return cs; + } + + /** + * Gets the normalized components corresponding to the specified + * unnormalized components. + * + * @param components + * the array of unnormalized components. + * @param offset + * the offset where the components should be read from the array + * of unnormalized components. + * @param normComponents + * the array where the resulting normalized components are + * written (or null to prompt the method to create the return + * array). + * @param normOffset + * the offset that tells where the results should be written in + * the return array. + * @return the normalized components. + */ + public float[] getNormalizedComponents(int[] components, int offset, float normComponents[], + int normOffset) { + if (bits == null) { + // awt.26C=bits is null + throw new UnsupportedOperationException(Messages.getString("awt.26C")); //$NON-NLS-1$ + } + + if (normComponents == null) { + normComponents = new float[numComponents + normOffset]; + } + + if (hasAlpha && isAlphaPremultiplied) { + float normAlpha = (float)components[offset + numColorComponents] + / maxValues[numColorComponents]; + if (normAlpha != 0.0f) { + for (int i = 0; i < numColorComponents; i++) { + normComponents[normOffset + i] = components[offset + i] + / (normAlpha * maxValues[i]); + } + normComponents[normOffset + numColorComponents] = normAlpha; + } else { + for (int i = 0; i < numComponents; i++) { + normComponents[normOffset + i] = 0.0f; + } + } + } else { + for (int i = 0; i < numComponents; i++) { + normComponents[normOffset + i] = (float)components[offset + i] / maxValues[i]; + } + } + + return normComponents; + } + + /** + * Gets the data element corresponding to the unnormalized components. + * + * @param components + * the components. + * @param offset + * the offset to start reading the components from the array of + * components. + * @return the data element. + */ + public int getDataElement(int[] components, int offset) { + throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$ + "supported by this ColorModel"); //$NON-NLS-1$ + } + + /** + * Gets the unnormalized components corresponding to the specified + * normalized components. + * + * @param normComponents + * the array of normalized components. + * @param normOffset + * the offset where the components should be read from the array + * of normalized components. + * @param components + * the array where the resulting unnormalized components are + * written (or null to prompt the method to create the return + * array). + * @param offset + * the offset that tells where the results should be written in + * the return array. + * @return the unnormalized components. + */ + public int[] getUnnormalizedComponents(float normComponents[], int normOffset, + int components[], int offset) { + + if (bits == null) { + // awt.26C=bits is null + throw new UnsupportedOperationException(Messages.getString("awt.26C")); //$NON-NLS-1$ + } + + if (normComponents.length - normOffset < numComponents) { + // awt.273=The length of normComponents minus normOffset is less + // than numComponents + throw new IllegalArgumentException(Messages.getString("awt.273")); //$NON-NLS-1$ + } + + if (components == null) { + components = new int[numComponents + offset]; + } else { + if (components.length - offset < numComponents) { + // awt.272=The length of components minus offset is less than + // numComponents + throw new IllegalArgumentException(Messages.getString("awt.272")); //$NON-NLS-1$ + } + } + + if (hasAlpha && isAlphaPremultiplied) { + float alpha = normComponents[normOffset + numColorComponents]; + for (int i = 0; i < numColorComponents; i++) { + components[offset + i] = (int)(normComponents[normOffset + i] * maxValues[i] + * alpha + 0.5f); + } + components[offset + numColorComponents] = (int)(normComponents[normOffset + + numColorComponents] + * maxValues[numColorComponents] + 0.5f); + } else { + for (int i = 0; i < numComponents; i++) { + components[offset + i] = (int)(normComponents[normOffset + i] * maxValues[i] + 0.5f); + } + } + + return components; + } + + /** + * Gets the data element corresponding to the normalized components. + * + * @param normComponents + * the normalized components. + * @param normOffset + * the offset where the normalized components should be read from + * the normalized component array. + * @return the data element. + */ + public int getDataElement(float normComponents[], int normOffset) { + int unnormComponents[] = getUnnormalizedComponents(normComponents, normOffset, null, 0); + return getDataElement(unnormComponents, 0); + } + + /** + * Takes a pixel whose data is defined by an integer, and writes the + * corresponding components into the components array, starting from the + * index offset. + * + * @param pixel + * the pixel data. + * @param components + * the data array to write the components to (or null to have the + * method create the return array). + * @param offset + * the offset that determines where the results are written in + * the components array. + * @return the array of components corresponding to the pixel. + */ + public int[] getComponents(int pixel, int components[], int offset) { + throw new UnsupportedOperationException("This method is not " + //$NON-NLS-1$ + "supported by this ColorModel"); //$NON-NLS-1$ + } + + /** + * Gets the red component of the pixel determined by the pixel data. + * + * @param pixel + * the pixel. + * @return the red component of the given pixel. + */ + public abstract int getRed(int pixel); + + /** + * Takes the pixel data and returns the integer value corresponding to the + * pixel's color in RGB format. + * + * @param pixel + * the pixel data. + * @return the corresponding RGB integer value. + */ + public int getRGB(int pixel) { + return (getAlpha(pixel) << 24 | getRed(pixel) << 16 | getGreen(pixel) << 8 | getBlue(pixel)); + } + + /** + * Gets the green component of the pixel determined by the pixel data. + * + * @param pixel + * the pixel. + * @return the green component of the given pixel. + */ + public abstract int getGreen(int pixel); + + /** + * Gets the size of the desired component of this color model. + * + * @param componentIdx + * the index that determines which component size to get. + * @return the component size corresponding to the index. + * @throws NullPointerException + * if this color model doesn't support an array of separate + * components. + * @throws ArrayIndexOutOfBoundsException + * if the index is negative or greater than or equal to the + * number of components. + */ + public int getComponentSize(int componentIdx) { + if (bits == null) { + // awt.26C=bits is null + throw new NullPointerException(Messages.getString("awt.26C")); //$NON-NLS-1$ + } + + if (componentIdx < 0 || componentIdx >= bits.length) { + // awt.274=componentIdx is greater than the number of components or + // less than zero + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.274")); //$NON-NLS-1$ + } + + return bits[componentIdx]; + } + + /** + * Gets the blue component of the pixel determined by the pixel data. + * + * @param pixel + * the pixel. + * @return the blue component of the given pixel. + */ + public abstract int getBlue(int pixel); + + /** + * Gets the alpha component of the pixel determined by the pixel data. + * + * @param pixel + * the pixel. + * @return the alpha component of the given pixel. + */ + public abstract int getAlpha(int pixel); + + /** + * Gets the array of sizes of the different components. + * + * @return the array of sizes of the different components. + */ + public int[] getComponentSize() { + if (bits != null) { + return bits.clone(); + } + return null; + } + + /** + * Checks if the alpha component is pre-multiplied. + * + * @return true, if the alpha component is pre-multiplied. + */ + public final boolean isAlphaPremultiplied() { + return isAlphaPremultiplied; + } + + /** + * Checks whether this color model supports alpha. + * + * @return true, if this color model has alpha. + */ + public final boolean hasAlpha() { + return hasAlpha; + } + + @Override + public int hashCode() { + int hash = 0; + int tmp; + + if (hasAlpha) { + hash ^= 1; + hash <<= 8; + } + if (isAlphaPremultiplied) { + hash ^= 1; + hash <<= 8; + } + + tmp = hash >>> 24; + hash ^= numColorComponents; + hash <<= 8; + hash |= tmp; + + tmp = hash >>> 24; + hash ^= transparency; + hash <<= 8; + hash |= tmp; + + tmp = hash >>> 24; + hash ^= cs.getType(); + hash <<= 8; + hash |= tmp; + + tmp = hash >>> 24; + hash ^= pixel_bits; + hash <<= 8; + hash |= tmp; + + tmp = hash >>> 24; + hash ^= transferType; + hash <<= 8; + hash |= tmp; + + if (bits != null) { + + for (int element : bits) { + tmp = hash >>> 24; + hash ^= element; + hash <<= 8; + hash |= tmp; + } + + } + + return hash; + } + + public int getTransparency() { + return transparency; + } + + /** + * Gets the transfer type, which is the type of Java primitive value that + * corresponds to the bit length per pixel: either + * {@link DataBuffer#TYPE_BYTE}, {@link DataBuffer#TYPE_USHORT}, + * {@link DataBuffer#TYPE_INT}, or {@link DataBuffer#TYPE_UNDEFINED}. + * + * @return the transfer type. + */ + public final int getTransferType() { + return transferType; + } + + /** + * Gets the pixel size in bits. + * + * @return the pixel size. + */ + public int getPixelSize() { + return pixel_bits; + } + + /** + * Gets the number of components of this color model. + * + * @return the number of components. + */ + public int getNumComponents() { + return numComponents; + } + + /** + * Gets the number of color components of this color model. + * + * @return the number color components. + */ + public int getNumColorComponents() { + return numColorComponents; + } + + /** + * Gets the default RGB color model. + * + * @return the default RGB color model. + */ + public static ColorModel getRGBdefault() { + if (RGBdefault == null) { + RGBdefault = new DirectColorModel(32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); + } + return RGBdefault; + } + + /* + * Construct INT pixel representation from Object + * @param obj + * @return + */ + /** + * Construct pixel. + * + * @param obj + * the obj. + * @return the int. + */ + private int constructPixel(Object obj) { + int pixel = 0; + + switch (getTransferType()) { + + case DataBuffer.TYPE_BYTE: + byte[] bPixel = (byte[])obj; + if (bPixel.length > 1) { + // awt.275=This pixel representation is not suuported by tis + // Color Model + throw new UnsupportedOperationException(Messages.getString("awt.275")); //$NON-NLS-1$ + } + pixel = bPixel[0] & 0xff; + break; + + case DataBuffer.TYPE_USHORT: + short[] sPixel = (short[])obj; + if (sPixel.length > 1) { + // awt.275=This pixel representation is not suuported by tis + // Color Model + throw new UnsupportedOperationException(Messages.getString("awt.275")); //$NON-NLS-1$ + } + pixel = sPixel[0] & 0xffff; + break; + + case DataBuffer.TYPE_INT: + int[] iPixel = (int[])obj; + if (iPixel.length > 1) { + // awt.275=This pixel representation is not suuported by tis + // Color Model + throw new UnsupportedOperationException(Messages.getString("awt.275")); //$NON-NLS-1$ + } + pixel = iPixel[0]; + break; + + default: + // awt.22D=This transferType ( {0} ) is not supported by this + // color model + throw new UnsupportedOperationException(Messages.getString("awt.22D", //$NON-NLS-1$ + transferType)); + + } + return pixel; + } + + /** + * Gets the transfer type, which is the type of Java primitive value that + * corresponds to the bit length per pixel: either + * {@link DataBuffer#TYPE_BYTE}, {@link DataBuffer#TYPE_USHORT}, + * {@link DataBuffer#TYPE_INT}, or {@link DataBuffer#TYPE_UNDEFINED}. + * + * @param bits + * the array of component masks. + * @return the transfer type. + */ + static int getTransferType(int bits) { + if (bits <= 8) { + return DataBuffer.TYPE_BYTE; + } else if (bits <= 16) { + return DataBuffer.TYPE_USHORT; + } else if (bits <= 32) { + return DataBuffer.TYPE_INT; + } else { + return DataBuffer.TYPE_UNDEFINED; + } + } + + @Override + public void finalize() { + // This method is added for the API compatibility + // Don't need to call super since Object's finalize is always empty + } +} diff --git a/awt/java/awt/image/ComponentColorModel.java b/awt/java/awt/image/ComponentColorModel.java new file mode 100644 index 000000000..4328fd37e --- /dev/null +++ b/awt/java/awt/image/ComponentColorModel.java @@ -0,0 +1,1482 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.color.ColorSpace; + +import org.apache.harmony.awt.gl.color.LUTColorConverter; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class ComponentColorModel represents a color model that is defined in + * terms of its components. + * + * @since Android 1.0 + */ +public class ComponentColorModel extends ColorModel { + + /** + * The signed. + */ + private boolean signed; // Pixel samples are signed. + + // Samples with TransferType DataBuffer.TYPE_BYTE, + // DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT - + // unsigned. Samples with others TransferType - + // signed. + + /** + * The integral. + */ + private boolean integral; // Pixel samples are integral. + + // Samples with TransferType DataBuffer.TYPE_BYTE, + // DataBuffer.TYPE_USHORT, DataBuffer.Short and + // DataBuffer.TYPE_INT - integral. + + /** + * The scale factors. + */ + private float scaleFactors[]; // Array of factors for reduction components + + // values into the form scaled from 0 to 255 + + /** + * The donot support unnormalized. + */ + private boolean donotSupportUnnormalized; // This Color Model don't support + + // unnormolized form + + /** + * The need alpha divide. + */ + private boolean needAlphaDivide; // hasAlpha && isAlphaPremultiplied + + /** + * The calc value. + */ + private boolean calcValue; // Value was culculated + + /** + * The need scale. + */ + private boolean needScale; // Normalized value need to scale + + /** + * The min vals. + */ + private float minVals[]; // Array of Min normalized values + + /** + * The ranges. + */ + private float ranges[]; // Array of range normalized values + + /** + * The alpha lut. + */ + private byte alphaLUT[]; // Lookup table for scale alpha value + + /** + * The color lu ts. + */ + private byte colorLUTs[][]; // Lookup tables for scale color values + + /** + * The from_ linea r_ rg b_ lut. + */ + private byte from_LINEAR_RGB_LUT[]; // Lookup table for conversion from + + // Linear RGB Color Space into sRGB + + /** + * The to_ linea r_8 rg b_ lut. + */ + private byte to_LINEAR_8RGB_LUT[]; // Lookup table for conversion from + + // sRGB Color Space into Linear RGB + // 8 bit + + /** + * The to_ linea r_16 rg b_ lut. + */ + private short to_LINEAR_16RGB_LUT[]; // Lookup table for conversion from + + // sRGB Color Space into Linear RGB + // 16 bit + + /** + * The LINEA r_ rg b_ length. + */ + private int LINEAR_RGB_Length; // Linear RGB bit length + + /** + * The factor. + */ + private float fFactor; // Scale factor + + /** + * The is_s rgb. + */ + private boolean is_sRGB; // ColorModel has sRGB ColorSpace + + /** + * The is_ linea r_ rgb. + */ + private boolean is_LINEAR_RGB; // Color Model has Linear RGB Color + + // Space + + /** + * Instantiates a new component color model. + * + * @param colorSpace + * the color space. + * @param bits + * the array of component masks. + * @param hasAlpha + * whether the color model has alpha. + * @param isAlphaPremultiplied + * whether the alpha is pre-multiplied. + * @param transparency + * the transparency strategy, @see java.awt.Transparency. + * @param transferType + * the transfer type (primitive java type to use for the + * components). + */ + public ComponentColorModel(ColorSpace colorSpace, int bits[], boolean hasAlpha, + boolean isAlphaPremultiplied, int transparency, int transferType) { + super(createPixelBits(colorSpace, hasAlpha, transferType), validateBits(bits, colorSpace, + hasAlpha, transferType), colorSpace, hasAlpha, isAlphaPremultiplied, transparency, + transferType); + + needScale = false; + switch (transferType) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: + signed = false; + integral = true; + donotSupportUnnormalized = false; + scaleFactors = new float[numComponents]; + for (int i = 0; i < numColorComponents; i++) { + scaleFactors[i] = 1.0f / maxValues[i]; + if (cs.getMinValue(i) != 0.0f || cs.getMaxValue(i) != 1.0f) { + donotSupportUnnormalized = true; + } + } + if (hasAlpha) { + maxValues[numColorComponents] = (1 << bits[numColorComponents]) - 1; + scaleFactors[numColorComponents] = 1.0f / maxValues[numColorComponents]; + } + break; + case DataBuffer.TYPE_SHORT: + signed = true; + integral = true; + donotSupportUnnormalized = true; + scaleFactors = new float[numComponents]; + for (int i = 0; i < numComponents; i++) { + maxValues[i] = Short.MAX_VALUE; + scaleFactors[i] = 1.0f / maxValues[i]; + if (cs.getMinValue(i) != 0.0f || cs.getMaxValue(i) != 1.0f) { + needScale = true; + } + } + if (needScale) { + minVals = new float[numColorComponents]; + ranges = new float[numColorComponents]; + for (int i = 0; i < numColorComponents; i++) { + minVals[i] = cs.getMinValue(i); + ranges[i] = cs.getMaxValue(i) - minVals[i]; + } + } + break; + case DataBuffer.TYPE_FLOAT: + case DataBuffer.TYPE_DOUBLE: + signed = true; + integral = false; + donotSupportUnnormalized = true; + break; + default: + // awt.215=transferType is not one of DataBuffer.TYPE_BYTE, + // DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, + // DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, or + // DataBuffer.TYPE_DOUBLE + throw new IllegalArgumentException(Messages.getString("awt.215")); //$NON-NLS-1$ + } + + needAlphaDivide = hasAlpha && isAlphaPremultiplied; + initLUTs(); + } + + /** + * Instantiates a new component color model. + * + * @param colorSpace + * the color space. + * @param hasAlpha + * whether the color model has alpha. + * @param isAlphaPremultiplied + * whether the alpha is pre-multiplied. + * @param transparency + * the transparency strategy, @see java.awt.Transparency. + * @param transferType + * the transfer type (primitive java type to use for the + * components). + */ + public ComponentColorModel(ColorSpace colorSpace, boolean hasAlpha, + boolean isAlphaPremultiplied, int transparency, int transferType) { + + this(colorSpace, createPixelBitsArray(colorSpace, hasAlpha, transferType), hasAlpha, + isAlphaPremultiplied, transparency, transferType); + } + + /** + * Validate bits. + * + * @param bits + * the bits. + * @param colorSpace + * the color space. + * @param hasAlpha + * the has alpha. + * @param transferType + * the transfer type. + * @return the int[]. + */ + private static int[] validateBits(int bits[], ColorSpace colorSpace, boolean hasAlpha, + int transferType) { + if (bits != null) { + return bits; + } + + int numComponents = colorSpace.getNumComponents(); + if (hasAlpha) { + numComponents++; + } + bits = new int[numComponents]; + + int componentLength = DataBuffer.getDataTypeSize(transferType); + + for (int i = 0; i < numComponents; i++) { + bits[i] = componentLength; + } + + return bits; + } + + /** + * Creates the pixel bits. + * + * @param colorSpace + * the color space. + * @param hasAlpha + * the has alpha. + * @param transferType + * the transfer type. + * @return the int. + */ + private static int createPixelBits(ColorSpace colorSpace, boolean hasAlpha, int transferType) { + int numComponents = colorSpace.getNumComponents(); + if (hasAlpha) { + numComponents++; + } + int componentLength = DataBuffer.getDataTypeSize(transferType); + return numComponents * componentLength; + } + + /** + * Creates the pixel bits array. + * + * @param colorSpace + * the color space. + * @param hasAlpha + * the has alpha. + * @param transferType + * the transfer type. + * @return the int[]. + */ + private static int[] createPixelBitsArray(ColorSpace colorSpace, boolean hasAlpha, + int transferType) { + + int numComponents = colorSpace.getNumComponents(); + if (hasAlpha) { + numComponents++; + } + + int bits[] = new int[numComponents]; + for (int i = 0; i < numComponents; i++) { + bits[i] = DataBuffer.getDataTypeSize(transferType); + } + return bits; + } + + @Override + public Object getDataElements(int components[], int offset, Object obj) { + if (donotSupportUnnormalized) { + // awt.213=This ComponentColorModel does not support the + // unnormalized form + throw new IllegalArgumentException(Messages.getString("awt.213")); //$NON-NLS-1$ + } + + if (offset + numComponents > components.length) { + // awt.216=The components array is not large enough to hold all the + // color and alpha components + throw new IllegalArgumentException(Messages.getString("awt.216")); //$NON-NLS-1$ + } + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[]; + if (obj == null) { + ba = new byte[numComponents]; + } else { + ba = (byte[])obj; + } + for (int i = 0, idx = offset; i < numComponents; i++, idx++) { + ba[i] = (byte)components[idx]; + } + return ba; + case DataBuffer.TYPE_USHORT: + short sa[]; + if (obj == null) { + sa = new short[numComponents]; + } else { + sa = (short[])obj; + } + for (int i = 0, idx = offset; i < numComponents; i++, idx++) { + sa[i] = (short)components[idx]; + } + return sa; + case DataBuffer.TYPE_INT: + int ia[]; + if (obj == null) { + ia = new int[numComponents]; + } else { + ia = (int[])obj; + } + for (int i = 0, idx = offset; i < numComponents; i++, idx++) { + ia[i] = components[idx]; + } + return ia; + default: + // awt.217=The transfer type of this ComponentColorModel is not + // one + // of the following transfer types: DataBuffer.TYPE_BYTE, + // DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT + throw new UnsupportedOperationException(Messages.getString("awt.217")); //$NON-NLS-1$ + } + } + + @Override + public Object getDataElements(float normComponents[], int normOffset, Object obj) { + if (needScale) { + for (int i = 0, idx = 0; i < numColorComponents; i++, idx++) { + normComponents[idx] = (normComponents[idx] - minVals[i]) / ranges[i]; + } + } + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[]; + if (obj == null) { + ba = new byte[numComponents]; + } else { + ba = (byte[])obj; + } + + if (needAlphaDivide) { + float alpha = normComponents[normOffset + numColorComponents]; + for (int i = 0, idx = normOffset; i < numColorComponents; i++, idx++) { + ba[i] = (byte)(normComponents[idx] * alpha * maxValues[i] + 0.5f); + } + ba[numColorComponents] = (byte)(normComponents[normOffset + numColorComponents] + * maxValues[numColorComponents] + 0.5f); + } else { + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + ba[idx] = (byte)(normComponents[idx] * maxValues[i] + 0.5f); + } + } + return ba; + + case DataBuffer.TYPE_USHORT: + short usa[]; + if (obj == null) { + usa = new short[numComponents]; + } else { + usa = (short[])obj; + } + + if (needAlphaDivide) { + float alpha = normComponents[normOffset + numColorComponents]; + for (int i = 0, idx = 0; i < numColorComponents; i++, idx++) { + usa[i] = (short)(normComponents[idx] * alpha * maxValues[i] + 0.5f); + } + usa[numColorComponents] = (short)(alpha * maxValues[numColorComponents] + 0.5f); + } else { + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + usa[i] = (short)(normComponents[idx] * maxValues[i] + 0.5f); + } + } + return usa; + + case DataBuffer.TYPE_INT: + int ia[]; + if (obj == null) { + ia = new int[numComponents]; + } else { + ia = (int[])obj; + } + + if (needAlphaDivide) { + float alpha = normComponents[normOffset + numColorComponents]; + for (int i = 0, idx = 0; i < numColorComponents; i++, idx++) { + ia[i] = (int)(normComponents[idx] * alpha * maxValues[i] + 0.5f); + } + ia[numColorComponents] = (int)(alpha * maxValues[numColorComponents] + 0.5f); + } else { + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + ia[i] = (int)(normComponents[idx] * maxValues[i] + 0.5f); + } + } + return ia; + + case DataBuffer.TYPE_SHORT: + short sa[]; + if (obj == null) { + sa = new short[numComponents]; + } else { + sa = (short[])obj; + } + + if (needAlphaDivide) { + float alpha = normComponents[normOffset + numColorComponents]; + for (int i = 0, idx = 0; i < numColorComponents; i++, idx++) { + sa[i] = (short)(normComponents[idx] * alpha * maxValues[i] + 0.5f); + } + sa[numColorComponents] = (short)(alpha * maxValues[numColorComponents] + 0.5f); + } else { + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + sa[i] = (short)(normComponents[idx] * maxValues[i] + 0.5f); + } + } + return sa; + + case DataBuffer.TYPE_FLOAT: + float fa[]; + if (obj == null) { + fa = new float[numComponents]; + } else { + fa = (float[])obj; + } + + if (needAlphaDivide) { + float alpha = normComponents[normOffset + numColorComponents]; + for (int i = 0, idx = 0; i < numColorComponents; i++, idx++) { + fa[i] = normComponents[idx] * alpha; + } + fa[numColorComponents] = alpha; + } else { + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + fa[i] = normComponents[idx]; + } + } + return fa; + + case DataBuffer.TYPE_DOUBLE: + double da[]; + if (obj == null) { + da = new double[numComponents]; + } else { + da = (double[])obj; + } + + if (needAlphaDivide) { + double alpha = normComponents[normOffset + numColorComponents]; + for (int i = 0, idx = 0; i < numColorComponents; i++, idx++) { + da[i] = normComponents[idx] * alpha; + } + da[numColorComponents] = alpha; + } else { + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + da[i] = normComponents[idx]; + } + } + return da; + + default: + // awt.213=This ComponentColorModel does not support the + // unnormalized form + throw new IllegalArgumentException(Messages.getString("awt.213")); //$NON-NLS-1$ + } + } + + @Override + public Object getDataElements(int rgb, Object pixel) { + float normComp[]; + float comp[]; + + int red = (rgb >> 16) & 0xff; + int green = (rgb >> 8) & 0xff; + int blue = rgb & 0xff; + int alpha = (rgb >> 24) & 0xff; + + comp = new float[3]; + if (is_sRGB || is_LINEAR_RGB) { + if (is_LINEAR_RGB) { + if (LINEAR_RGB_Length == 8) { + red = to_LINEAR_8RGB_LUT[red] & 0xff; + green = to_LINEAR_8RGB_LUT[green] & 0xff; + blue = to_LINEAR_8RGB_LUT[blue] & 0xff; + } else { + red = to_LINEAR_16RGB_LUT[red] & 0xffff; + green = to_LINEAR_16RGB_LUT[green] & 0xffff; + blue = to_LINEAR_16RGB_LUT[blue] & 0xffff; + } + } + comp[0] = red / fFactor; + comp[1] = green / fFactor; + comp[2] = blue / fFactor; + if (!hasAlpha) { + normComp = comp; + } else { + float normAlpha = alpha / 255.0f; + normComp = new float[numComponents]; + for (int i = 0; i < numColorComponents; i++) { + normComp[i] = comp[i]; + } + normComp[numColorComponents] = normAlpha; + } + } else { + comp[0] = red / fFactor; + comp[1] = green / fFactor; + comp[2] = blue / fFactor; + float[] defComp = cs.fromRGB(comp); + if (!hasAlpha) { + normComp = defComp; + } else { + float normAlpha = alpha / 255.0f; + normComp = new float[numComponents]; + for (int i = 0; i < numColorComponents; i++) { + normComp[i] = defComp[i]; + } + normComp[numColorComponents] = normAlpha; + } + } + if (hasAlpha && isAlphaPremultiplied) { + normComp[0] *= normComp[numColorComponents]; + normComp[1] *= normComp[numColorComponents]; + normComp[2] *= normComp[numColorComponents]; + } + + return getDataElements(normComp, 0, pixel); + } + + @Override + public WritableRaster getAlphaRaster(WritableRaster raster) { + if (!hasAlpha) { + return null; + } + + int x = raster.getMinX(); + int y = raster.getMinY(); + int bandList[] = new int[1]; + bandList[0] = raster.getNumBands() - 1; + + return raster.createWritableChild(x, y, raster.getWidth(), raster.getHeight(), x, y, + bandList); + } + + @Override + public ColorModel coerceData(WritableRaster raster, boolean isAlphaPremultiplied) { + if (!hasAlpha || this.isAlphaPremultiplied == isAlphaPremultiplied) { + return this; + } + + int minX = raster.getMinX(); + int minY = raster.getMinY(); + int w = raster.getWidth(); + int h = raster.getHeight(); + + if (isAlphaPremultiplied) { + switch (transferType) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: + float alphaFactor = maxValues[numColorComponents]; + int iComponents[] = null; + int iTransparentComponents[] = new int[numComponents]; + for (int i = 0; i < h; i++, minY++) { + for (int j = 0, x = minX; j < w; j++, x++) { + iComponents = raster.getPixel(x, minY, iComponents); + if (iComponents[numColorComponents] == 0) { + raster.setPixel(x, minY, iTransparentComponents); + } else { + float alpha = iComponents[numColorComponents] / alphaFactor; + for (int n = 0; n < numColorComponents; n++) { + iComponents[n] = (int)(alpha * iComponents[n] + 0.5f); + } + raster.setPixel(x, minY, iComponents); + } + } + + } + break; + + case DataBuffer.TYPE_SHORT: + float sAlphaFactor = maxValues[numColorComponents]; + short sComponents[] = null; + short sTransparentComponents[] = new short[numComponents]; + for (int i = 0; i < h; i++, minY++) { + for (int j = 0, x = minX; j < w; j++, x++) { + sComponents = (short[])raster.getDataElements(x, minY, sComponents); + if (sComponents[numColorComponents] == 0) { + raster.setDataElements(x, minY, sTransparentComponents); + } else { + float alpha = sComponents[numColorComponents] / sAlphaFactor; + for (int n = 0; n < numColorComponents; n++) { + sComponents[n] = (byte)(alpha * sComponents[n] + 0.5f); + } + raster.setDataElements(x, minY, sComponents); + } + } + + } + break; + + case DataBuffer.TYPE_FLOAT: + float fComponents[] = null; + float fTransparentComponents[] = new float[numComponents]; + for (int i = 0; i < h; i++, minY++) { + for (int j = 0, x = minX; j < w; j++, x++) { + fComponents = raster.getPixel(x, minY, fComponents); + if (fComponents[numColorComponents] == 0.0f) { + raster.setDataElements(x, minY, fTransparentComponents); + } else { + float alpha = fComponents[numColorComponents]; + for (int n = 0; n < numColorComponents; n++) { + fComponents[n] = fComponents[n] * alpha; + } + raster.setPixel(x, minY, fComponents); + } + } + + } + break; + + case DataBuffer.TYPE_DOUBLE: + double dComponents[] = null; + double dTransparentComponents[] = new double[numComponents]; + for (int i = 0; i < h; i++, minY++) { + for (int j = 0, x = minX; j < w; j++, x++) { + dComponents = raster.getPixel(x, minY, dComponents); + if (dComponents[numColorComponents] == 0.0) { + raster.setPixel(x, minY, dTransparentComponents); + } else { + double alpha = dComponents[numColorComponents]; + for (int n = 0; n < numColorComponents; n++) { + dComponents[n] = dComponents[n] * alpha; + } + raster.setPixel(x, minY, dComponents); + } + } + + } + break; + + default: + // awt.219=This transferType is not supported by this color + // model + throw new UnsupportedOperationException(Messages.getString("awt.219")); //$NON-NLS-1$ + } + } else { + switch (transferType) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: + float alphaFactor = maxValues[numColorComponents]; + int iComponents[] = null; + int iTransparentComponents[] = new int[numComponents]; + for (int i = 0; i < h; i++, minY++) { + for (int j = 0, x = minX; j < w; j++, x++) { + iComponents = raster.getPixel(x, minY, iComponents); + if (iComponents[numColorComponents] == 0) { + raster.setPixel(x, minY, iTransparentComponents); + } else { + float alpha = iComponents[numColorComponents] / alphaFactor; + for (int n = 0; n < numColorComponents; n++) { + iComponents[n] = (int)(iComponents[n] / alpha + 0.5f); + } + raster.setPixel(x, minY, iComponents); + } + } + + } + break; + + case DataBuffer.TYPE_SHORT: + float sAlphaFactor = maxValues[numColorComponents]; + short sComponents[] = null; + short sTransparentComponents[] = new short[numComponents]; + for (int i = 0; i < h; i++, minY++) { + for (int j = 0, x = minX; j < w; j++, x++) { + sComponents = (short[])raster.getDataElements(x, minY, sComponents); + if (sComponents[numColorComponents] == 0) { + raster.setDataElements(x, minY, sTransparentComponents); + } else { + float alpha = sComponents[numColorComponents] / sAlphaFactor; + for (int n = 0; n < numColorComponents; n++) { + sComponents[n] = (byte)(sComponents[n] / alpha + 0.5f); + } + raster.setDataElements(x, minY, sComponents); + } + } + + } + break; + + case DataBuffer.TYPE_FLOAT: + float fComponents[] = null; + float fTransparentComponents[] = new float[numComponents]; + for (int i = 0; i < h; i++, minY++) { + for (int j = 0, x = minX; j < w; j++, x++) { + fComponents = raster.getPixel(x, minY, fComponents); + if (fComponents[numColorComponents] == 0.0f) { + raster.setDataElements(x, minY, fTransparentComponents); + } else { + float alpha = fComponents[numColorComponents]; + for (int n = 0; n < numColorComponents; n++) { + fComponents[n] = fComponents[n] / alpha; + } + raster.setPixel(x, minY, fComponents); + } + } + + } + break; + + case DataBuffer.TYPE_DOUBLE: + double dComponents[] = null; + double dTransparentComponents[] = new double[numComponents]; + for (int i = 0; i < h; i++, minY++) { + for (int j = 0, x = minX; j < w; j++, x++) { + dComponents = raster.getPixel(x, minY, dComponents); + if (dComponents[numColorComponents] == 0.0) { + raster.setPixel(x, minY, dTransparentComponents); + } else { + double alpha = dComponents[numColorComponents]; + for (int n = 0; n < numColorComponents; n++) { + dComponents[n] = dComponents[n] / alpha; + } + raster.setPixel(x, minY, dComponents); + } + } + + } + break; + default: + // awt.219=This transferType is not supported by this color + // model + throw new UnsupportedOperationException(Messages.getString("awt.219")); //$NON-NLS-1$ + } + } + + if (!signed) { + return new ComponentColorModel(cs, bits, hasAlpha, isAlphaPremultiplied, transparency, + transferType); + } + + return new ComponentColorModel(cs, null, hasAlpha, isAlphaPremultiplied, transparency, + transferType); + } + + @Override + public int[] getComponents(Object pixel, int[] components, int offset) { + if (donotSupportUnnormalized) { + // awt.213=This ComponentColorModel does not support the + // unnormalized form + throw new IllegalArgumentException(Messages.getString("awt.213")); //$NON-NLS-1$ + } + + if (components == null) { + components = new int[offset + numComponents]; + } else if (offset + numComponents > components.length) { + // awt.218=The components array is not large enough to hold all the + // color and alpha components + throw new IllegalArgumentException(Messages.getString("awt.218")); //$NON-NLS-1$ + } + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])pixel; + + for (int i = 0, idx = offset; i < numComponents; i++, idx++) { + components[idx] = ba[i] & 0xff; + } + return components; + + case DataBuffer.TYPE_USHORT: + short sa[] = (short[])pixel; + for (int i = 0, idx = offset; i < numComponents; i++, idx++) { + components[idx] = sa[i] & 0xffff; + } + return components; + + case DataBuffer.TYPE_INT: + int ia[] = (int[])pixel; + for (int i = 0, idx = offset; i < numComponents; i++, idx++) { + components[idx] = ia[i]; + } + return components; + + default: + // awt.217=The transfer type of this ComponentColorModel is not + // one + // of the following transfer types: DataBuffer.TYPE_BYTE, + // DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT + throw new UnsupportedOperationException(Messages.getString("awt.217")); //$NON-NLS-1$ + } + + } + + @Override + public float[] getNormalizedComponents(Object pixel, float normComponents[], int normOffset) { + + if (normComponents == null) { + normComponents = new float[numComponents + normOffset]; + } + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])pixel; + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + normComponents[idx] = (ba[i] & 0xff) * scaleFactors[i]; + } + break; + + case DataBuffer.TYPE_USHORT: + short usa[] = (short[])pixel; + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + normComponents[idx] = (usa[i] & 0xffff) * scaleFactors[i]; + } + break; + + case DataBuffer.TYPE_INT: + int ia[] = (int[])pixel; + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + normComponents[idx] = ia[i] * scaleFactors[i]; + } + break; + + case DataBuffer.TYPE_SHORT: + short sa[] = (short[])pixel; + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + normComponents[idx] = sa[i] * scaleFactors[i]; + } + break; + + case DataBuffer.TYPE_FLOAT: + float fa[] = (float[])pixel; + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + normComponents[idx] = fa[i]; + } + break; + + case DataBuffer.TYPE_DOUBLE: + double da[] = (double[])pixel; + for (int i = 0, idx = normOffset; i < numComponents; i++, idx++) { + normComponents[idx] = (float)da[i]; + } + break; + + default: + // awt.21A=This ComponentColorModel does not support this + // transferType + throw new IllegalArgumentException(Messages.getString("awt.21A")); //$NON-NLS-1$ + } + + if (needAlphaDivide) { + float alpha = normComponents[normOffset + numColorComponents]; + for (int i = 0, idx = normOffset; i < numColorComponents; i++, idx++) { + normComponents[idx] /= alpha; + } + } + + if (needScale) { + for (int i = 0, idx = normOffset; i < numColorComponents; i++, idx++) { + normComponents[idx] = minVals[i] + ranges[i] * normComponents[idx]; + } + } + return normComponents; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ComponentColorModel)) { + return false; + } + return super.equals(obj); + } + + @Override + public int getRed(Object inData) { + return getRGBComponent(inData, 0); + } + + @Override + public int getRGB(Object inData) { + int alpha = getAlpha(inData); + if (cs.getType() == ColorSpace.TYPE_GRAY) { + int gray = getRed(inData); + return (alpha << 24 | gray << 16 | gray << 8 | gray); + } + return (alpha << 24 | getRed(inData) << 16 | getGreen(inData) << 8 | getBlue(inData)); + } + + @Override + public int getGreen(Object inData) { + return getRGBComponent(inData, 1); + } + + @Override + public int getBlue(Object inData) { + return getRGBComponent(inData, 2); + } + + @Override + public int getAlpha(Object inData) { + if (!hasAlpha) { + return 255; + } + int alpha = 0; + + switch (transferType) { + case DataBuffer.TYPE_BYTE: { + byte ba[] = (byte[])inData; + alpha = ba[numColorComponents] & 0xff; + if (bits[numColorComponents] != 8) { + return alphaLUT[alpha] & 0xff; + } + return alpha; + } + case DataBuffer.TYPE_USHORT: { + short usa[] = (short[])inData; + alpha = usa[numColorComponents] & 0xffff; + if (bits[numColorComponents] != 8) { + return alphaLUT[alpha] & 0xff; + } + return alpha; + } + case DataBuffer.TYPE_INT: { + int ia[] = (int[])inData; + alpha = ia[numColorComponents]; + if (bits[numColorComponents] != 8) { + return alphaLUT[alpha] & 0xff; + } + return alpha; + } + case DataBuffer.TYPE_SHORT: { + short sa[] = (short[])inData; + alpha = sa[numColorComponents]; + if (bits[numColorComponents] != 8) { + return alphaLUT[alpha] & 0xff; + } + return alpha; + } + case DataBuffer.TYPE_FLOAT: { + float fa[] = (float[])inData; + return (int)(fa[numColorComponents] * 255.0f + 0.5f); + } + case DataBuffer.TYPE_DOUBLE: { + double da[] = (double[])inData; + return (int)(da[numColorComponents] * 255.0 + 0.5); + } + default: { + // awt.214=This Color Model doesn't support this transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + } + } + + @Override + public WritableRaster createCompatibleWritableRaster(int w, int h) { + SampleModel sm = createCompatibleSampleModel(w, h); + DataBuffer db = sm.createDataBuffer(); + return Raster.createWritableRaster(sm, db, null); + } + + @Override + public boolean isCompatibleSampleModel(SampleModel sm) { + if (!(sm instanceof ComponentSampleModel)) { + return false; + } + if (numComponents != sm.getNumBands()) { + return false; + } + if (transferType != sm.getTransferType()) { + return false; + } + return true; + } + + @Override + public SampleModel createCompatibleSampleModel(int w, int h) { + int bandOffsets[] = new int[numComponents]; + for (int i = 0; i < numComponents; i++) { + bandOffsets[i] = i; + } + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + return new PixelInterleavedSampleModel(transferType, w, h, numComponents, w + * numComponents, bandOffsets); + + default: + return new ComponentSampleModel(transferType, w, h, numComponents, w + * numComponents, bandOffsets); + } + } + + @Override + public boolean isCompatibleRaster(Raster raster) { + SampleModel sm = raster.getSampleModel(); + if (!(sm instanceof ComponentSampleModel)) { + return false; + } + + if (sm.getNumBands() != numComponents) { + return false; + } + if (raster.getTransferType() != transferType) { + return false; + } + + int sampleSizes[] = sm.getSampleSize(); + for (int i = 0; i < numComponents; i++) { + if (bits[i] != sampleSizes[i]) { + return false; + } + } + return true; + } + + @Override + public float[] getNormalizedComponents(int components[], int offset, float normComponents[], + int normOffset) { + if (donotSupportUnnormalized) { + // awt.213=This ComponentColorModel does not support the + // unnormalized form + throw new IllegalArgumentException(Messages.getString("awt.213")); //$NON-NLS-1$ + } + + return super.getNormalizedComponents(components, offset, normComponents, normOffset); + } + + @Override + public int getDataElement(int[] components, int offset) { + if (numComponents > 1) { + // awt.212=There is more than one component in this ColorModel + throw new IllegalArgumentException(Messages.getString("awt.212")); //$NON-NLS-1$ + } + if (donotSupportUnnormalized) { + // awt.213=This ComponentColorModel does not support the + // unnormalized form + throw new IllegalArgumentException(Messages.getString("awt.213")); //$NON-NLS-1$ + } + return components[offset]; + } + + @Override + public int[] getUnnormalizedComponents(float[] normComponents, int normOffset, + int[] components, int offset) { + + if (donotSupportUnnormalized) { + // awt.213=This ComponentColorModel does not support the + // unnormalized form + throw new IllegalArgumentException(Messages.getString("awt.213")); //$NON-NLS-1$ + } + + if (normComponents.length - normOffset < numComponents) { + // awt.21B=The length of normComponents minus normOffset is less + // than numComponents + throw new IllegalArgumentException(Messages.getString("awt.21B")); //$NON-NLS-1$ + } + + return super.getUnnormalizedComponents(normComponents, normOffset, components, offset); + } + + @Override + public int getDataElement(float normComponents[], int normOffset) { + if (numComponents > 1) { + // awt.212=There is more than one component in this ColorModel + throw new IllegalArgumentException(Messages.getString("awt.212")); //$NON-NLS-1$ + } + if (signed) { + // awt.210=The component value for this ColorModel is signed + throw new IllegalArgumentException(Messages.getString("awt.210")); //$NON-NLS-1$ + } + + Object pixel = getDataElements(normComponents, normOffset, null); + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])pixel; + return ba[0] & 0xff; + case DataBuffer.TYPE_USHORT: + short sa[] = (short[])pixel; + return sa[0] & 0xffff; + case DataBuffer.TYPE_INT: + int ia[] = (int[])pixel; + return ia[0]; + default: + // awt.211=Pixel values for this ColorModel are not conveniently + // representable as a single int + throw new IllegalArgumentException(Messages.getString("awt.211")); //$NON-NLS-1$ + } + } + + @Override + public int[] getComponents(int pixel, int components[], int offset) { + if (numComponents > 1) { + // awt.212=There is more than one component in this ColorModel + throw new IllegalArgumentException(Messages.getString("awt.212")); //$NON-NLS-1$ + } + if (donotSupportUnnormalized) { + // awt.213=This ComponentColorModel does not support the + // unnormalized form + throw new IllegalArgumentException(Messages.getString("awt.213")); //$NON-NLS-1$ + } + + if (components == null) { + components = new int[offset + 1]; + } + + components[offset] = pixel & maxValues[0]; + return components; + } + + @Override + public int getRed(int pixel) { + float rgb[] = toRGB(pixel); + return (int)(rgb[0] * 255.0f + 0.5f); + } + + @Override + public int getRGB(int pixel) { + return (getAlpha(pixel) << 24) | (getRed(pixel) << 16) | (getGreen(pixel) << 8) + | getBlue(pixel); + } + + @Override + public int getGreen(int pixel) { + float rgb[] = toRGB(pixel); + return (int)(rgb[1] * 255.0f + 0.5f); + } + + @Override + public int getBlue(int pixel) { + float rgb[] = toRGB(pixel); + return (int)(rgb[2] * 255.0f + 0.5f); + } + + @Override + public int getAlpha(int pixel) { + + // This method throw IllegalArgumentException according to + // Java API Spacification + if (signed) { + // awt.210=The component value for this ColorModel is signed + throw new IllegalArgumentException(Messages.getString("awt.210")); //$NON-NLS-1$ + } + + if (numComponents > 1) { + // awt.212=There is more than one component in this ColorModel + throw new IllegalArgumentException(Messages.getString("awt.212")); //$NON-NLS-1$ + } + + return 255; + } + + /** + * Initialization of Lookup tables. + */ + private void initLUTs() { + is_sRGB = cs.isCS_sRGB(); + is_LINEAR_RGB = (cs == LUTColorConverter.LINEAR_RGB_CS); + + if (hasAlpha && bits[numColorComponents] != 8 && integral) { + alphaLUT = new byte[maxValues[numColorComponents] + 1]; + for (int i = 0; i <= maxValues[numColorComponents]; i++) { + alphaLUT[i] = (byte)(scaleFactors[numColorComponents] * i + 0.5f); + } + } + + if (is_LINEAR_RGB) { + if (maxBitLength > 8) { + LINEAR_RGB_Length = 16; + from_LINEAR_RGB_LUT = LUTColorConverter.getFrom16lRGBtosRGB_LUT(); + to_LINEAR_16RGB_LUT = LUTColorConverter.getFromsRGBto16lRGB_LUT(); + } else { + LINEAR_RGB_Length = 8; + from_LINEAR_RGB_LUT = LUTColorConverter.getFrom8lRGBtosRGB_LUT(); + to_LINEAR_8RGB_LUT = LUTColorConverter.getFromsRGBto8lRGB_LUT(); + } + fFactor = ((1 << LINEAR_RGB_Length) - 1); + } else { + fFactor = 255.0f; + } + + if (!isAlphaPremultiplied && integral) { + colorLUTs = new byte[3][]; + + if (is_sRGB) { + for (int i = 0; i < numColorComponents; i++) { + if (bits[i] != 8) { + for (int j = 0; j < i; j++) { + if (bits[i] == bits[j]) { + colorLUTs[i] = colorLUTs[j]; + break; + } + } + colorLUTs[i] = new byte[maxValues[i] + 1]; + for (int j = 0; j <= maxValues[0]; j++) { + colorLUTs[i][j] = (byte)(scaleFactors[i] * j + 0.5f); + } + } + } + } + + if (is_LINEAR_RGB) { + + for (int i = 0; i < numColorComponents; i++) { + if (bits[i] != LINEAR_RGB_Length) { + for (int j = 0; j < i; j++) { + if (bits[i] == bits[j]) { + colorLUTs[i] = colorLUTs[j]; + break; + } + } + colorLUTs[i] = new byte[maxValues[i] + 1]; + for (int j = 0; j <= maxValues[0]; j++) { + int idx; + if (LINEAR_RGB_Length == 8) { + idx = (int)(scaleFactors[i] * j + 0.5f); + } else { + idx = (int)(scaleFactors[i] * j * 257.0f + 0.5f); + } + colorLUTs[i][j] = from_LINEAR_RGB_LUT[idx]; + } + } + } + } + + } + } + + /** + * To rgb. + * + * @param pixel + * the integer representation of the pixel. + * @return the array of normalized sRGB components. + */ + private float[] toRGB(int pixel) { + + // This method throw IllegalArgumentException according to + // Java API Spacification + if (signed) { + // awt.210=The component value for this ColorModel is signed + throw new IllegalArgumentException(Messages.getString("awt.210")); //$NON-NLS-1$ + } + + if (numComponents > 1) { + // awt.212=There is more than one component in this ColorModel + throw new IllegalArgumentException(Messages.getString("awt.212")); //$NON-NLS-1$ + } + + Object obj = null; + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = new byte[1]; + ba[0] = (byte)pixel; + obj = ba; + break; + + case DataBuffer.TYPE_USHORT: + short sa[] = new short[1]; + sa[0] = (short)pixel; + obj = sa; + break; + + case DataBuffer.TYPE_INT: + int ia[] = new int[1]; + ia[0] = pixel; + obj = ia; + break; + + } + + return cs.toRGB(getNormalizedComponents(obj, null, 0)); + } + + /** + * Gets the RGB component. + * + * @param pixel + * the pixel. + * @param idx + * the index of component. + * @return the RGB value from 0 to 255 pixel's component. + */ + private int getRGBComponent(Object pixel, int idx) { + if (is_sRGB) { + int comp = getDefComponent(pixel, idx); + if (calcValue || bits[idx] == 8) { + return comp; + } + return colorLUTs[idx][comp] & 0xff; + } else if (is_LINEAR_RGB) { + int comp = getDefComponent(pixel, idx); + if (calcValue || bits[idx] == LINEAR_RGB_Length) { + return from_LINEAR_RGB_LUT[comp] & 0xff; + } + return colorLUTs[idx][comp] & 0xff; + } + + float normComp[] = getNormalizedComponents(pixel, null, 0); + float rgbComp[] = cs.toRGB(normComp); + return (int)(rgbComp[idx] * 255.0f + 0.5f); + } + + /** + * Gets the def component. + * + * @param pixel + * the pixel. + * @param idx + * the index of component. + * @return the tentative value of the pixel component. + */ + private int getDefComponent(Object pixel, int idx) { + int comp = 0; + calcValue = false; + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])pixel; + comp = ba[idx] & 0xff; + if (needAlphaDivide) { + int alpha = ba[numColorComponents] & 0xff; + if (alpha == 0) { + comp = 0; + } else { + float normAlpha = scaleFactors[numColorComponents] * alpha; + comp = (int)(comp * fFactor / normAlpha + 0.5f); + } + calcValue = true; + } + return comp; + + case DataBuffer.TYPE_USHORT: + short usa[] = (short[])pixel; + comp = usa[idx] & 0xffff; + if (needAlphaDivide) { + int alpha = usa[numColorComponents] & 0xffff; + if (alpha == 0) { + comp = 0; + } else { + float normAlpha = scaleFactors[numColorComponents] * alpha; + comp = (int)(comp * fFactor / normAlpha + 0.5f); + } + calcValue = true; + } + return comp; + + case DataBuffer.TYPE_INT: + int ia[] = (int[])pixel; + comp = ia[idx]; + if (needAlphaDivide) { + int alpha = ia[numColorComponents]; + if (alpha == 0) { + comp = 0; + } else { + float normAlpha = scaleFactors[numColorComponents] * alpha; + comp = (int)(comp * fFactor / normAlpha + 0.5f); + } + calcValue = true; + } + return comp; + + case DataBuffer.TYPE_SHORT: + short sa[] = (short[])pixel; + comp = sa[idx]; + if (needAlphaDivide) { + int alpha = sa[numColorComponents]; + if (alpha == 0) { + comp = 0; + } else { + float normAlpha = scaleFactors[numColorComponents] * alpha; + comp = (int)(comp * fFactor / normAlpha + 0.5f); + } + calcValue = true; + } + return comp; + + case DataBuffer.TYPE_FLOAT: + float fa[] = (float[])pixel; + if (needAlphaDivide) { + float alpha = fa[numColorComponents]; + if (fa[numColorComponents] == 0.0f) { + comp = 0; + } else { + comp = (int)(fa[idx] * fFactor / alpha + 0.5f); + } + } else { + comp = (int)(fa[idx] * fFactor + 0.5f); + } + calcValue = true; + return comp; + + case DataBuffer.TYPE_DOUBLE: + double da[] = (double[])pixel; + if (needAlphaDivide) { + if (da[numColorComponents] == 0.0) { + comp = 0; + } else { + comp = (int)(da[idx] * fFactor / da[numColorComponents] + 0.5); + } + } else { + comp = (int)(da[idx] * fFactor + 0.5); + } + calcValue = true; + return comp; + + default: + // awt.214=This Color Model doesn't support this transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + } + +} diff --git a/awt/java/awt/image/ComponentSampleModel.java b/awt/java/awt/image/ComponentSampleModel.java new file mode 100644 index 000000000..7f8140918 --- /dev/null +++ b/awt/java/awt/image/ComponentSampleModel.java @@ -0,0 +1,705 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.util.Arrays; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The ComponentSampleModel class represents a set of image data whose each + * element - the sample of a pixel - takes one data element of the DataBuffer. + *

+ * The Bank indices denote the correspondence between the bank of data buffers + * and a band of image data. The Pixel stride is the number of data array + * elements between two samples for the same band on the same scanline. The + * pixel stride for a BandedSampleModel is one. The scanline stride represents + * the number of data array elements between a specified sample and the + * corresponding sample in the same column in the next scanline. The array of + * band offsets gives the starting offsets within each data banks of the in the + * DataBuffer. The bank indices represents the indices within each bank of the + * DataBuffer corresponding to a band of image data. + * + * @since Android 1.0 + */ +public class ComponentSampleModel extends SampleModel { + + /** + * The band offsets array of this ComponentSampleModel. + */ + protected int bandOffsets[]; + + /** + * The bank indices array of this ComponentSampleModel. + */ + protected int bankIndices[]; + + /** + * The number of bands in this ComponentSampleModel. + */ + protected int numBands; + + /** + * The number banks of this ComponentSampleModel. + */ + protected int numBanks; + + /** + * The scanline stride of this ComponentSampleModel. + */ + protected int scanlineStride; + + /** + * The pixel stride of this ComponentSampleModel. + */ + protected int pixelStride; + + /** + * Instantiates a new ComponentSampleModel with the specified properties. + * + * @param dataType + * the data type of samples. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param pixelStride + * the pixel stride of the image data. + * @param scanlineStride + * the scanline stride of the image data. + * @param bankIndices + * the array of the bank indices. + * @param bandOffsets + * the array of the band offsets. + */ + public ComponentSampleModel(int dataType, int w, int h, int pixelStride, int scanlineStride, + int bankIndices[], int bandOffsets[]) { + + super(dataType, w, h, bandOffsets.length); + + if (pixelStride < 0) { + // awt.24B=Pixel stride must be >= 0 + throw new IllegalArgumentException(Messages.getString("awt.24B")); //$NON-NLS-1$ + } + + if (scanlineStride < 0) { + // awt.24C=Scanline stride must be >= 0 + throw new IllegalArgumentException(Messages.getString("awt.24C")); //$NON-NLS-1$ + } + + if (bankIndices.length != bandOffsets.length) { + // awt.24D=Bank Indices length must be equal Bank Offsets length + throw new IllegalArgumentException(Messages.getString("awt.24D")); //$NON-NLS-1$ + } + + this.pixelStride = pixelStride; + this.scanlineStride = scanlineStride; + this.bandOffsets = bandOffsets.clone(); + this.bankIndices = bankIndices.clone(); + this.numBands = bandOffsets.length; + + int maxBank = 0; + for (int i = 0; i < bankIndices.length; i++) { + if (bankIndices[i] < 0) { + // awt.24E=Index of {0} bank must be >= 0 + throw new IllegalArgumentException(Messages.getString("awt.24E", i)); //$NON-NLS-1$ + } + if (bankIndices[i] > maxBank) { + maxBank = bankIndices[i]; + } + } + this.numBanks = maxBank + 1; + + } + + /** + * Instantiates a new ComponentSampleModel with the specified properties. + * + * @param dataType + * the data type of the samples. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param pixelStride + * the pixel stride of the image data. + * @param scanlineStride + * the scanline stride of the image data. + * @param bandOffsets + * the band offsets. + */ + public ComponentSampleModel(int dataType, int w, int h, int pixelStride, int scanlineStride, + int bandOffsets[]) { + + super(dataType, w, h, bandOffsets.length); + if (pixelStride < 0) { + // awt.24B=Pixel stride must be >= 0 + throw new IllegalArgumentException(Messages.getString("awt.24B")); //$NON-NLS-1$ + } + + if (scanlineStride < 0) { + // awt.24C=Scanline stride must be >= 0 + throw new IllegalArgumentException(Messages.getString("awt.24C")); //$NON-NLS-1$ + } + + this.pixelStride = pixelStride; + this.scanlineStride = scanlineStride; + this.bandOffsets = bandOffsets.clone(); + this.numBands = bandOffsets.length; + this.numBanks = 1; + + this.bankIndices = new int[numBands]; + for (int i = 0; i < numBands; i++) { + bankIndices[i] = 0; + } + } + + @Override + public Object getDataElements(int x, int y, Object obj, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + switch (dataType) { + case DataBuffer.TYPE_BYTE: + byte bdata[]; + if (obj == null) { + bdata = new byte[numBands]; + } else { + bdata = (byte[])obj; + } + + for (int i = 0; i < numBands; i++) { + bdata[i] = (byte)getSample(x, y, i, data); + } + + obj = bdata; + break; + + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + short sdata[]; + if (obj == null) { + sdata = new short[numBands]; + } else { + sdata = (short[])obj; + } + + for (int i = 0; i < numBands; i++) { + sdata[i] = (short)getSample(x, y, i, data); + } + + obj = sdata; + break; + + case DataBuffer.TYPE_INT: + int idata[]; + if (obj == null) { + idata = new int[numBands]; + } else { + idata = (int[])obj; + } + + for (int i = 0; i < numBands; i++) { + idata[i] = getSample(x, y, i, data); + } + + obj = idata; + break; + + case DataBuffer.TYPE_FLOAT: + float fdata[]; + if (obj == null) { + fdata = new float[numBands]; + } else { + fdata = (float[])obj; + } + + for (int i = 0; i < numBands; i++) { + fdata[i] = getSampleFloat(x, y, i, data); + } + + obj = fdata; + break; + + case DataBuffer.TYPE_DOUBLE: + double ddata[]; + if (obj == null) { + ddata = new double[numBands]; + } else { + ddata = (double[])obj; + } + + for (int i = 0; i < numBands; i++) { + ddata[i] = getSampleDouble(x, y, i, data); + } + + obj = ddata; + break; + } + + return obj; + } + + @Override + public void setDataElements(int x, int y, Object obj, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + switch (dataType) { + case DataBuffer.TYPE_BYTE: + byte barr[] = (byte[])obj; + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, barr[i] & 0xff, data); + } + break; + + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + short sarr[] = (short[])obj; + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, sarr[i] & 0xffff, data); + } + break; + + case DataBuffer.TYPE_INT: + int iarr[] = (int[])obj; + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, iarr[i], data); + } + break; + + case DataBuffer.TYPE_FLOAT: + float farr[] = (float[])obj; + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, farr[i], data); + } + break; + + case DataBuffer.TYPE_DOUBLE: + double darr[] = (double[])obj; + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, darr[i], data); + } + break; + } + } + + /** + * Compares this ComponentSampleModel with the specified Object. + * + * @param o + * the Object. + * @return true, if the object is a ComponentSampleModel with identical data + * values to this ComponentSampleModel, false otherwise. + */ + @Override + public boolean equals(Object o) { + if ((o == null) || !(o instanceof ComponentSampleModel)) { + return false; + } + ComponentSampleModel model = (ComponentSampleModel)o; + return this.width == model.width && this.height == model.height + && this.numBands == model.numBands && this.dataType == model.dataType + && Arrays.equals(this.bandOffsets, model.bandOffsets) + && Arrays.equals(this.bankIndices, model.bankIndices) + && this.numBands == model.numBands && this.numBanks == model.numBanks + && this.scanlineStride == model.scanlineStride + && this.pixelStride == model.pixelStride; + } + + /** + * @see java.awt.image.SampleModel#createSubsetSampleModel(int[]) + */ + @Override + public SampleModel createSubsetSampleModel(int bands[]) { + if (bands.length > this.numBands) { + // awt.64=The number of the bands in the subset is greater than the + // number of bands in the sample model + throw new RasterFormatException(Messages.getString("awt.64")); //$NON-NLS-1$ + } + + int indices[] = new int[bands.length]; + int offsets[] = new int[bands.length]; + + for (int i = 0; i < bands.length; i++) { + indices[i] = bankIndices[bands[i]]; + offsets[i] = bandOffsets[bands[i]]; + } + + return new ComponentSampleModel(dataType, width, height, pixelStride, scanlineStride, + indices, offsets); + + } + + @Override + public SampleModel createCompatibleSampleModel(int w, int h) { + return new ComponentSampleModel(dataType, w, h, pixelStride, pixelStride * w, bankIndices, + bandOffsets); + } + + @Override + public int[] getPixel(int x, int y, int iArray[], DataBuffer data) { + int pixel[]; + + if (iArray == null) { + pixel = new int[numBands]; + } else { + pixel = iArray; + } + + for (int i = 0; i < numBands; i++) { + pixel[i] = getSample(x, y, i, data); + } + + return pixel; + } + + @Override + public void setPixel(int x, int y, int iArray[], DataBuffer data) { + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, iArray[i], data); + } + } + + @Override + public int getSample(int x, int y, int b, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + return data.getElem(bankIndices[b], y * scanlineStride + x * pixelStride + bandOffsets[b]); + } + + @Override + public float getSampleFloat(int x, int y, int b, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + return data.getElemFloat(bankIndices[b], y * scanlineStride + x * pixelStride + + bandOffsets[b]); + } + + @Override + public double getSampleDouble(int x, int y, int b, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + return data.getElemDouble(bankIndices[b], y * scanlineStride + x * pixelStride + + bandOffsets[b]); + } + + @Override + public int[] getPixels(int x, int y, int w, int h, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x > this.width || x + w > this.width || y > this.height + || y + h > this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int pixels[] = null; + int idx = 0; + + if (iArray == null) { + pixels = new int[w * h * numBands]; + } else { + pixels = iArray; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numBands; n++) { + pixels[idx++] = getSample(j, i, n, data); + } + } + } + + return pixels; + } + + @Override + public void setPixels(int x, int y, int w, int h, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int idx = 0; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numBands; n++) { + setSample(j, i, n, iArray[idx++], data); + } + } + } + } + + @Override + public void setSample(int x, int y, int b, int s, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + data.setElem(bankIndices[b], y * scanlineStride + x * pixelStride + bandOffsets[b], s); + } + + @Override + public int[] getSamples(int x, int y, int w, int h, int b, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int samples[]; + int idx = 0; + + if (iArray == null) { + samples = new int[w * h]; + } else { + samples = iArray; + } + + if (data == null) { + // awt.295=data is null + throw new NullPointerException(Messages.getString("awt.295")); //$NON-NLS-1$ + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + samples[idx++] = getSample(j, i, b, data); + } + } + + return samples; + } + + @Override + public void setSamples(int x, int y, int w, int h, int b, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int idx = 0; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + setSample(j, i, b, iArray[idx++], data); + } + } + } + + @Override + public void setSample(int x, int y, int b, float s, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + data.setElemFloat(bankIndices[b], y * scanlineStride + x * pixelStride + bandOffsets[b], s); + } + + @Override + public void setSample(int x, int y, int b, double s, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + data + .setElemDouble(bankIndices[b], y * scanlineStride + x * pixelStride + + bandOffsets[b], s); + } + + @Override + public DataBuffer createDataBuffer() { + DataBuffer data = null; + + int maxOffset = bandOffsets[0]; + for (int i = 1; i < bandOffsets.length; i++) { + if (bandOffsets[i] > maxOffset) { + maxOffset = bandOffsets[i]; + } + } + int size = (height - 1) * scanlineStride + (width - 1) * pixelStride + maxOffset + 1; + + switch (dataType) { + case DataBuffer.TYPE_BYTE: + data = new DataBufferByte(size, numBanks); + break; + case DataBuffer.TYPE_SHORT: + data = new DataBufferShort(size, numBanks); + break; + case DataBuffer.TYPE_USHORT: + data = new DataBufferUShort(size, numBanks); + break; + case DataBuffer.TYPE_INT: + data = new DataBufferInt(size, numBanks); + break; + case DataBuffer.TYPE_FLOAT: + data = new DataBufferFloat(size, numBanks); + break; + case DataBuffer.TYPE_DOUBLE: + data = new DataBufferDouble(size, numBanks); + break; + } + + return data; + + } + + /** + * Gets the offset of the specified band of the specified pixel. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param b + * the band. + * @return the offset of the specified band of the specified pixel. + */ + public int getOffset(int x, int y, int b) { + return y * scanlineStride + x * pixelStride + bandOffsets[b]; + } + + /** + * Gets the offset of the first band of the specified pixel. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @return the offset of the first band of the specified pixel. + */ + public int getOffset(int x, int y) { + return y * scanlineStride + x * pixelStride + bandOffsets[0]; + } + + @Override + public final int getSampleSize(int band) { + return DataBuffer.getDataTypeSize(dataType); + } + + @Override + public final int[] getSampleSize() { + int sampleSizes[] = new int[numBands]; + int size = DataBuffer.getDataTypeSize(dataType); + + for (int i = 0; i < numBands; i++) { + sampleSizes[i] = size; + } + return sampleSizes; + } + + /** + * Gets an array of bank indices corresponding to this ComponentSampleModel. + * + * @return the array of bank indices. + */ + public final int[] getBankIndices() { + return bankIndices.clone(); + } + + /** + * Gets an array of the band offsets corresponding to this + * ComponentSampleModel. + * + * @return the array of band offsets. + */ + public final int[] getBandOffsets() { + return bandOffsets.clone(); + } + + /** + * Gets a hash code of this ComponentSampleModel object. + * + * @return a hash code of this ComponentSampleModel object. + */ + @Override + public int hashCode() { + int hash = 0; + int tmp = 0; + + hash = width; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= height; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= numBands; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= dataType; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + for (int element : bandOffsets) { + hash ^= element; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + } + for (int element : bankIndices) { + hash ^= element; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + } + hash ^= pixelStride; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + + hash ^= scanlineStride; + return hash; + } + + /** + * Gets the scanline stride of this ComponentSampleModel. + * + * @return the scanline stride of this ComponentSampleModel. + */ + public final int getScanlineStride() { + return scanlineStride; + } + + /** + * Gets the pixel stride. + * + * @return the pixel stride. + */ + public final int getPixelStride() { + return pixelStride; + } + + @Override + public final int getNumDataElements() { + return numBands; + } + +} diff --git a/awt/java/awt/image/ConvolveOp.java b/awt/java/awt/image/ConvolveOp.java new file mode 100644 index 000000000..6eb8909b8 --- /dev/null +++ b/awt/java/awt/image/ConvolveOp.java @@ -0,0 +1,545 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Sep 29, 2005 + */ + +package java.awt.image; + +import java.awt.*; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The ConvolveOp class convolves from the source data to the destination using + * a convolution kernel. Each output pixel is represented as the result of + * multiplying the kernel and the surround of the input pixel. + * + * @since Android 1.0 + */ +public class ConvolveOp implements BufferedImageOp, RasterOp { + + /** + * The Constant EDGE_ZERO_FILL indicates that pixels at the edge of the + * destination image are set to zero. + */ + public static final int EDGE_ZERO_FILL = 0; + + /** + * The Constant EDGE_NO_OP indicates that pixels at the edge of the source + * image are converted to the edge pixels in the destination without + * modification. + */ + public static final int EDGE_NO_OP = 1; + + /** + * The kernel. + */ + private Kernel kernel; + + /** + * The edge cond. + */ + private int edgeCond; + + /** + * The rhs. + */ + private RenderingHints rhs = null; + + static { + // TODO + // System.loadLibrary("imageops"); + } + + /** + * Instantiates a new ConvolveOp object with the specified Kernel and + * specified edges condition. + * + * @param kernel + * the specified Kernel. + * @param edgeCondition + * the specified edge condition. + * @param hints + * the RenderingHints object, or null. + */ + public ConvolveOp(Kernel kernel, int edgeCondition, RenderingHints hints) { + this.kernel = kernel; + this.edgeCond = edgeCondition; + this.rhs = hints; + } + + /** + * Instantiates a new ConvolveOp object with the specified Kernel and + * EDGE_ZERO_FILL edge condition. + * + * @param kernel + * the specified Kernel. + */ + public ConvolveOp(Kernel kernel) { + this.kernel = kernel; + this.edgeCond = EDGE_ZERO_FILL; + } + + /** + * Gets the Kernel object of this ConvolveOp. + * + * @return the Kernel object of this ConvolveOp. + */ + public final Kernel getKernel() { + return (Kernel)kernel.clone(); + } + + public final RenderingHints getRenderingHints() { + return rhs; + } + + /** + * Gets the edge condition of this ConvolveOp. + * + * @return the edge condition: EDGE_NO_OP or EDGE_ZERO_FILL. + */ + public int getEdgeCondition() { + return edgeCond; + } + + public final Rectangle2D getBounds2D(Raster src) { + return src.getBounds(); + } + + public final Rectangle2D getBounds2D(BufferedImage src) { + return getBounds2D(src.getRaster()); + } + + public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { + if (dstPt == null) { + dstPt = new Point2D.Float(); + } + + dstPt.setLocation(srcPt); + return dstPt; + } + + public WritableRaster createCompatibleDestRaster(Raster src) { + return src.createCompatibleWritableRaster(); + } + + public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) { + if (dstCM == null) { + dstCM = src.getColorModel(); + } + + if (dstCM instanceof IndexColorModel) { + dstCM = ColorModel.getRGBdefault(); + } + + WritableRaster r = dstCM.isCompatibleSampleModel(src.getSampleModel()) ? src.getRaster() + .createCompatibleWritableRaster(src.getWidth(), src.getHeight()) : dstCM + .createCompatibleWritableRaster(src.getWidth(), src.getHeight()); + + return new BufferedImage(dstCM, r, dstCM.isAlphaPremultiplied(), null); + } + + public final WritableRaster filter(Raster src, WritableRaster dst) { + if (src == null) { // Should throw according to spec + // awt.256=Source raster is null + throw new NullPointerException(Messages.getString("awt.256")); //$NON-NLS-1$ + } + + if (src == dst) { + // awt.257=Source raster is equal to destination + throw new IllegalArgumentException(Messages.getString("awt.257")); //$NON-NLS-1$ + } + + if (dst == null) { + dst = createCompatibleDestRaster(src); + } else if (src.getNumBands() != dst.getNumBands()) { + // awt.258=Number of source bands ({0}) is not equal to number of + // destination bands ({1}) + throw new IllegalArgumentException(Messages.getString( + "awt.258", src.getNumBands(), dst.getNumBands())); //$NON-NLS-1$ + } + + // TODO + // if (ippFilter(src, dst, BufferedImage.TYPE_CUSTOM) != 0) + if (slowFilter(src, dst) != 0) { + // awt.21F=Unable to transform source + throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$ + } + + return dst; + } + + /** + * Slow filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @return the int. + */ + private int slowFilter(Raster src, WritableRaster dst) { + try { + SampleModel sm = src.getSampleModel(); + + int numBands = src.getNumBands(); + int srcHeight = src.getHeight(); + int srcWidth = src.getWidth(); + + int xOrigin = kernel.getXOrigin(); + int yOrigin = kernel.getYOrigin(); + int kWidth = kernel.getWidth(); + int kHeight = kernel.getHeight(); + float[] data = kernel.getKernelData(null); + + int srcMinX = src.getMinX(); + int srcMinY = src.getMinY(); + int dstMinX = dst.getMinX(); + int dstMinY = dst.getMinY(); + + int srcConvMaxX = srcWidth - (kWidth - xOrigin - 1); + int srcConvMaxY = srcHeight - (kHeight - yOrigin - 1); + + int[] maxValues = new int[numBands]; + int[] masks = new int[numBands]; + int[] sampleSizes = sm.getSampleSize(); + + for (int i = 0; i < numBands; i++) { + maxValues[i] = (1 << sampleSizes[i]) - 1; + masks[i] = ~(maxValues[i]); + } + + // Processing bounds + float[] pixels = null; + pixels = src.getPixels(srcMinX, srcMinY, srcWidth, srcHeight, pixels); + float[] newPixels = new float[pixels.length]; + int rowLength = srcWidth * numBands; + if (this.edgeCond == ConvolveOp.EDGE_NO_OP) { + // top + int start = 0; + int length = yOrigin * rowLength; + System.arraycopy(pixels, start, newPixels, start, length); + // bottom + start = (srcHeight - (kHeight - yOrigin - 1)) * rowLength; + length = (kHeight - yOrigin - 1) * rowLength; + System.arraycopy(pixels, start, newPixels, start, length); + // middle + length = xOrigin * numBands; + int length1 = (kWidth - xOrigin - 1) * numBands; + start = yOrigin * rowLength; + int start1 = (yOrigin + 1) * rowLength - length1; + for (int i = yOrigin; i < (srcHeight - (kHeight - yOrigin - 1)); i++) { + System.arraycopy(pixels, start, newPixels, start, length); + System.arraycopy(pixels, start1, newPixels, start1, length1); + start += rowLength; + start1 += rowLength; + } + + } + + // Cycle over pixels to be calculated + for (int i = yOrigin; i < srcConvMaxY; i++) { + for (int j = xOrigin; j < srcConvMaxX; j++) { + + // Take kernel data in backward direction, convolution + int kernelIdx = data.length - 1; + + int pixelIndex = i * rowLength + j * numBands; + for (int hIdx = 0, rasterHIdx = i - yOrigin; hIdx < kHeight; hIdx++, rasterHIdx++) { + for (int wIdx = 0, rasterWIdx = j - xOrigin; wIdx < kWidth; wIdx++, rasterWIdx++) { + int curIndex = rasterHIdx * rowLength + rasterWIdx * numBands; + for (int idx = 0; idx < numBands; idx++) { + newPixels[pixelIndex + idx] += data[kernelIdx] + * pixels[curIndex + idx]; + } + kernelIdx--; + } + } + + // Check for overflow now + for (int idx = 0; idx < numBands; idx++) { + if (((int)newPixels[pixelIndex + idx] & masks[idx]) != 0) { + if (newPixels[pixelIndex + idx] < 0) { + newPixels[pixelIndex + idx] = 0; + } else { + newPixels[pixelIndex + idx] = maxValues[idx]; + } + } + } + } + } + + dst.setPixels(dstMinX, dstMinY, srcWidth, srcHeight, newPixels); + } catch (Exception e) { // Something goes wrong, signal error + return 1; + } + return 0; + } + + public final BufferedImage filter(BufferedImage src, BufferedImage dst) { + if (src == null) { + // awt.259=Source image is null + throw new NullPointerException(Messages.getString("awt.259")); //$NON-NLS-1$ + } + + if (src == dst) { + // awt.25A=Source equals to destination + throw new IllegalArgumentException(Messages.getString("awt.25A")); //$NON-NLS-1$ + } + + ColorModel srcCM = src.getColorModel(); + BufferedImage finalDst = null; + + if (srcCM instanceof IndexColorModel) { + src = ((IndexColorModel)srcCM).convertToIntDiscrete(src.getRaster(), true); + srcCM = src.getColorModel(); + } + + if (dst == null) { + dst = createCompatibleDestImage(src, srcCM); + } else { + if (!srcCM.equals(dst.getColorModel())) { + // Treat BufferedImage.TYPE_INT_RGB and + // BufferedImage.TYPE_INT_ARGB as same + if (!((src.getType() == BufferedImage.TYPE_INT_RGB || src.getType() == BufferedImage.TYPE_INT_ARGB) && (dst + .getType() == BufferedImage.TYPE_INT_RGB || dst.getType() == BufferedImage.TYPE_INT_ARGB))) { + finalDst = dst; + dst = createCompatibleDestImage(src, srcCM); + } + } + } + + // Skip alpha channel for TYPE_INT_RGB images + // TODO + // if (ippFilter(src.getRaster(), dst.getRaster(), src.getType()) != 0) + if (slowFilter(src.getRaster(), dst.getRaster()) != 0) { + // awt.21F=Unable to transform source + throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$ + } + + if (finalDst != null) { + Graphics2D g = finalDst.createGraphics(); + g.setComposite(AlphaComposite.Src); + g.drawImage(dst, 0, 0, null); + } else { + finalDst = dst; + } + + return finalDst; + } + + // TODO remove when this method is used + /** + * Ipp filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @param imageType + * the image type. + * @return the int. + */ + @SuppressWarnings("unused") + private int ippFilter(Raster src, WritableRaster dst, int imageType) { + int srcStride, dstStride; + boolean skipChannel = false; + int channels; + int offsets[] = null; + + switch (imageType) { + case BufferedImage.TYPE_INT_RGB: + case BufferedImage.TYPE_INT_BGR: { + channels = 4; + srcStride = src.getWidth() * 4; + dstStride = dst.getWidth() * 4; + skipChannel = true; + break; + } + + case BufferedImage.TYPE_INT_ARGB: + case BufferedImage.TYPE_INT_ARGB_PRE: + case BufferedImage.TYPE_4BYTE_ABGR: + case BufferedImage.TYPE_4BYTE_ABGR_PRE: { + channels = 4; + srcStride = src.getWidth() * 4; + dstStride = dst.getWidth() * 4; + break; + } + + case BufferedImage.TYPE_BYTE_GRAY: { + channels = 1; + srcStride = src.getWidth(); + dstStride = dst.getWidth(); + break; + } + + case BufferedImage.TYPE_3BYTE_BGR: { + channels = 3; + srcStride = src.getWidth() * 3; + dstStride = dst.getWidth() * 3; + break; + } + + case BufferedImage.TYPE_USHORT_GRAY: // TODO - could be done in + // native code? + case BufferedImage.TYPE_USHORT_565_RGB: + case BufferedImage.TYPE_USHORT_555_RGB: + case BufferedImage.TYPE_BYTE_BINARY: { + return slowFilter(src, dst); + } + + default: { + SampleModel srcSM = src.getSampleModel(); + SampleModel dstSM = dst.getSampleModel(); + + if (srcSM instanceof PixelInterleavedSampleModel + && dstSM instanceof PixelInterleavedSampleModel) { + // Check PixelInterleavedSampleModel + if (srcSM.getDataType() != DataBuffer.TYPE_BYTE + || dstSM.getDataType() != DataBuffer.TYPE_BYTE) { + return slowFilter(src, dst); + } + + channels = srcSM.getNumBands(); // Have IPP functions for 1, + // 3 and 4 channels + if (!(channels == 1 || channels == 3 || channels == 4)) { + return slowFilter(src, dst); + } + + srcStride = ((ComponentSampleModel)srcSM).getScanlineStride(); + dstStride = ((ComponentSampleModel)dstSM).getScanlineStride(); + } else if (srcSM instanceof SinglePixelPackedSampleModel + && dstSM instanceof SinglePixelPackedSampleModel) { + // Check SinglePixelPackedSampleModel + SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel)srcSM; + SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel)dstSM; + + channels = sppsm1.getNumBands(); + + // TYPE_INT_RGB, TYPE_INT_ARGB... + if (sppsm1.getDataType() != DataBuffer.TYPE_INT + || sppsm2.getDataType() != DataBuffer.TYPE_INT + || !(channels == 3 || channels == 4)) { + return slowFilter(src, dst); + } + + // Check compatibility of sample models + if (!Arrays.equals(sppsm1.getBitOffsets(), sppsm2.getBitOffsets()) + || !Arrays.equals(sppsm1.getBitMasks(), sppsm2.getBitMasks())) { + return slowFilter(src, dst); + } + + for (int i = 0; i < channels; i++) { + if (sppsm1.getSampleSize(i) != 8) { + return slowFilter(src, dst); + } + } + + if (channels == 3) { // Cannot skip channel, don't know + // which is alpha... + channels = 4; + } + + srcStride = sppsm1.getScanlineStride() * 4; + dstStride = sppsm2.getScanlineStride() * 4; + } else { + return slowFilter(src, dst); + } + + // Fill offsets if there's a child raster + if (src.getParent() != null || dst.getParent() != null) { + if (src.getSampleModelTranslateX() != 0 || src.getSampleModelTranslateY() != 0 + || dst.getSampleModelTranslateX() != 0 + || dst.getSampleModelTranslateY() != 0) { + offsets = new int[4]; + offsets[0] = -src.getSampleModelTranslateX() + src.getMinX(); + offsets[1] = -src.getSampleModelTranslateY() + src.getMinY(); + offsets[2] = -dst.getSampleModelTranslateX() + dst.getMinX(); + offsets[3] = -dst.getSampleModelTranslateY() + dst.getMinY(); + } + } + } + } + + Object srcData, dstData; + AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance(); + try { + srcData = dbAccess.getData(src.getDataBuffer()); + dstData = dbAccess.getData(dst.getDataBuffer()); + } catch (IllegalArgumentException e) { + return -1; // Unknown data buffer type + } + + return ippFilter32f(kernel.data, kernel.getWidth(), kernel.getHeight(), + kernel.getXOrigin(), kernel.getYOrigin(), edgeCond, srcData, src.getWidth(), src + .getHeight(), srcStride, dstData, dst.getWidth(), dst.getHeight(), + dstStride, channels, skipChannel, offsets); + } + + /** + * Ipp filter32f. + * + * @param kernel + * the kernel. + * @param kWidth + * the k width. + * @param kHeight + * the k height. + * @param anchorX + * the anchor x. + * @param anchorY + * the anchor y. + * @param borderType + * the border type. + * @param src + * the src. + * @param srcWidth + * the src width. + * @param srcHeight + * the src height. + * @param srcStride + * the src stride. + * @param dst + * the dst. + * @param dstWidth + * the dst width. + * @param dstHeight + * the dst height. + * @param dstStride + * the dst stride. + * @param channels + * the channels. + * @param skipChannel + * the skip channel. + * @param offsets + * the offsets. + * @return the int. + */ + private native int ippFilter32f(float kernel[], int kWidth, int kHeight, int anchorX, + int anchorY, int borderType, Object src, int srcWidth, int srcHeight, int srcStride, + Object dst, int dstWidth, int dstHeight, int dstStride, int channels, + boolean skipChannel, int offsets[]); +} diff --git a/awt/java/awt/image/CropImageFilter.java b/awt/java/awt/image/CropImageFilter.java new file mode 100644 index 000000000..2f4ca7813 --- /dev/null +++ b/awt/java/awt/image/CropImageFilter.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.util.Hashtable; + +/** + * The CropImageFilter class crops a rectangular region of an source Image and + * provides a source for a new image containing the extracted region. + * + * @since Android 1.0 + */ +public class CropImageFilter extends ImageFilter { + + /** + * The HEIGHT. + */ + private final int X, Y, WIDTH, HEIGHT; + + /** + * Instantiates a new CropImageFilter object with the specified rectangular + * area. + * + * @param x + * the X coordinate of rectangular area. + * @param y + * the Y coordinate of rectangular area. + * @param w + * the width of rectangular area. + * @param h + * the height of rectangular area. + */ + public CropImageFilter(int x, int y, int w, int h) { + X = x; + Y = y; + WIDTH = w; + HEIGHT = h; + } + + @SuppressWarnings("unchecked") + @Override + public void setProperties(Hashtable props) { + Hashtable fprops; + if (props == null) { + fprops = new Hashtable(); + } else { + fprops = (Hashtable)props.clone(); + } + String propName = "Crop Filters"; //$NON-NLS-1$ + String prop = "x=" + X + "; y=" + Y + "; width=" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + WIDTH + "; height=" + HEIGHT; //$NON-NLS-1$ + Object o = fprops.get(propName); + if (o != null) { + if (o instanceof String) { + prop = (String)o + "; " + prop; //$NON-NLS-1$ + } else { + prop = o.toString() + "; " + prop; //$NON-NLS-1$ + } + } + fprops.put(propName, prop); + consumer.setProperties(fprops); + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, + int scansize) { + + if (x + w < X || X + WIDTH < x || y + h < Y || Y + HEIGHT < y) { + return; + } + + int destX, destY, destWidth, destHeight, endX, endY, srcEndX, srcEndY; + + int newOffset = off; + + endX = X + WIDTH; + endY = Y + HEIGHT; + + srcEndX = x + w; + srcEndY = y + h; + + if (x <= X) { + destX = 0; + newOffset += X; + if (endX >= srcEndX) { + destWidth = srcEndX - X; + } else { + destWidth = WIDTH; + } + } else { + destX = x - X; + if (endX >= srcEndX) { + destWidth = w; + } else { + destWidth = endX - x; + } + } + + if (y <= Y) { + newOffset += scansize * (Y - y); + destY = 0; + if (endY >= srcEndY) { + destHeight = srcEndY - Y; + } else { + destHeight = HEIGHT; + } + } else { + destY = y - Y; + if (endY >= srcEndY) { + destHeight = h; + } else { + destHeight = endY - y; + } + } + consumer.setPixels(destX, destY, destWidth, destHeight, model, pixels, newOffset, scansize); + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, + int scansize) { + + if (x + w < X || X + WIDTH < x || y + h < Y || Y + HEIGHT < y) { + return; + } + + int destX, destY, destWidth, destHeight, endX, endY, srcEndX, srcEndY; + + int newOffset = off; + + endX = X + WIDTH; + endY = Y + HEIGHT; + + srcEndX = x + w; + srcEndY = y + h; + + if (x <= X) { + destX = 0; + newOffset += X; + if (endX >= srcEndX) { + destWidth = srcEndX - X; + } else { + destWidth = WIDTH; + } + } else { + destX = x - X; + if (endX >= srcEndX) { + destWidth = w; + } else { + destWidth = endX - x; + } + } + + if (y <= Y) { + newOffset += scansize * (Y - y); + destY = 0; + if (endY >= srcEndY) { + destHeight = srcEndY - Y; + } else { + destHeight = HEIGHT; + } + } else { + destY = y - Y; + if (endY >= srcEndY) { + destHeight = h; + } else { + destHeight = endY - y; + } + } + consumer.setPixels(destX, destY, destWidth, destHeight, model, pixels, newOffset, scansize); + } + + @Override + public void setDimensions(int w, int h) { + consumer.setDimensions(WIDTH, HEIGHT); + } + +} diff --git a/awt/java/awt/image/DataBuffer.java b/awt/java/awt/image/DataBuffer.java new file mode 100644 index 000000000..92f900fd3 --- /dev/null +++ b/awt/java/awt/image/DataBuffer.java @@ -0,0 +1,481 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import org.apache.harmony.awt.gl.image.DataBufferListener; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class DataBuffer is a wrapper class for a data array to be used for the + * situation where a suite of functionality acts on a set of data in a + * consistent way even though the primitive type of the data may vary from one + * use to the next. + * + * @since Android 1.0 + */ +public abstract class DataBuffer { + + /** + * The Constant TYPE_BYTE. + */ + public static final int TYPE_BYTE = 0; + + /** + * The Constant TYPE_USHORT. + */ + public static final int TYPE_USHORT = 1; + + /** + * The Constant TYPE_SHORT. + */ + public static final int TYPE_SHORT = 2; + + /** + * The Constant TYPE_INT. + */ + public static final int TYPE_INT = 3; + + /** + * The Constant TYPE_FLOAT. + */ + public static final int TYPE_FLOAT = 4; + + /** + * The Constant TYPE_DOUBLE. + */ + public static final int TYPE_DOUBLE = 5; + + /** + * The Constant TYPE_UNDEFINED. + */ + public static final int TYPE_UNDEFINED = 32; + + /** + * The data type indicates the primitive type of the data in this + * DataBuffer. + */ + protected int dataType; + + /** + * The number of data arrays in this DataBuffer. + */ + protected int banks; + + /** + * The starting index for reading the data from the first (or only) internal + * data array. + */ + protected int offset; + + /** + * The length (number of elements) of the data arrays. + */ + protected int size; + + /** + * The starting indices for reading the data from the internal data arrays. + */ + protected int offsets[]; + + /** + * The data changed. + */ + boolean dataChanged = true; + + /** + * The data taken. + */ + boolean dataTaken = false; + + /** + * The listener. + */ + DataBufferListener listener; + + static { + AwtImageBackdoorAccessorImpl.init(); + } + + /** + * Instantiates a new data buffer. + * + * @param dataType + * the data type. + * @param size + * the length (number of elements) of the data arrays. + * @param numBanks + * the number of data arrays to create. + * @param offsets + * the starting indices for reading the data from the internal + * data arrays. + */ + protected DataBuffer(int dataType, int size, int numBanks, int[] offsets) { + this.dataType = dataType; + this.size = size; + this.banks = numBanks; + this.offsets = offsets.clone(); + this.offset = offsets[0]; + } + + /** + * Instantiates a new data buffer with all of the data arrays starting at + * the same index. + * + * @param dataType + * the data type. + * @param size + * the length (number of elements) of the data arrays. + * @param numBanks + * the number of data arrays to create. + * @param offset + * the offset to use for all of the data arrays. + */ + protected DataBuffer(int dataType, int size, int numBanks, int offset) { + this.dataType = dataType; + this.size = size; + this.banks = numBanks; + this.offset = offset; + this.offsets = new int[numBanks]; + int i = 0; + while (i < numBanks) { + offsets[i++] = offset; + } + } + + /** + * Instantiates a new data buffer with all of the data arrays read from the + * beginning (at offset zero). + * + * @param dataType + * the data type. + * @param size + * the length (number of elements) of the data arrays. + * @param numBanks + * the number of data arrays to create. + */ + protected DataBuffer(int dataType, int size, int numBanks) { + this.dataType = dataType; + this.size = size; + this.banks = numBanks; + this.offset = 0; + this.offsets = new int[numBanks]; + } + + /** + * Instantiates a new data buffer with one internal data array read from the + * beginning (at offset zero). + * + * @param dataType + * the data type. + * @param size + * the length (number of elements) of the data arrays. + */ + protected DataBuffer(int dataType, int size) { + this.dataType = dataType; + this.size = size; + this.banks = 1; + this.offset = 0; + this.offsets = new int[1]; + } + + /** + * Sets the data value in the specified array at the specified index. + * + * @param bank + * the internal array to the data to. + * @param i + * the index within the array where the data should be written. + * @param val + * the value to write into the array. + */ + public abstract void setElem(int bank, int i, int val); + + /** + * Sets the float data value in the specified array at the specified index. + * + * @param bank + * the internal array to the data to. + * @param i + * the index within the array where the data should be written. + * @param val + * the value to write into the array. + */ + public void setElemFloat(int bank, int i, float val) { + setElem(bank, i, (int)val); + } + + /** + * Sets the double data value in the specified array at the specified index. + * + * @param bank + * the internal array to the data to. + * @param i + * the index within the array where the data should be written. + * @param val + * the value to write into the array. + */ + public void setElemDouble(int bank, int i, double val) { + setElem(bank, i, (int)val); + } + + /** + * Sets the data value in the first array at the specified index. + * + * @param i + * the index within the array where the data should be written. + * @param val + * the value to write into the array. + */ + public void setElem(int i, int val) { + setElem(0, i, val); + } + + /** + * Gets the data value from the specified data array at the specified index. + * + * @param bank + * the data array to read from. + * @param i + * the index within the array where the data should be read. + * @return the data element. + */ + public abstract int getElem(int bank, int i); + + /** + * Gets the float-type data value from the specified data array at the + * specified index. + * + * @param bank + * the data array to read from. + * @param i + * the index within the array where the data should be read. + * @return the data element. + */ + public float getElemFloat(int bank, int i) { + return getElem(bank, i); + } + + /** + * Gets the double-type data value from the specified data array at the + * specified index. + * + * @param bank + * the data array to read from. + * @param i + * the index within the array where the data should be read. + * @return the data element. + */ + public double getElemDouble(int bank, int i) { + return getElem(bank, i); + } + + /** + * Sets the float data value in the first array at the specified index. + * + * @param i + * the index within the array where the data should be written. + * @param val + * the value to write into the array. + */ + public void setElemFloat(int i, float val) { + setElemFloat(0, i, val); + } + + /** + * Sets the double data value in the first array at the specified index. + * + * @param i + * the index within the array where the data should be written. + * @param val + * the value to write into the array. + */ + public void setElemDouble(int i, double val) { + setElemDouble(0, i, val); + } + + /** + * Gets the data value from the first data array at the specified index and + * returns it as an integer. + * + * @param i + * the index within the array where the data should be read. + * @return the data element. + */ + public int getElem(int i) { + return getElem(0, i); + } + + /** + * Gets the data value from the first data array at the specified index and + * returns it as a float. + * + * @param i + * the index within the array where the data should be read. + * @return the data element. + */ + public float getElemFloat(int i) { + return getElem(0, i); + } + + /** + * Gets the data value from the first data array at the specified index and + * returns it as a double. + * + * @param i + * the index within the array where the data should be read. + * @return the data element. + */ + public double getElemDouble(int i) { + return getElem(i); + } + + /** + * Gets the array giving the offsets corresponding to the internal data + * arrays. + * + * @return the array of offsets. + */ + public int[] getOffsets() { + return offsets; + } + + /** + * Gets the size in bits of the primitive data type. + * + * @return the size in bits of the primitive data type. + */ + public int getSize() { + return size; + } + + /** + * Gets the offset corresponding to the first internal data array. + * + * @return the offset. + */ + public int getOffset() { + return offset; + } + + /** + * Gets the number of data arrays in this DataBuffer. + * + * @return the number of data arrays. + */ + public int getNumBanks() { + return banks; + } + + /** + * Gets the primitive type of this buffer's data. + * + * @return the data type. + */ + public int getDataType() { + return this.dataType; + } + + /** + * Gets the size in bits of the primitive data type. + * + * @param type + * the primitive type. + * @return the size in bits of the primitive data type. + */ + public static int getDataTypeSize(int type) { + switch (type) { + + case TYPE_BYTE: + return 8; + + case TYPE_USHORT: + case TYPE_SHORT: + return 16; + + case TYPE_INT: + case TYPE_FLOAT: + return 32; + + case TYPE_DOUBLE: + return 64; + + default: + // awt.22C=Unknown data type {0} + throw new IllegalArgumentException(Messages.getString("awt.22C", type)); //$NON-NLS-1$ + } + } + + /** + * Notifies the listener that the data has changed. + */ + void notifyChanged() { + if (listener != null && !dataChanged) { + dataChanged = true; + listener.dataChanged(); + } + } + + /** + * Notifies the listener that the data has been released. + */ + void notifyTaken() { + if (listener != null && !dataTaken) { + dataTaken = true; + listener.dataTaken(); + } + } + + /** + * Release the data. + */ + void releaseData() { + if (listener != null && dataTaken) { + dataTaken = false; + listener.dataReleased(); + } + } + + /** + * Adds the data buffer listener. + * + * @param listener + * the listener. + */ + void addDataBufferListener(DataBufferListener listener) { + this.listener = listener; + } + + /** + * Removes the data buffer listener. + */ + void removeDataBufferListener() { + listener = null; + } + + /** + * Validate. + */ + void validate() { + dataChanged = false; + } + +} diff --git a/awt/java/awt/image/DataBufferByte.java b/awt/java/awt/image/DataBufferByte.java new file mode 100644 index 000000000..3407de84d --- /dev/null +++ b/awt/java/awt/image/DataBufferByte.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +/** + * The Class DataBufferByte is the subclass of DataBuffer for the case where the + * underlying data is of type byte. + * + * @since Android 1.0 + */ +public final class DataBufferByte extends DataBuffer { + + /** + * The data. + */ + byte data[][]; + + /** + * Instantiates a new data buffer of type unsigned short. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + * @param offsets + * the starting indices for reading the data from the internal + * data arrays. + */ + public DataBufferByte(byte dataArrays[][], int size, int offsets[]) { + super(TYPE_BYTE, size, dataArrays.length, offsets); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type unsigned short. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + */ + public DataBufferByte(byte dataArrays[][], int size) { + super(TYPE_BYTE, size, dataArrays.length); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type unsigned short with a single + * underlying array of data. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + * @param offset + * the starting index to use when reading the data. + */ + public DataBufferByte(byte dataArray[], int size, int offset) { + super(TYPE_BYTE, size, 1, offset); + data = new byte[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new data buffer of type unsigned short with a single + * underlying array of data starting at index 0. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + */ + public DataBufferByte(byte dataArray[], int size) { + super(TYPE_BYTE, size); + data = new byte[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new empty data buffer of type unsigned short with offsets + * equal to zero. + * + * @param size + * the length (number of elements) to use from the data arrays. + * @param numBanks + * the number of data arrays to create. + */ + public DataBufferByte(int size, int numBanks) { + super(TYPE_BYTE, size, numBanks); + data = new byte[numBanks][]; + int i = 0; + while (i < numBanks) { + data[i++] = new byte[size]; + } + } + + /** + * Instantiates a new empty data buffer of type unsigned short with a single + * underlying array of data starting at index 0. + * + * @param size + * the length (number of elements) to use. + */ + public DataBufferByte(int size) { + super(TYPE_BYTE, size); + data = new byte[1][]; + data[0] = new byte[size]; + } + + @Override + public void setElem(int bank, int i, int val) { + data[bank][offsets[bank] + i] = (byte)val; + notifyChanged(); + } + + @Override + public void setElem(int i, int val) { + data[0][offset + i] = (byte)val; + notifyChanged(); + } + + @Override + public int getElem(int bank, int i) { + return (data[bank][offsets[bank] + i]) & 0xff; + } + + /** + * Gets the data of the specified internal data array. + * + * @param bank + * the index of the desired data array. + * @return the data. + */ + public byte[] getData(int bank) { + notifyTaken(); + return data[bank]; + } + + @Override + public int getElem(int i) { + return (data[0][offset + i]) & 0xff; + } + + /** + * Gets the bank data. + * + * @return the bank data. + */ + public byte[][] getBankData() { + notifyTaken(); + return data.clone(); + } + + /** + * Gets the data of the first data array. + * + * @return the data. + */ + public byte[] getData() { + notifyTaken(); + return data[0]; + } + +} diff --git a/awt/java/awt/image/DataBufferDouble.java b/awt/java/awt/image/DataBufferDouble.java new file mode 100644 index 000000000..5d0a51688 --- /dev/null +++ b/awt/java/awt/image/DataBufferDouble.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +/** + * The Class DataBufferDouble is the subclass of DataBuffer for the case where + * the underlying data is of type double. + * + * @since Android 1.0 + */ +public final class DataBufferDouble extends DataBuffer { + + /** + * The data. + */ + double data[][]; + + /** + * Instantiates a new data buffer of type double. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + * @param offsets + * the starting indices for reading the data from the internal + * data arrays. + */ + public DataBufferDouble(double dataArrays[][], int size, int offsets[]) { + super(TYPE_DOUBLE, size, dataArrays.length, offsets); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type double. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + */ + public DataBufferDouble(double dataArrays[][], int size) { + super(TYPE_DOUBLE, size, dataArrays.length); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type double with a single underlying + * array of data. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + * @param offset + * the starting index to use when reading the data. + */ + public DataBufferDouble(double dataArray[], int size, int offset) { + super(TYPE_DOUBLE, size, 1, offset); + data = new double[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new data buffer of type double with a single underlying + * array of data starting at index 0. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + */ + public DataBufferDouble(double dataArray[], int size) { + super(TYPE_DOUBLE, size); + data = new double[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new empty data buffer of type double with offsets equal to + * zero. + * + * @param size + * the length (number of elements) to use from the data arrays. + * @param numBanks + * the number of data arrays to create. + */ + public DataBufferDouble(int size, int numBanks) { + super(TYPE_DOUBLE, size, numBanks); + data = new double[numBanks][]; + int i = 0; + while (i < numBanks) { + data[i++] = new double[size]; + } + } + + /** + * Instantiates a new empty data buffer of type double with a single + * underlying array of data starting at index 0. + * + * @param size + * the length (number of elements) to use. + */ + public DataBufferDouble(int size) { + super(TYPE_DOUBLE, size); + data = new double[1][]; + data[0] = new double[size]; + } + + @Override + public void setElem(int bank, int i, int val) { + data[bank][offsets[bank] + i] = val; + notifyChanged(); + } + + @Override + public void setElemFloat(int bank, int i, float val) { + data[bank][offsets[bank] + i] = val; + notifyChanged(); + } + + @Override + public void setElemDouble(int bank, int i, double val) { + data[bank][offsets[bank] + i] = val; + notifyChanged(); + } + + @Override + public void setElem(int i, int val) { + data[0][offset + i] = val; + notifyChanged(); + } + + @Override + public int getElem(int bank, int i) { + return (int)(data[bank][offsets[bank] + i]); + } + + @Override + public float getElemFloat(int bank, int i) { + return (float)(data[bank][offsets[bank] + i]); + } + + @Override + public double getElemDouble(int bank, int i) { + return data[bank][offsets[bank] + i]; + } + + @Override + public void setElemFloat(int i, float val) { + data[0][offset + i] = val; + notifyChanged(); + } + + @Override + public void setElemDouble(int i, double val) { + data[0][offset + i] = val; + notifyChanged(); + } + + /** + * Gets the data of the specified internal data array. + * + * @param bank + * the index of the desired data array. + * @return the data. + */ + public double[] getData(int bank) { + notifyTaken(); + return data[bank]; + } + + @Override + public int getElem(int i) { + return (int)(data[0][offset + i]); + } + + @Override + public float getElemFloat(int i) { + return (float)(data[0][offset + i]); + } + + @Override + public double getElemDouble(int i) { + return data[0][offset + i]; + } + + /** + * Gets the bank data. + * + * @return the bank data. + */ + public double[][] getBankData() { + notifyTaken(); + return data.clone(); + } + + /** + * Gets the data of the first data array. + * + * @return the data. + */ + public double[] getData() { + notifyTaken(); + return data[0]; + } +} diff --git a/awt/java/awt/image/DataBufferFloat.java b/awt/java/awt/image/DataBufferFloat.java new file mode 100644 index 000000000..9a4a6bfed --- /dev/null +++ b/awt/java/awt/image/DataBufferFloat.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +/** + * The Class DataBufferFloat is the subclass of DataBuffer for the case where + * the underlying data is float. + * + * @since Android 1.0 + */ +public final class DataBufferFloat extends DataBuffer { + + /** + * The data. + */ + float data[][]; + + /** + * Instantiates a new data buffer of type float. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + * @param offsets + * the starting indices for reading the data from the internal + * data arrays. + */ + public DataBufferFloat(float dataArrays[][], int size, int offsets[]) { + super(TYPE_FLOAT, size, dataArrays.length, offsets); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type float. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + */ + public DataBufferFloat(float dataArrays[][], int size) { + super(TYPE_FLOAT, size, dataArrays.length); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type float with a single underlying + * array of data. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + * @param offset + * the starting index to use when reading the data. + */ + public DataBufferFloat(float dataArray[], int size, int offset) { + super(TYPE_FLOAT, size, 1, offset); + data = new float[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new data buffer of type float with a single underlying + * array of data starting at index 0. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + */ + public DataBufferFloat(float dataArray[], int size) { + super(TYPE_FLOAT, size); + data = new float[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new empty data buffer of type float with offsets equal to + * zero. + * + * @param size + * the length (number of elements) to use from the data arrays. + * @param numBanks + * the number of data arrays to create. + */ + public DataBufferFloat(int size, int numBanks) { + super(TYPE_FLOAT, size, numBanks); + data = new float[numBanks][]; + int i = 0; + while (i < numBanks) { + data[i++] = new float[size]; + } + } + + /** + * Instantiates a new empty data buffer of type float with a single + * underlying array of data starting at index 0. + * + * @param size + * the length (number of elements) to use. + */ + public DataBufferFloat(int size) { + super(TYPE_FLOAT, size); + data = new float[1][]; + data[0] = new float[size]; + } + + @Override + public void setElem(int bank, int i, int val) { + data[bank][offsets[bank] + i] = val; + notifyChanged(); + } + + @Override + public void setElemFloat(int bank, int i, float val) { + data[bank][offsets[bank] + i] = val; + notifyChanged(); + } + + @Override + public void setElemDouble(int bank, int i, double val) { + data[bank][offsets[bank] + i] = (float)val; + notifyChanged(); + } + + @Override + public void setElem(int i, int val) { + data[0][offset + i] = val; + notifyChanged(); + } + + @Override + public int getElem(int bank, int i) { + return (int)(data[bank][offsets[bank] + i]); + } + + @Override + public float getElemFloat(int bank, int i) { + return data[bank][offsets[bank] + i]; + } + + @Override + public double getElemDouble(int bank, int i) { + return data[bank][offsets[bank] + i]; + } + + @Override + public void setElemFloat(int i, float val) { + data[0][offset + i] = val; + notifyChanged(); + } + + @Override + public void setElemDouble(int i, double val) { + data[0][offset + i] = (float)val; + notifyChanged(); + } + + /** + * Gets the data of the specified internal data array. + * + * @param bank + * the index of the desired array. + * @return the data. + */ + public float[] getData(int bank) { + notifyTaken(); + return data[bank]; + } + + @Override + public int getElem(int i) { + return (int)(data[0][offset + i]); + } + + @Override + public float getElemFloat(int i) { + return data[0][offset + i]; + } + + @Override + public double getElemDouble(int i) { + return data[0][offset + i]; + } + + /** + * Gets the bank data. + * + * @return the bank data. + */ + public float[][] getBankData() { + notifyTaken(); + return data.clone(); + } + + /** + * Gets the data of the first data array. + * + * @return the data. + */ + public float[] getData() { + notifyTaken(); + return data[0]; + } +} diff --git a/awt/java/awt/image/DataBufferInt.java b/awt/java/awt/image/DataBufferInt.java new file mode 100644 index 000000000..380a1278d --- /dev/null +++ b/awt/java/awt/image/DataBufferInt.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +/** + * The Class DataBufferInt is the subclass of DataBuffer for the case where the + * underlying data is of type integer. + * + * @since Android 1.0 + */ +public final class DataBufferInt extends DataBuffer { + + /** + * The data. + */ + int data[][]; + + /** + * Instantiates a new data buffer of type integer. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + * @param offsets + * the starting indices for reading the data from the internal + * data arrays. + */ + public DataBufferInt(int dataArrays[][], int size, int offsets[]) { + super(TYPE_INT, size, dataArrays.length, offsets); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type integer. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + */ + public DataBufferInt(int dataArrays[][], int size) { + super(TYPE_INT, size, dataArrays.length); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type integer with a single underlying + * array of data. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + * @param offset + * the starting index to use when reading the data. + */ + public DataBufferInt(int dataArray[], int size, int offset) { + super(TYPE_INT, size, 1, offset); + data = new int[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new data buffer of type integer with a single underlying + * array of data starting at index 0. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + */ + public DataBufferInt(int dataArray[], int size) { + super(TYPE_INT, size); + data = new int[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new empty data buffer of type integer with offsets equal + * to zero. + * + * @param size + * the length (number of elements) to use from the data arrays. + * @param numBanks + * the number of data arrays to create. + */ + public DataBufferInt(int size, int numBanks) { + super(TYPE_INT, size, numBanks); + data = new int[numBanks][]; + int i = 0; + while (i < numBanks) { + data[i++] = new int[size]; + } + } + + /** + * Instantiates a new empty data buffer of type integer with a single + * underlying array of data starting at index 0. + * + * @param size + * the length (number of elements) to use. + */ + public DataBufferInt(int size) { + super(TYPE_INT, size); + data = new int[1][]; + data[0] = new int[size]; + } + + @Override + public void setElem(int bank, int i, int val) { + data[bank][offsets[bank] + i] = val; + notifyChanged(); + } + + @Override + public void setElem(int i, int val) { + data[0][offset + i] = val; + notifyChanged(); + } + + @Override + public int getElem(int bank, int i) { + return data[bank][offsets[bank] + i]; + } + + /** + * Gets the data of the specified internal data array. + * + * @param bank + * the index of the desired data array. + * @return the data. + */ + public int[] getData(int bank) { + notifyTaken(); + return data[bank]; + } + + @Override + public int getElem(int i) { + return data[0][offset + i]; + } + + /** + * Gets the bank data. + * + * @return the bank data. + */ + public int[][] getBankData() { + notifyTaken(); + return data.clone(); + } + + /** + * Gets the data of the first data array. + * + * @return the data. + */ + public int[] getData() { + notifyTaken(); + return data[0]; + } +} diff --git a/awt/java/awt/image/DataBufferShort.java b/awt/java/awt/image/DataBufferShort.java new file mode 100644 index 000000000..1b11b29c3 --- /dev/null +++ b/awt/java/awt/image/DataBufferShort.java @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +/** + * The Class DataBufferShort is the subclass of DataBuffer for the case where + * the underlying data is short. + * + * @since Android 1.0 + */ +public final class DataBufferShort extends DataBuffer { + + /** + * The data. + */ + short data[][]; + + /** + * Instantiates a new data buffer of type short. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + * @param offsets + * the starting indices for reading the data from the internal + * data arrays. + */ + public DataBufferShort(short dataArrays[][], int size, int offsets[]) { + super(TYPE_SHORT, size, dataArrays.length, offsets); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type short. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + */ + public DataBufferShort(short dataArrays[][], int size) { + super(TYPE_SHORT, size, dataArrays.length); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type short with a single underlying + * array of data. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + * @param offset + * the starting index to use when reading the data. + */ + public DataBufferShort(short dataArray[], int size, int offset) { + super(TYPE_SHORT, size, 1, offset); + data = new short[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new data buffer of type short with a single underlying + * array of data starting at index 0. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + */ + public DataBufferShort(short dataArray[], int size) { + super(TYPE_SHORT, size); + data = new short[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new data buffer of type short with offsets equal to zero. + * + * @param size + * the length (number of elements) to use from the data arrays. + * @param numBanks + * the number of data arrays to create. + */ + public DataBufferShort(int size, int numBanks) { + super(TYPE_SHORT, size, numBanks); + data = new short[numBanks][]; + int i = 0; + while (i < numBanks) { + data[i++] = new short[size]; + } + } + + /** + * Instantiates a new empty data buffer of type short with a single + * underlying array of data starting at index 0. + * + * @param size + * the length (number of elements) to use. + */ + public DataBufferShort(int size) { + super(TYPE_SHORT, size); + data = new short[1][]; + data[0] = new short[size]; + } + + @Override + public void setElem(int bank, int i, int val) { + data[bank][offsets[bank] + i] = (short)val; + notifyChanged(); + } + + @Override + public void setElem(int i, int val) { + data[0][offset + i] = (short)val; + notifyChanged(); + } + + @Override + public int getElem(int bank, int i) { + return (data[bank][offsets[bank] + i]); + } + + /** + * Gets the data of the specified internal data array. + * + * @param bank + * the index of the desired data array. + * @return the data. + */ + public short[] getData(int bank) { + notifyTaken(); + return data[bank]; + } + + @Override + public int getElem(int i) { + return (data[0][offset + i]); + } + + /** + * Gets the bank data. + * + * @return the bank data. + */ + public short[][] getBankData() { + notifyTaken(); + return data.clone(); + } + + /** + * Gets the data of the first data array. + * + * @return the data. + */ + public short[] getData() { + notifyTaken(); + return data[0]; + } +} diff --git a/awt/java/awt/image/DataBufferUShort.java b/awt/java/awt/image/DataBufferUShort.java new file mode 100644 index 000000000..58d9d8340 --- /dev/null +++ b/awt/java/awt/image/DataBufferUShort.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class DataBufferUShort is the subclass of DataBuffer for the case where + * the underlying data is unsigned short. + * + * @since Android 1.0 + */ +public final class DataBufferUShort extends DataBuffer { + + /** + * The data. + */ + short data[][]; + + /** + * Instantiates a new data buffer of type unsigned short. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + * @param offsets + * the starting indices for reading the data from the internal + * data arrays. + */ + public DataBufferUShort(short dataArrays[][], int size, int offsets[]) { + super(TYPE_USHORT, size, dataArrays.length, offsets); + for (int i = 0; i < dataArrays.length; i++) { + if (dataArrays[i].length < offsets[i] + size) { + // awt.28d=Length of dataArray[{0}] is less than size + + // offset[{1}] + throw new IllegalArgumentException(Messages.getString("awt.28D", i, i)); //$NON-NLS-1$ + } + } + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type unsigned short. + * + * @param dataArrays + * the data arrays to copy the data from. + * @param size + * the length (number of elements) to use from the data arrays. + */ + public DataBufferUShort(short dataArrays[][], int size) { + super(TYPE_USHORT, size, dataArrays.length); + data = dataArrays.clone(); + } + + /** + * Instantiates a new data buffer of type unsigned short with a single + * underlying array of data. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + * @param offset + * the starting index to use when reading the data. + */ + public DataBufferUShort(short dataArray[], int size, int offset) { + super(TYPE_USHORT, size, 1, offset); + if (dataArray.length < size + offset) { + // awt.28E=Length of dataArray is less than size + offset + throw new IllegalArgumentException(Messages.getString("awt.28E")); //$NON-NLS-1$ + } + data = new short[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new data buffer of type unsigned short with a single + * underlying array of data starting at index 0. + * + * @param dataArray + * the data array to copy the data from. + * @param size + * the length (number of elements) to use. + */ + public DataBufferUShort(short dataArray[], int size) { + super(TYPE_USHORT, size); + data = new short[1][]; + data[0] = dataArray; + } + + /** + * Instantiates a new empty data buffer of type unsigned short with offsets + * equal to zero. + * + * @param size + * the length (number of elements) to use from the data arrays. + * @param numBanks + * the number of data arrays to create. + */ + public DataBufferUShort(int size, int numBanks) { + super(TYPE_USHORT, size, numBanks); + data = new short[numBanks][]; + int i = 0; + while (i < numBanks) { + data[i++] = new short[size]; + } + } + + /** + * Instantiates a new empty data buffer of type unsigned short with a single + * underlying array of data starting at index 0. + * + * @param size + * the length (number of elements) to use. + */ + public DataBufferUShort(int size) { + super(TYPE_USHORT, size); + data = new short[1][]; + data[0] = new short[size]; + } + + @Override + public void setElem(int bank, int i, int val) { + data[bank][offsets[bank] + i] = (short)val; + notifyChanged(); + } + + @Override + public void setElem(int i, int val) { + data[0][offset + i] = (short)val; + notifyChanged(); + } + + @Override + public int getElem(int bank, int i) { + return (data[bank][offsets[bank] + i]) & 0xffff; + } + + /** + * Gets the data of the specified internal data array. + * + * @param bank + * the index of the desired data array. + * @return the data. + */ + public short[] getData(int bank) { + notifyTaken(); + return data[bank]; + } + + @Override + public int getElem(int i) { + return (data[0][offset + i]) & 0xffff; + } + + /** + * Gets the bank data. + * + * @return the bank data. + */ + public short[][] getBankData() { + notifyTaken(); + return data.clone(); + } + + /** + * Gets the data of the first data array. + * + * @return the data. + */ + public short[] getData() { + notifyTaken(); + return data[0]; + } +} diff --git a/awt/java/awt/image/DirectColorModel.java b/awt/java/awt/image/DirectColorModel.java new file mode 100644 index 000000000..700eb7a39 --- /dev/null +++ b/awt/java/awt/image/DirectColorModel.java @@ -0,0 +1,889 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.color.ColorSpace; +import java.awt.Transparency; +import java.util.Arrays; + +import org.apache.harmony.awt.gl.color.LUTColorConverter; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class DirectColorModel represents a standard (packed) RGB color model + * with additional support for converting between sRGB color space and 8 or 16 + * bit linear RGB color space using lookup tables. + * + * @since Android 1.0 + */ +public class DirectColorModel extends PackedColorModel { + + /** + * The from_ linea r_ rg b_ lut. + */ + private byte from_LINEAR_RGB_LUT[]; // Lookup table for conversion from + + // Linear RGB Color Space into sRGB + + /** + * The to_ linea r_8 rg b_ lut. + */ + private byte to_LINEAR_8RGB_LUT[]; // Lookup table for conversion from + + // sRGB Color Space into Linear RGB + // 8 bit + + /** + * The to_ linea r_16 rg b_ lut. + */ + private short to_LINEAR_16RGB_LUT[]; // Lookup table for conversion from + + // sRGB Color Space into Linear RGB + // 16 bit + + /** + * The alpha lut. + */ + private byte alphaLUT[]; // Lookup table for scale alpha value + + /** + * The color lu ts. + */ + private byte colorLUTs[][]; // Lookup tables for scale color values + + /** + * The is_s rgb. + */ + private boolean is_sRGB; // ColorModel has sRGB ColorSpace + + /** + * The is_ linea r_ rgb. + */ + private boolean is_LINEAR_RGB; // Color Model has Linear RGB Color + + // Space + + /** + * The LINEA r_ rg b_ length. + */ + private int LINEAR_RGB_Length; // Linear RGB bit length + + /** + * The factor. + */ + private float fFactor; // Scale factor + + /** + * Instantiates a new direct color model. + * + * @param space + * the color space. + * @param bits + * the array of component masks. + * @param rmask + * the bitmask corresponding to the red band. + * @param gmask + * the bitmask corresponding to the green band. + * @param bmask + * the bitmask corresponding to the blue band. + * @param amask + * the bitmask corresponding to the alpha band. + * @param isAlphaPremultiplied + * whether the alpha is pre-multiplied in this color model. + * @param transferType + * the transfer type (primitive java type to use for the + * components). + * @throws IllegalArgumentException + * if the number of bits in the combined bitmasks for the color + * bands is less than one or greater than 32. + */ + public DirectColorModel(ColorSpace space, int bits, int rmask, int gmask, int bmask, int amask, + boolean isAlphaPremultiplied, int transferType) { + + super(space, bits, rmask, gmask, bmask, amask, isAlphaPremultiplied, + (amask == 0 ? Transparency.OPAQUE : Transparency.TRANSLUCENT), transferType); + + initLUTs(); + } + + /** + * Instantiates a new direct color model, determining the transfer type from + * the bits array, the transparency from the alpha mask, and the default + * color space {@link ColorSpace#CS_sRGB}. + * + * @param bits + * the array of component masks. + * @param rmask + * the bitmask corresponding to the red band. + * @param gmask + * the bitmask corresponding to the green band. + * @param bmask + * the bitmask corresponding to the blue band. + * @param amask + * the bitmask corresponding to the alpha band. + */ + public DirectColorModel(int bits, int rmask, int gmask, int bmask, int amask) { + + super(ColorSpace.getInstance(ColorSpace.CS_sRGB), bits, rmask, gmask, bmask, amask, false, + (amask == 0 ? Transparency.OPAQUE : Transparency.TRANSLUCENT), ColorModel + .getTransferType(bits)); + + initLUTs(); + } + + /** + * Instantiates a new direct color model with no alpha channel, determining + * the transfer type from the bits array, the default color space + * {@link ColorSpace#CS_sRGB}, and with the transparency set to + * {@link Transparency#OPAQUE}. + * + * @param bits + * the array of component masks. + * @param rmask + * the bitmask corresponding to the red band. + * @param gmask + * the bitmask corresponding to the green band. + * @param bmask + * the bitmask corresponding to the blue band. + */ + public DirectColorModel(int bits, int rmask, int gmask, int bmask) { + this(bits, rmask, gmask, bmask, 0); + } + + @Override + public Object getDataElements(int components[], int offset, Object obj) { + int pixel = 0; + for (int i = 0; i < numComponents; i++) { + pixel |= (components[offset + i] << offsets[i]) & componentMasks[i]; + } + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[]; + if (obj == null) { + ba = new byte[1]; + } else { + ba = (byte[])obj; + } + ba[0] = (byte)pixel; + obj = ba; + break; + + case DataBuffer.TYPE_USHORT: + short sa[]; + if (obj == null) { + sa = new short[1]; + } else { + sa = (short[])obj; + } + sa[0] = (short)pixel; + obj = sa; + break; + + case DataBuffer.TYPE_INT: + int ia[]; + if (obj == null) { + ia = new int[1]; + } else { + ia = (int[])obj; + } + ia[0] = pixel; + obj = ia; + break; + + default: + // awt.214=This Color Model doesn't support this transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + + return obj; + } + + @Override + public Object getDataElements(int rgb, Object pixel) { + if (equals(ColorModel.getRGBdefault())) { + int ia[]; + if (pixel == null) { + ia = new int[1]; + } else { + ia = (int[])pixel; + } + ia[0] = rgb; + return ia; + } + + int alpha = (rgb >> 24) & 0xff; + int red = (rgb >> 16) & 0xff; + int green = (rgb >> 8) & 0xff; + int blue = rgb & 0xff; + + float comp[] = new float[numColorComponents]; + float normComp[] = null; + + if (is_sRGB || is_LINEAR_RGB) { + if (is_LINEAR_RGB) { + if (LINEAR_RGB_Length == 8) { + red = to_LINEAR_8RGB_LUT[red] & 0xff; + green = to_LINEAR_8RGB_LUT[green] & 0xff; + blue = to_LINEAR_8RGB_LUT[blue] & 0xff; + } else { + red = to_LINEAR_16RGB_LUT[red] & 0xffff; + green = to_LINEAR_16RGB_LUT[green] & 0xffff; + blue = to_LINEAR_16RGB_LUT[blue] & 0xffff; + } + } + comp[0] = red / fFactor; + comp[1] = green / fFactor; + comp[2] = blue / fFactor; + if (!hasAlpha) { + normComp = comp; + } else { + float normAlpha = alpha / 255.0f; + normComp = new float[numComponents]; + for (int i = 0; i < numColorComponents; i++) { + normComp[i] = comp[i]; + } + normComp[numColorComponents] = normAlpha; + } + } else { + comp[0] = red / fFactor; + comp[1] = green / fFactor; + comp[2] = blue / fFactor; + float rgbComp[] = cs.fromRGB(comp); + if (!hasAlpha) { + normComp = rgbComp; + } else { + float normAlpha = alpha / 255.0f; + normComp = new float[numComponents]; + for (int i = 0; i < numColorComponents; i++) { + normComp[i] = rgbComp[i]; + } + normComp[numColorComponents] = normAlpha; + } + } + + int pxl = 0; + if (hasAlpha) { + float normAlpha = normComp[numColorComponents]; + alpha = (int)(normAlpha * maxValues[numColorComponents] + 0.5f); + if (isAlphaPremultiplied) { + red = (int)(normComp[0] * normAlpha * maxValues[0] + 0.5f); + green = (int)(normComp[1] * normAlpha * maxValues[1] + 0.5f); + blue = (int)(normComp[2] * normAlpha * maxValues[2] + 0.5f); + } else { + red = (int)(normComp[0] * maxValues[0] + 0.5f); + green = (int)(normComp[1] * maxValues[1] + 0.5f); + blue = (int)(normComp[2] * maxValues[2] + 0.5f); + } + pxl = (alpha << offsets[3]) & componentMasks[3]; + } else { + red = (int)(normComp[0] * maxValues[0] + 0.5f); + green = (int)(normComp[1] * maxValues[1] + 0.5f); + blue = (int)(normComp[2] * maxValues[2] + 0.5f); + } + + pxl |= ((red << offsets[0]) & componentMasks[0]) + | ((green << offsets[1]) & componentMasks[1]) + | ((blue << offsets[2]) & componentMasks[2]); + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[]; + if (pixel == null) { + ba = new byte[1]; + } else { + ba = (byte[])pixel; + } + ba[0] = (byte)pxl; + return ba; + + case DataBuffer.TYPE_USHORT: + short sa[]; + if (pixel == null) { + sa = new short[1]; + } else { + sa = (short[])pixel; + } + sa[0] = (short)pxl; + return sa; + + case DataBuffer.TYPE_INT: + int ia[]; + if (pixel == null) { + ia = new int[1]; + } else { + ia = (int[])pixel; + } + ia[0] = pxl; + return ia; + + default: + // awt.214=This Color Model doesn't support this transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + } + + @Override + public final ColorModel coerceData(WritableRaster raster, boolean isAlphaPremultiplied) { + + if (!hasAlpha || this.isAlphaPremultiplied == isAlphaPremultiplied) { + return this; + } + + int minX = raster.getMinX(); + int minY = raster.getMinY(); + int w = raster.getWidth(); + int h = raster.getHeight(); + + int components[] = null; + int transparentComponents[] = new int[numComponents]; + + float alphaFactor = maxValues[numColorComponents]; + + if (isAlphaPremultiplied) { + switch (transferType) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: + for (int i = 0; i < h; i++, minY++) { + for (int j = 0, x = minX; j < w; j++, x++) { + components = raster.getPixel(x, minY, components); + if (components[numColorComponents] == 0) { + raster.setPixel(x, minY, transparentComponents); + } else { + float alpha = components[numColorComponents] / alphaFactor; + for (int n = 0; n < numColorComponents; n++) { + components[n] = (int)(alpha * components[n] + 0.5f); + } + raster.setPixel(x, minY, components); + } + } + + } + break; + + default: + // awt.214=This Color Model doesn't support this + // transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + } else { + switch (transferType) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: + for (int i = 0; i < h; i++, minY++) { + for (int j = 0, x = minX; j < w; j++, x++) { + components = raster.getPixel(x, minY, components); + if (components[numColorComponents] != 0) { + float alpha = alphaFactor / components[numColorComponents]; + for (int n = 0; n < numColorComponents; n++) { + components[n] = (int)(alpha * components[n] + 0.5f); + } + raster.setPixel(x, minY, components); + } + } + + } + break; + + default: + // awt.214=This Color Model doesn't support this + // transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + + } + + return new DirectColorModel(cs, pixel_bits, componentMasks[0], componentMasks[1], + componentMasks[2], componentMasks[3], isAlphaPremultiplied, transferType); + } + + @Override + public String toString() { + // The output format based on 1.5 release behaviour. + // It could be reveled such way: + // BufferedImage bi = new BufferedImage(1, 1, + // BufferedImage.TYPE_INT_ARGB); + // ColorModel cm = bi.getColorModel(); + // System.out.println(cm.toString()); + String str = "DirectColorModel:" + " rmask = " + //$NON-NLS-1$ //$NON-NLS-2$ + Integer.toHexString(componentMasks[0]) + " gmask = " + //$NON-NLS-1$ + Integer.toHexString(componentMasks[1]) + " bmask = " + //$NON-NLS-1$ + Integer.toHexString(componentMasks[2]) + " amask = " + //$NON-NLS-1$ + (!hasAlpha ? "0" : Integer.toHexString(componentMasks[3])); //$NON-NLS-1$ + + return str; + } + + @Override + public final int[] getComponents(Object pixel, int components[], int offset) { + + if (components == null) { + components = new int[numComponents + offset]; + } + + int intPixel = 0; + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])pixel; + intPixel = ba[0] & 0xff; + break; + + case DataBuffer.TYPE_USHORT: + short sa[] = (short[])pixel; + intPixel = sa[0] & 0xffff; + break; + + case DataBuffer.TYPE_INT: + int ia[] = (int[])pixel; + intPixel = ia[0]; + break; + + default: + // awt.22D=This transferType ( {0} ) is not supported by this + // color model + throw new UnsupportedOperationException(Messages.getString("awt.22D", //$NON-NLS-1$ + transferType)); + } + + return getComponents(intPixel, components, offset); + } + + @Override + public int getRed(Object inData) { + int pixel = 0; + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])inData; + pixel = ba[0] & 0xff; + break; + + case DataBuffer.TYPE_USHORT: + short sa[] = (short[])inData; + pixel = sa[0] & 0xffff; + break; + + case DataBuffer.TYPE_INT: + int ia[] = (int[])inData; + pixel = ia[0]; + break; + + default: + // awt.214=This Color Model doesn't support this transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + return getRed(pixel); + } + + @Override + public int getRGB(Object inData) { + int pixel = 0; + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])inData; + pixel = ba[0] & 0xff; + break; + + case DataBuffer.TYPE_USHORT: + short sa[] = (short[])inData; + pixel = sa[0] & 0xffff; + break; + + case DataBuffer.TYPE_INT: + int ia[] = (int[])inData; + pixel = ia[0]; + break; + + default: + // awt.214=This Color Model doesn't support this transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + return getRGB(pixel); + } + + @Override + public int getGreen(Object inData) { + int pixel = 0; + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])inData; + pixel = ba[0] & 0xff; + break; + + case DataBuffer.TYPE_USHORT: + short sa[] = (short[])inData; + pixel = sa[0] & 0xffff; + break; + + case DataBuffer.TYPE_INT: + int ia[] = (int[])inData; + pixel = ia[0]; + break; + + default: + // awt.214=This Color Model doesn't support this transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + return getGreen(pixel); + } + + @Override + public int getBlue(Object inData) { + int pixel = 0; + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])inData; + pixel = ba[0] & 0xff; + break; + + case DataBuffer.TYPE_USHORT: + short sa[] = (short[])inData; + pixel = sa[0] & 0xffff; + break; + + case DataBuffer.TYPE_INT: + int ia[] = (int[])inData; + pixel = ia[0]; + break; + + default: + // awt.214=This Color Model doesn't support this transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + return getBlue(pixel); + } + + @Override + public int getAlpha(Object inData) { + int pixel = 0; + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])inData; + pixel = ba[0] & 0xff; + break; + + case DataBuffer.TYPE_USHORT: + short sa[] = (short[])inData; + pixel = sa[0] & 0xffff; + break; + + case DataBuffer.TYPE_INT: + int ia[] = (int[])inData; + pixel = ia[0]; + break; + + default: + // awt.214=This Color Model doesn't support this transferType + throw new UnsupportedOperationException(Messages.getString("awt.214")); //$NON-NLS-1$ + } + return getAlpha(pixel); + } + + @Override + public final WritableRaster createCompatibleWritableRaster(int w, int h) { + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new IllegalArgumentException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + int bandMasks[] = componentMasks.clone(); + + if (pixel_bits > 16) { + return Raster.createPackedRaster(DataBuffer.TYPE_INT, w, h, bandMasks, null); + } else if (pixel_bits > 8) { + return Raster.createPackedRaster(DataBuffer.TYPE_USHORT, w, h, bandMasks, null); + } else { + return Raster.createPackedRaster(DataBuffer.TYPE_BYTE, w, h, bandMasks, null); + } + } + + @Override + public boolean isCompatibleRaster(Raster raster) { + SampleModel sm = raster.getSampleModel(); + if (!(sm instanceof SinglePixelPackedSampleModel)) { + return false; + } + + SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel)sm; + + if (sppsm.getNumBands() != numComponents) { + return false; + } + if (raster.getTransferType() != transferType) { + return false; + } + + int maskBands[] = sppsm.getBitMasks(); + return Arrays.equals(maskBands, componentMasks); + } + + @Override + public int getDataElement(int components[], int offset) { + int pixel = 0; + for (int i = 0; i < numComponents; i++) { + pixel |= (components[offset + i] << offsets[i]) & componentMasks[i]; + } + return pixel; + } + + @Override + public final int[] getComponents(int pixel, int components[], int offset) { + if (components == null) { + components = new int[numComponents + offset]; + } + for (int i = 0; i < numComponents; i++) { + components[offset + i] = (pixel & componentMasks[i]) >> offsets[i]; + } + return components; + } + + @Override + public final int getRed(int pixel) { + if (is_sRGB) { + return getComponentFrom_sRGB(pixel, 0); + } + if (is_LINEAR_RGB) { + return getComponentFrom_LINEAR_RGB(pixel, 0); + } + return getComponentFrom_RGB(pixel, 0); + } + + @Override + public final int getRGB(int pixel) { + return (getAlpha(pixel) << 24) | (getRed(pixel) << 16) | (getGreen(pixel) << 8) + | getBlue(pixel); + } + + @Override + public final int getGreen(int pixel) { + if (is_sRGB) { + return getComponentFrom_sRGB(pixel, 1); + } + if (is_LINEAR_RGB) { + return getComponentFrom_LINEAR_RGB(pixel, 1); + } + return getComponentFrom_RGB(pixel, 1); + } + + @Override + public final int getBlue(int pixel) { + if (is_sRGB) { + return getComponentFrom_sRGB(pixel, 2); + } + if (is_LINEAR_RGB) { + return getComponentFrom_LINEAR_RGB(pixel, 2); + } + return getComponentFrom_RGB(pixel, 2); + } + + @Override + public final int getAlpha(int pixel) { + if (!hasAlpha) { + return 255; + } + int a = (pixel & componentMasks[3]) >>> offsets[3]; + if (bits[3] == 8) { + return a; + } + return alphaLUT[a] & 0xff; + } + + /** + * Gets the red mask. + * + * @return the red mask. + */ + public final int getRedMask() { + return componentMasks[0]; + } + + /** + * Gets the green mask. + * + * @return the green mask. + */ + public final int getGreenMask() { + return componentMasks[1]; + } + + /** + * Gets the blue mask. + * + * @return the blue mask. + */ + public final int getBlueMask() { + return componentMasks[2]; + } + + /** + * Gets the alpha mask. + * + * @return the alpha mask. + */ + public final int getAlphaMask() { + if (hasAlpha) { + return componentMasks[3]; + } + return 0; + } + + /** + * Initialization of Lookup tables. + */ + private void initLUTs() { + is_sRGB = cs.isCS_sRGB(); + is_LINEAR_RGB = (cs == LUTColorConverter.LINEAR_RGB_CS); + + if (is_LINEAR_RGB) { + if (maxBitLength > 8) { + LINEAR_RGB_Length = 16; + from_LINEAR_RGB_LUT = LUTColorConverter.getFrom16lRGBtosRGB_LUT(); + to_LINEAR_16RGB_LUT = LUTColorConverter.getFromsRGBto16lRGB_LUT(); + } else { + LINEAR_RGB_Length = 8; + from_LINEAR_RGB_LUT = LUTColorConverter.getFrom8lRGBtosRGB_LUT(); + to_LINEAR_8RGB_LUT = LUTColorConverter.getFromsRGBto8lRGB_LUT(); + } + fFactor = ((1 << LINEAR_RGB_Length) - 1); + } else { + fFactor = 255.0f; + } + + if (hasAlpha && bits[3] != 8) { + alphaLUT = new byte[maxValues[3] + 1]; + for (int i = 0; i <= maxValues[3]; i++) { + alphaLUT[i] = (byte)(scales[3] * i + 0.5f); + } + + } + + if (!isAlphaPremultiplied) { + colorLUTs = new byte[3][]; + + if (is_sRGB) { + for (int i = 0; i < numColorComponents; i++) { + if (bits[i] != 8) { + for (int j = 0; j < i; j++) { + if (bits[i] == bits[j]) { + colorLUTs[i] = colorLUTs[j]; + break; + } + } + colorLUTs[i] = new byte[maxValues[i] + 1]; + for (int j = 0; j <= maxValues[i]; j++) { + colorLUTs[i][j] = (byte)(scales[i] * j + 0.5f); + } + } + } + } + + if (is_LINEAR_RGB) { + for (int i = 0; i < numColorComponents; i++) { + if (bits[i] != LINEAR_RGB_Length) { + for (int j = 0; j < i; j++) { + if (bits[i] == bits[j]) { + colorLUTs[i] = colorLUTs[j]; + break; + } + } + colorLUTs[i] = new byte[maxValues[i] + 1]; + for (int j = 0; j <= maxValues[0]; j++) { + int idx; + if (LINEAR_RGB_Length == 8) { + idx = (int)(scales[i] * j + 0.5f); + } else { + idx = (int)(scales[i] * j * 257.0f + 0.5f); + } + colorLUTs[i][j] = from_LINEAR_RGB_LUT[idx]; + } + } + } + } + + } + } + + /** + * This method return RGB component value if Color Model has sRGB + * ColorSpace. + * + * @param pixel + * the integer representation of the pixel. + * @param idx + * the index of the pixel component. + * @return the value of the pixel component scaled from 0 to 255. + */ + private int getComponentFrom_sRGB(int pixel, int idx) { + int comp = (pixel & componentMasks[idx]) >> offsets[idx]; + if (isAlphaPremultiplied) { + int alpha = (pixel & componentMasks[3]) >>> offsets[3]; + comp = alpha == 0 ? 0 : (int)(scales[idx] * comp * 255.0f / (scales[3] * alpha) + 0.5f); + } else if (bits[idx] != 8) { + comp = colorLUTs[idx][comp] & 0xff; + } + return comp; + } + + /** + * This method return RGB component value if Color Model has Linear RGB + * ColorSpace. + * + * @param pixel + * the integer representation of the pixel. + * @param idx + * the index of the pixel component. + * @return the value of the pixel component scaled from 0 to 255. + */ + private int getComponentFrom_LINEAR_RGB(int pixel, int idx) { + int comp = (pixel & componentMasks[idx]) >> offsets[idx]; + if (isAlphaPremultiplied) { + float factor = ((1 << LINEAR_RGB_Length) - 1); + int alpha = (pixel & componentMasks[3]) >> offsets[3]; + comp = alpha == 0 ? 0 : (int)(scales[idx] * comp * factor / (scales[3] * alpha) + 0.5f); + } else if (bits[idx] != LINEAR_RGB_Length) { + comp = colorLUTs[idx][comp] & 0xff; + } else { + comp = from_LINEAR_RGB_LUT[comp] & 0xff; + } + return comp; + } + + /** + * This method return RGB component value if Color Model has arbitrary RGB + * ColorSapce. + * + * @param pixel + * the integer representation of the pixel. + * @param idx + * the index of the pixel component. + * @return the value of the pixel component scaled from 0 to 255. + */ + private int getComponentFrom_RGB(int pixel, int idx) { + int components[] = getComponents(pixel, null, 0); + float[] normComponents = getNormalizedComponents(components, 0, null, 0); + float[] sRGBcomponents = cs.toRGB(normComponents); + return (int)(sRGBcomponents[idx] * 255.0f + 0.5f); + } + +} diff --git a/awt/java/awt/image/FilteredImageSource.java b/awt/java/awt/image/FilteredImageSource.java new file mode 100644 index 000000000..ed8558d3c --- /dev/null +++ b/awt/java/awt/image/FilteredImageSource.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.util.Hashtable; + +/** + * The FilteredImageSource class is used for producing image data for a new + * filtered version of the original image using the specified filter object. + * + * @since Android 1.0 + */ +public class FilteredImageSource implements ImageProducer { + + /** + * The source. + */ + private final ImageProducer source; + + /** + * The filter. + */ + private final ImageFilter filter; + + /** + * The cons table. + */ + private final Hashtable consTable = new Hashtable(); + + /** + * Instantiates a new FilteredImageSource object with the specified + * ImageProducer and the ImageFilter objects. + * + * @param orig + * the specified ImageProducer. + * @param imgf + * the specified ImageFilter. + */ + public FilteredImageSource(ImageProducer orig, ImageFilter imgf) { + source = orig; + filter = imgf; + } + + public synchronized boolean isConsumer(ImageConsumer ic) { + if (ic != null) { + return consTable.containsKey(ic); + } + return false; + } + + public void startProduction(ImageConsumer ic) { + addConsumer(ic); + ImageConsumer fic = consTable.get(ic); + source.startProduction(fic); + } + + public void requestTopDownLeftRightResend(ImageConsumer ic) { + if (ic != null && isConsumer(ic)) { + ImageFilter fic = (ImageFilter)consTable.get(ic); + fic.resendTopDownLeftRight(source); + } + } + + public synchronized void removeConsumer(ImageConsumer ic) { + if (ic != null && isConsumer(ic)) { + ImageConsumer fic = consTable.get(ic); + source.removeConsumer(fic); + consTable.remove(ic); + } + } + + public synchronized void addConsumer(ImageConsumer ic) { + if (ic != null && !isConsumer(ic)) { + ImageConsumer fic = filter.getFilterInstance(ic); + source.addConsumer(fic); + consTable.put(ic, fic); + } + } +} diff --git a/awt/java/awt/image/ImageConsumer.java b/awt/java/awt/image/ImageConsumer.java new file mode 100644 index 000000000..caf87d108 --- /dev/null +++ b/awt/java/awt/image/ImageConsumer.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.util.Hashtable; + +/** + * The ImageConsumer interface provides the data about the image and about how + * its data is delivered. A ImageProducer provides all of the information about + * the image using the methods defined in this interface. + * + * @since Android 1.0 + */ +public interface ImageConsumer { + + /** + * The Constant RANDOMPIXELORDER indicates that the pixels are delivered in + * a random order. + */ + public static final int RANDOMPIXELORDER = 1; + + /** + * The Constant TOPDOWNLEFTRIGHT indicates that the pixels are delivered in + * top-down, left-to-right order. + */ + public static final int TOPDOWNLEFTRIGHT = 2; + + /** + * The Constant COMPLETESCANLINES indicates that the pixels are delivered in + * complete scanline. + */ + public static final int COMPLETESCANLINES = 4; + + /** + * The Constant SINGLEPASS indicates that pixels are delivered in a single + * pass. + */ + public static final int SINGLEPASS = 8; + + /** + * The Constant SINGLEFRAME indicates that image consists of single frame. + */ + public static final int SINGLEFRAME = 16; + + /** + * The Constant IMAGEERROR indicates an image error during image producing. + */ + public static final int IMAGEERROR = 1; + + /** + * The Constant SINGLEFRAMEDONE indicates that only one of the image's + * frames is completed. + */ + public static final int SINGLEFRAMEDONE = 2; + + /** + * The Constant STATICIMAGEDONE indicates that the image is completed. + */ + public static final int STATICIMAGEDONE = 3; + + /** + * The Constant IMAGEABORTED indicates that the image producing process is + * aborted. + */ + public static final int IMAGEABORTED = 4; + + /** + * Sets the properties for the image associated with this ImageConsumer. + * + * @param props + * the properties for the image associated with this + * ImageConsumer. + */ + public void setProperties(Hashtable props); + + /** + * Sets the ColorModel object. + * + * @param model + * the new ColorModel. + */ + public void setColorModel(ColorModel model); + + /** + * Sets the pixels for the specified rectangular area of the image. + * + * @param x + * the X coordinate of rectangular area. + * @param y + * the Y coordinate of rectangular area. + * @param w + * the width of rectangular area. + * @param h + * the height of rectangular area. + * @param model + * the specified ColorModel to be used for pixels converting. + * @param pixels + * the array of pixels. + * @param off + * the offset of pixels array. + * @param scansize + * the distance from the one row of pixels to the next row in the + * specified array. + */ + public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, + int scansize); + + /** + * Sets the pixels for the specified rectangular area of the image. + * + * @param x + * the X coordinate of rectangular area. + * @param y + * the Y coordinate of rectangular area. + * @param w + * the width of rectangular area. + * @param h + * the height of rectangular area. + * @param model + * the specified ColorModel to be used for pixels converting. + * @param pixels + * the array of pixels. + * @param off + * the offset of pixels array. + * @param scansize + * the distance from the one row of pixels to the next row in the + * specified array. + */ + public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, + int scansize); + + /** + * Sets the dimensions of a source image. + * + * @param width + * the width of the image. + * @param height + * the height of the image. + */ + public void setDimensions(int width, int height); + + /** + * Sets the hint flags of pixels order, which is used by the ImageConsumer + * for obtaining pixels from the ImageProducer for which this ImageConsumer + * is added. + * + * @param hintflags + * the mask of hint flags. + */ + public void setHints(int hintflags); + + /** + * THis method is called in the one of the following cases: + *

    + *
  • The ImageProducer (for which this ImageConsumer is added) has been + * delivered all pixels of the source image.
  • + *
  • A one frame of an animation has been completed.
  • + *
  • An error while loading or producing of the image has occurred. + *
+ * + * @param status + * the status of image producing. + */ + public void imageComplete(int status); + +} diff --git a/awt/java/awt/image/ImageFilter.java b/awt/java/awt/image/ImageFilter.java new file mode 100644 index 000000000..d2c9f5024 --- /dev/null +++ b/awt/java/awt/image/ImageFilter.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.util.Hashtable; + +/** + * The ImageFilter class provides a filter for delivering image data from an + * ImageProducer to an ImageConsumer. + * + * @since Android 1.0 + */ +public class ImageFilter implements ImageConsumer, Cloneable { + + /** + * The consumer. + */ + protected ImageConsumer consumer; + + /** + * Instantiates a new ImageFilter. + */ + public ImageFilter() { + super(); + } + + /** + * Gets an instance of an ImageFilter object which performs the filtering + * for the specified ImageConsumer. + * + * @param ic + * the specified ImageConsumer. + * @return an ImageFilter used to perform the filtering for the specified + * ImageConsumer. + */ + public ImageFilter getFilterInstance(ImageConsumer ic) { + ImageFilter filter = (ImageFilter)clone(); + filter.consumer = ic; + return filter; + } + + @SuppressWarnings("unchecked") + public void setProperties(Hashtable props) { + Hashtable fprops; + if (props == null) { + fprops = new Hashtable(); + } else { + fprops = (Hashtable)props.clone(); + } + String propName = "Filters"; //$NON-NLS-1$ + String prop = "Null filter"; //$NON-NLS-1$ + Object o = fprops.get(propName); + if (o != null) { + if (o instanceof String) { + prop = (String)o + "; " + prop; //$NON-NLS-1$ + } else { + prop = o.toString() + "; " + prop; //$NON-NLS-1$ + } + } + fprops.put(propName, prop); + consumer.setProperties(fprops); + } + + /** + * Returns a copy of this ImageFilter. + * + * @return a copy of this ImageFilter. + */ + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + + /** + * Responds to a request for a Top-Down-Left-Right ordered resend of the + * pixel data from an ImageConsumer. + * + * @param ip + * the ImageProducer that provides this instance of the filter. + */ + public void resendTopDownLeftRight(ImageProducer ip) { + ip.requestTopDownLeftRightResend(this); + } + + public void setColorModel(ColorModel model) { + consumer.setColorModel(model); + } + + public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, + int scansize) { + consumer.setPixels(x, y, w, h, model, pixels, off, scansize); + } + + public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, + int scansize) { + consumer.setPixels(x, y, w, h, model, pixels, off, scansize); + } + + public void setDimensions(int width, int height) { + consumer.setDimensions(width, height); + } + + public void setHints(int hints) { + consumer.setHints(hints); + } + + public void imageComplete(int status) { + consumer.imageComplete(status); + } + +} diff --git a/awt/java/awt/image/ImageObserver.java b/awt/java/awt/image/ImageObserver.java new file mode 100644 index 000000000..21ec41bf2 --- /dev/null +++ b/awt/java/awt/image/ImageObserver.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.Image; + +/** + * the ImageObserver interface is an asynchronous update interface for receiving + * notifications about Image construction status. + * + * @since Android 1.0 + */ +public interface ImageObserver { + + /** + * The Constant WIDTH indicates that the width of the image is available. + */ + public static final int WIDTH = 1; + + /** + * The Constant HEIGHT indicates that the width of the image is available. + */ + public static final int HEIGHT = 2; + + /** + * The Constant PROPERTIES indicates that the properties of the image are + * available. + */ + public static final int PROPERTIES = 4; + + /** + * The Constant SOMEBITS indicates that more bits needed for drawing a + * scaled variation of the image pixels are available. + */ + public static final int SOMEBITS = 8; + + /** + * The Constant FRAMEBITS indicates that complete frame of a image which was + * previously drawn is now available for drawing again. + */ + public static final int FRAMEBITS = 16; + + /** + * The Constant ALLBITS indicates that an image which was previously drawn + * is now complete and can be drawn again. + */ + public static final int ALLBITS = 32; + + /** + * The Constant ERROR indicates that error occurred. + */ + public static final int ERROR = 64; + + /** + * The Constant ABORT indicates that the image producing is aborted. + */ + public static final int ABORT = 128; + + /** + * This method is called when information about an Image interface becomes + * available. This method returns true if further updates are needed, false + * if not. + * + * @param img + * the image to be observed. + * @param infoflags + * the bitwise OR combination of information flags: ABORT, + * ALLBITS, ERROR, FRAMEBITS, HEIGHT, PROPERTIES, SOMEBITS, + * WIDTH. + * @param x + * the X coordinate. + * @param y + * the Y coordinate. + * @param width + * the width. + * @param height + * the height. + * @return true if further updates are needed, false if not. + */ + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height); + +} diff --git a/awt/java/awt/image/ImageProducer.java b/awt/java/awt/image/ImageProducer.java new file mode 100644 index 000000000..9138be271 --- /dev/null +++ b/awt/java/awt/image/ImageProducer.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +/** + * The ImageProducer provides an interface for objects which produce the image + * data. ImageProducer is used for reconstructing the image. Each image contains + * an ImageProducer. + * + * @since Android 1.0 + */ +public interface ImageProducer { + + /** + * Checks if the specified ImageConsumer is registered with this + * ImageProvider or not. + * + * @param ic + * the ImageConsumer to be checked. + * @return true, if the specified ImageConsumer is registered with this + * ImageProvider, false otherwise. + */ + public boolean isConsumer(ImageConsumer ic); + + /** + * Starts a reconstruction of the image data which will be delivered to this + * consumer. This method adds the specified ImageConsumer before + * reconstructing the image. + * + * @param ic + * the specified ImageConsumer. + */ + public void startProduction(ImageConsumer ic); + + /** + * Requests the ImageProducer to resend the image data in + * ImageConsumer.TOPDOWNLEFTRIGHT order. + * + * @param ic + * the specified ImageConsumer. + */ + public void requestTopDownLeftRightResend(ImageConsumer ic); + + /** + * Deregisters the specified ImageConsumer. + * + * @param ic + * the specified ImageConsumer. + */ + public void removeConsumer(ImageConsumer ic); + + /** + * Adds the specified ImageConsumer object to this ImageProducer. + * + * @param ic + * the specified ImageConsumer. + */ + public void addConsumer(ImageConsumer ic); + +} diff --git a/awt/java/awt/image/ImagingOpException.java b/awt/java/awt/image/ImagingOpException.java new file mode 100644 index 000000000..e0c0127f4 --- /dev/null +++ b/awt/java/awt/image/ImagingOpException.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Oct 5, 2005 + */ + +package java.awt.image; + +/** + * The ImagingOpException class provides error notification when the + * BufferedImageOp or RasterOp filter methods can not perform the desired filter + * operation. + * + * @since Android 1.0 + */ +public class ImagingOpException extends RuntimeException { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 8026288481846276658L; + + /** + * Instantiates a new ImagingOpException with a detail message. + * + * @param s + * the detail message. + */ + public ImagingOpException(String s) { + super(s); + } +} diff --git a/awt/java/awt/image/IndexColorModel.java b/awt/java/awt/image/IndexColorModel.java new file mode 100644 index 000000000..0b06acda6 --- /dev/null +++ b/awt/java/awt/image/IndexColorModel.java @@ -0,0 +1,1080 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.math.BigInteger; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class IndexColorModel represents a color model in which the color values + * of the pixels are read from a palette. + * + * @since Android 1.0 + */ +public class IndexColorModel extends ColorModel { + + /** + * The color map. + */ + private int colorMap[]; // Color Map + + /** + * The map size. + */ + private int mapSize; // Color Map size + + /** + * The transparent index. + */ + private int transparentIndex; // Index of fully transparent pixel + + /** + * The gray palette. + */ + private boolean grayPalette; // Color Model has Color Map with Gray Pallete + + /** + * The valid bits. + */ + private BigInteger validBits; // Specify valid Color Map values + + /** + * The Constant CACHESIZE. + */ + private static final int CACHESIZE = 20; // Cache size. Cache used for + + // improving performace of selection + // nearest color in Color Map + + /** + * The cachetable. + */ + private final int cachetable[] = new int[CACHESIZE * 2]; // Cache table - + + // used for + + // storing RGB values and that appropriate indices + // in the Color Map + + /** + * The next insert idx. + */ + private int nextInsertIdx = 0; // Next index for insertion into Cache table + + /** + * The total inserted. + */ + private int totalInserted = 0; // Number of inserted values into Cache table + + /** + * Instantiates a new index color model. + * + * @param bits + * the array of component masks. + * @param size + * the size of the color map. + * @param cmap + * the array that gives the color mapping. + * @param start + * the start index of the color mapping data within the cmap + * array. + * @param transferType + * the transfer type (primitive java type to use for the + * components). + * @param validBits + * a list of which bits represent valid colormap values, or null + * if all are valid. + * @throws IllegalArgumentException + * if the size of the color map is less than one. + */ + public IndexColorModel(int bits, int size, int cmap[], int start, int transferType, + BigInteger validBits) { + + super(bits, IndexColorModel.createBits(true), ColorSpace.getInstance(ColorSpace.CS_sRGB), + true, false, Transparency.OPAQUE, validateTransferType(transferType)); + + if (size < 1) { + // awt.264=Size of the color map is less than 1 + throw new IllegalArgumentException(Messages.getString("awt.264")); //$NON-NLS-1$ + } + + mapSize = size; + colorMap = new int[mapSize]; + transparentIndex = -1; + + if (validBits != null) { + for (int i = 0; i < mapSize; i++) { + if (!validBits.testBit(i)) { + this.validBits = validBits; + } + break; + } + } + + transparency = Transparency.OPAQUE; + int alphaMask = 0xff000000; + int alpha = 0; + + for (int i = 0; i < mapSize; i++, start++) { + colorMap[i] = cmap[start]; + alpha = cmap[start] & alphaMask; + + if (alpha == alphaMask) { + continue; + } + if (alpha == 0) { + if (transparentIndex < 0) { + transparentIndex = i; + } + if (transparency == Transparency.OPAQUE) { + transparency = Transparency.BITMASK; + } + } else if (alpha != alphaMask && transparency != Transparency.TRANSLUCENT) { + transparency = Transparency.TRANSLUCENT; + } + + } + checkPalette(); + + } + + /** + * Instantiates a new index color model. + * + * @param bits + * the array of component masks. + * @param size + * the size of the color map. + * @param cmap + * the array that gives the color mapping. + * @param start + * the start index of the color mapping data within the cmap + * array. + * @param hasalpha + * whether this color model uses alpha. + * @param trans + * the transparency supported, @see java.awt.Transparency. + * @param transferType + * the transfer type (primitive java type to use for the + * components). + * @throws IllegalArgumentException + * if the size of the color map is less than one. + */ + public IndexColorModel(int bits, int size, int cmap[], int start, boolean hasalpha, int trans, + int transferType) { + + super(bits, IndexColorModel.createBits(hasalpha || (trans >= 0)), ColorSpace + .getInstance(ColorSpace.CS_sRGB), (hasalpha || (trans >= 0)), false, + Transparency.OPAQUE, validateTransferType(transferType)); + + if (size < 1) { + // awt.264=Size of the color map is less than 1 + throw new IllegalArgumentException(Messages.getString("awt.264")); //$NON-NLS-1$ + } + + mapSize = size; + colorMap = new int[mapSize]; + if (trans >= 0 && trans < mapSize) { + transparentIndex = trans; + transparency = Transparency.BITMASK; + } else { + transparentIndex = -1; + transparency = Transparency.OPAQUE; + } + + int alphaMask = 0xff000000; + int alpha = 0; + + for (int i = 0; i < mapSize; i++, start++) { + if (transparentIndex == i) { + colorMap[i] = cmap[start] & 0x00ffffff; + continue; + } + if (hasalpha) { + alpha = cmap[start] & alphaMask; + colorMap[i] = cmap[start]; + + if (alpha == alphaMask) { + continue; + } + if (alpha == 0) { + if (trans < 0) { + trans = i; + } + if (transparency == Transparency.OPAQUE) { + transparency = Transparency.BITMASK; + } + } else if (alpha != 0 && transparency != Transparency.TRANSLUCENT) { + transparency = Transparency.TRANSLUCENT; + } + } else { + colorMap[i] = alphaMask | cmap[start]; + } + } + checkPalette(); + + } + + /** + * Instantiates a new index color model by building the color map from + * arrays of red, green, blue, and alpha values. + * + * @param bits + * the array of component masks. + * @param size + * the size of the color map. + * @param r + * the array giving the red components of the entries in the + * color map. + * @param g + * the array giving the green components of the entries in the + * color map. + * @param b + * the array giving the blue components of the entries in the + * color map. + * @param a + * the array giving the alpha components of the entries in the + * color map. + * @throws IllegalArgumentException + * if the size of the color map is less than one. + * @throws ArrayIndexOutOfBoundsException + * if the size of one of the component arrays is less than the + * size of the color map. + */ + public IndexColorModel(int bits, int size, byte r[], byte g[], byte b[], byte a[]) { + + super(bits, IndexColorModel.createBits(true), ColorSpace.getInstance(ColorSpace.CS_sRGB), + true, false, Transparency.OPAQUE, validateTransferType(ColorModel + .getTransferType(bits))); + + createColorMap(size, r, g, b, a, -1); + checkPalette(); + } + + /** + * Instantiates a new index color model by building the color map from + * arrays of red, green, and blue values. + * + * @param bits + * the array of component masks. + * @param size + * the size of the color map. + * @param r + * the array giving the red components of the entries in the + * color map. + * @param g + * the array giving the green components of the entries in the + * color map. + * @param b + * the array giving the blue components of the entries in the + * color map. + * @param trans + * the transparency supported, @see java.awt.Transparency. + * @throws IllegalArgumentException + * if the size of the color map is less than one. + * @throws ArrayIndexOutOfBoundsException + * if the size of one of the component arrays is less than the + * size of the color map. + */ + public IndexColorModel(int bits, int size, byte r[], byte g[], byte b[], int trans) { + + super(bits, IndexColorModel.createBits((trans >= 0)), ColorSpace + .getInstance(ColorSpace.CS_sRGB), (trans >= 0), false, Transparency.OPAQUE, + validateTransferType(ColorModel.getTransferType(bits))); + + createColorMap(size, r, g, b, null, trans); + checkPalette(); + } + + /** + * Instantiates a new index color model by building the color map from + * arrays of red, green, and blue values. + * + * @param bits + * the array of component masks. + * @param size + * the size of the color map. + * @param r + * the array giving the red components of the entries in the + * color map. + * @param g + * the array giving the green components of the entries in the + * color map. + * @param b + * the array giving the blue components of the entries in the + * color map. + * @throws IllegalArgumentException + * if the size of the color map is less than one. + * @throws ArrayIndexOutOfBoundsException + * if the size of one of the component arrays is less than the + * size of the color map. + */ + public IndexColorModel(int bits, int size, byte r[], byte g[], byte b[]) { + super(bits, IndexColorModel.createBits(false), ColorSpace.getInstance(ColorSpace.CS_sRGB), + false, false, Transparency.OPAQUE, validateTransferType(ColorModel + .getTransferType(bits))); + + createColorMap(size, r, g, b, null, -1); + checkPalette(); + } + + /** + * Instantiates a new index color model. + * + * @param bits + * the array of component masks. + * @param size + * the size of the color map. + * @param cmap + * the array that gives the color mapping. + * @param start + * the start index of the color mapping data within the cmap + * array. + * @param hasalpha + * whether this color model uses alpha. + * @param trans + * the transparency supported, @see java.awt.Transparency. + * @throws IllegalArgumentException + * if the size of the color map is less than one. + */ + public IndexColorModel(int bits, int size, byte cmap[], int start, boolean hasalpha, int trans) { + + super(bits, IndexColorModel.createBits(hasalpha || (trans >= 0)), ColorSpace + .getInstance(ColorSpace.CS_sRGB), (hasalpha || (trans >= 0)), false, + Transparency.OPAQUE, validateTransferType(ColorModel.getTransferType(bits))); + + if (size < 1) { + // awt.264=Size of the color map is less than 1 + throw new IllegalArgumentException(Messages.getString("awt.264")); //$NON-NLS-1$ + } + + mapSize = size; + colorMap = new int[mapSize]; + transparentIndex = -1; + + transparency = Transparency.OPAQUE; + int alpha = 0xff000000; + + for (int i = 0; i < mapSize; i++) { + colorMap[i] = (cmap[start++] & 0xff) << 16 | (cmap[start++] & 0xff) << 8 + | (cmap[start++] & 0xff); + if (trans == i) { + if (transparency == Transparency.OPAQUE) { + transparency = Transparency.BITMASK; + } + if (hasalpha) { + start++; + } + continue; + } + if (hasalpha) { + alpha = cmap[start++] & 0xff; + if (alpha == 0) { + if (transparency == Transparency.OPAQUE) { + transparency = Transparency.BITMASK; + if (trans < 0) { + trans = i; + } + } + } else { + if (alpha != 0xff && transparency != Transparency.TRANSLUCENT) { + transparency = Transparency.TRANSLUCENT; + } + } + alpha <<= 24; + } + colorMap[i] |= alpha; + } + + if (trans >= 0 && trans < mapSize) { + transparentIndex = trans; + } + checkPalette(); + + } + + /** + * Instantiates a new index color model. + * + * @param bits + * the array of component masks. + * @param size + * the size of the color map. + * @param cmap + * the array that gives the color mapping. + * @param start + * the start index of the color mapping data within the cmap + * array. + * @param hasalpha + * whether this color model uses alpha. + * @throws IllegalArgumentException + * if the size of the color map is less than one. + */ + public IndexColorModel(int bits, int size, byte cmap[], int start, boolean hasalpha) { + + this(bits, size, cmap, start, hasalpha, -1); + } + + @Override + public Object getDataElements(int[] components, int offset, Object pixel) { + int rgb = (components[offset] << 16) | (components[offset + 1]) << 8 + | components[offset + 2]; + if (hasAlpha) { + rgb |= components[offset + 3] << 24; + } else { + rgb |= 0xff000000; + } + return getDataElements(rgb, pixel); + } + + @Override + public synchronized Object getDataElements(int rgb, Object pixel) { + int red = (rgb >> 16) & 0xff; + int green = (rgb >> 8) & 0xff; + int blue = rgb & 0xff; + int alpha = rgb >>> 24; + int pixIdx = 0; + + for (int i = 0; i < totalInserted; i++) { + int idx = i * 2; + if (rgb == cachetable[idx]) { + return createDataObject(cachetable[idx + 1], pixel); + } + } + + if (!hasAlpha && grayPalette) { + int grey = (red * 77 + green * 150 + blue * 29 + 128) >>> 8; + int minError = 255; + int error = 0; + + for (int i = 0; i < mapSize; i++) { + error = Math.abs((colorMap[i] & 0xff) - grey); + if (error < minError) { + pixIdx = i; + if (error == 0) { + break; + } + minError = error; + } + } + } else if (alpha == 0 && transparentIndex > -1) { + pixIdx = transparentIndex; + } else { + int minAlphaError = 255; + int minError = 195075; // 255^2 + 255^2 + 255^2 + int alphaError; + int error = 0; + + for (int i = 0; i < mapSize; i++) { + int pix = colorMap[i]; + if (rgb == pix) { + pixIdx = i; + break; + } + alphaError = Math.abs(alpha - (pix >>> 24)); + if (alphaError <= minAlphaError) { + minAlphaError = alphaError; + + int buf = ((pix >> 16) & 0xff) - red; + error = buf * buf; + + if (error < minError) { + buf = ((pix >> 8) & 0xff) - green; + error += buf * buf; + + if (error < minError) { + buf = (pix & 0xff) - blue; + error += buf * buf; + + if (error < minError) { + pixIdx = i; + minError = error; + } + } + } + } + } + } + + cachetable[nextInsertIdx] = rgb; + cachetable[nextInsertIdx + 1] = pixIdx; + + nextInsertIdx = (nextInsertIdx + 2) % (CACHESIZE * 2); + if (totalInserted < CACHESIZE) { + totalInserted++; + } + + return createDataObject(pixIdx, pixel); + } + + /** + * Converts an image from indexed to RGB format. + * + * @param raster + * the raster containing the source image. + * @param forceARGB + * whether to use the default RGB color model. + * @return the buffered image. + * @throws IllegalArgumentException + * if the raster is not compatible with this color model. + */ + public BufferedImage convertToIntDiscrete(Raster raster, boolean forceARGB) { + + if (!isCompatibleRaster(raster)) { + // awt.265=The raster argument is not compatible with this + // IndexColorModel + throw new IllegalArgumentException(Messages.getString("awt.265")); //$NON-NLS-1$ + } + + ColorModel model; + if (forceARGB || transparency == Transparency.TRANSLUCENT) { + model = ColorModel.getRGBdefault(); + } else if (transparency == Transparency.BITMASK) { + model = new DirectColorModel(25, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x01000000); + } else { + model = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff); + } + + int w = raster.getWidth(); + int h = raster.getHeight(); + + WritableRaster distRaster = model.createCompatibleWritableRaster(w, h); + + int minX = raster.getMinX(); + int minY = raster.getMinY(); + + Object obj = null; + int pixels[] = null; + + for (int i = 0; i < h; i++, minY++) { + obj = raster.getDataElements(minX, minY, w, 1, obj); + if (obj instanceof byte[]) { + byte ba[] = (byte[])obj; + if (pixels == null) { + pixels = new int[ba.length]; + } + for (int j = 0; j < ba.length; j++) { + pixels[j] = colorMap[ba[j] & 0xff]; + } + } else if (obj instanceof short[]) { + short sa[] = (short[])obj; + if (pixels == null) { + pixels = new int[sa.length]; + } + for (int j = 0; j < sa.length; j++) { + pixels[j] = colorMap[sa[j] & 0xffff]; + } + } + if (obj instanceof int[]) { + int ia[] = (int[])obj; + if (pixels == null) { + pixels = new int[ia.length]; + } + for (int j = 0; j < ia.length; j++) { + pixels[j] = colorMap[ia[j]]; + } + } + + distRaster.setDataElements(0, i, w, 1, pixels); + } + + return new BufferedImage(model, distRaster, false, null); + } + + /** + * Gets the valid pixels. + * + * @return the valid pixels. + */ + public BigInteger getValidPixels() { + return validBits; + } + + @Override + public String toString() { + // The output format based on 1.5 release behaviour. + // It could be reveled such way: + // BufferedImage bi = new BufferedImage(1, 1, + // BufferedImage.TYPE_BYTE_INDEXED); + // ColorModel cm = bi.getColorModel(); + // System.out.println(cm.toString()); + String str = "IndexColorModel: #pixel_bits = " + pixel_bits + //$NON-NLS-1$ + " numComponents = " + numComponents + " color space = " + cs + //$NON-NLS-1$ //$NON-NLS-2$ + " transparency = "; //$NON-NLS-1$ + + if (transparency == Transparency.OPAQUE) { + str = str + "Transparency.OPAQUE"; //$NON-NLS-1$ + } else if (transparency == Transparency.BITMASK) { + str = str + "Transparency.BITMASK"; //$NON-NLS-1$ + } else { + str = str + "Transparency.TRANSLUCENT"; //$NON-NLS-1$ + } + + str = str + " transIndex = " + transparentIndex + " has alpha = " + //$NON-NLS-1$ //$NON-NLS-2$ + hasAlpha + " isAlphaPre = " + isAlphaPremultiplied; //$NON-NLS-1$ + + return str; + } + + @Override + public int[] getComponents(Object pixel, int components[], int offset) { + int pixIdx = -1; + if (pixel instanceof byte[]) { + byte ba[] = (byte[])pixel; + pixIdx = ba[0] & 0xff; + } else if (pixel instanceof short[]) { + short sa[] = (short[])pixel; + pixIdx = sa[0] & 0xffff; + } else if (pixel instanceof int[]) { + int ia[] = (int[])pixel; + pixIdx = ia[0]; + } else { + // awt.219=This transferType is not supported by this color model + throw new UnsupportedOperationException(Messages.getString("awt.219")); //$NON-NLS-1$ + } + + return getComponents(pixIdx, components, offset); + } + + @Override + public WritableRaster createCompatibleWritableRaster(int w, int h) { + WritableRaster raster; + if (pixel_bits == 1 || pixel_bits == 2 || pixel_bits == 4) { + raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, w, h, 1, pixel_bits, null); + } else if (pixel_bits <= 8) { + raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, w, h, 1, null); + } else if (pixel_bits <= 16) { + raster = Raster.createInterleavedRaster(DataBuffer.TYPE_USHORT, w, h, 1, null); + } else { + // awt.266=The number of bits in a pixel is greater than 16 + throw new UnsupportedOperationException(Messages.getString("awt.266")); //$NON-NLS-1$ + } + + return raster; + } + + @Override + public boolean isCompatibleSampleModel(SampleModel sm) { + if (sm == null) { + return false; + } + + if (!(sm instanceof MultiPixelPackedSampleModel) && !(sm instanceof ComponentSampleModel)) { + return false; + } + + if (sm.getTransferType() != transferType) { + return false; + } + if (sm.getNumBands() != 1) { + return false; + } + + return true; + } + + @Override + public SampleModel createCompatibleSampleModel(int w, int h) { + if (pixel_bits == 1 || pixel_bits == 2 || pixel_bits == 4) { + return new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, w, h, pixel_bits); + } + int bandOffsets[] = new int[1]; + bandOffsets[0] = 0; + return new ComponentSampleModel(transferType, w, h, 1, w, bandOffsets); + + } + + @Override + public boolean isCompatibleRaster(Raster raster) { + int sampleSize = raster.getSampleModel().getSampleSize(0); + return (raster.getTransferType() == transferType && raster.getNumBands() == 1 && (1 << sampleSize) >= mapSize); + } + + @Override + public int getDataElement(int components[], int offset) { + int rgb = (components[offset] << 16) | (components[offset + 1]) << 8 + | components[offset + 2]; + + if (hasAlpha) { + rgb |= components[offset + 3] << 24; + } else { + rgb |= 0xff000000; + } + + int pixel; + + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte ba[] = (byte[])getDataElements(rgb, null); + pixel = ba[0] & 0xff; + break; + case DataBuffer.TYPE_USHORT: + short sa[] = (short[])getDataElements(rgb, null); + pixel = sa[0] & 0xffff; + break; + default: + // awt.267=The transferType is invalid + throw new UnsupportedOperationException(Messages.getString("awt.267")); //$NON-NLS-1$ + } + + return pixel; + } + + /** + * Gets the color map. + * + * @param rgb + * the destination array where the color map is written. + */ + public final void getRGBs(int rgb[]) { + System.arraycopy(colorMap, 0, rgb, 0, mapSize); + } + + /** + * Gets the red component of the color map. + * + * @param r + * the destination array. + */ + public final void getReds(byte r[]) { + for (int i = 0; i < mapSize; i++) { + r[i] = (byte)(colorMap[i] >> 16); + } + } + + /** + * Gets the green component of the color map. + * + * @param g + * the destination array. + */ + public final void getGreens(byte g[]) { + for (int i = 0; i < mapSize; i++) { + g[i] = (byte)(colorMap[i] >> 8); + } + } + + /** + * Gets the blue component of the color map. + * + * @param b + * the destination array. + */ + public final void getBlues(byte b[]) { + for (int i = 0; i < mapSize; i++) { + b[i] = (byte)colorMap[i]; + } + } + + /** + * Gets the alpha component of the color map. + * + * @param a + * the destination array. + */ + public final void getAlphas(byte a[]) { + for (int i = 0; i < mapSize; i++) { + a[i] = (byte)(colorMap[i] >> 24); + } + } + + @Override + public int[] getComponents(int pixel, int components[], int offset) { + if (components == null) { + components = new int[offset + numComponents]; + } + + components[offset + 0] = getRed(pixel); + components[offset + 1] = getGreen(pixel); + components[offset + 2] = getBlue(pixel); + if (hasAlpha && (components.length - offset) > 3) { + components[offset + 3] = getAlpha(pixel); + } + + return components; + } + + /** + * Checks if the specified pixel is valid for this color model. + * + * @param pixel + * the pixel. + * @return true, if the pixel is valid. + */ + public boolean isValid(int pixel) { + if (validBits == null) { + return (pixel >= 0 && pixel < mapSize); + } + return (pixel < mapSize && validBits.testBit(pixel)); + } + + @Override + public final int getRed(int pixel) { + return (colorMap[pixel] >> 16) & 0xff; + } + + @Override + public final int getRGB(int pixel) { + return colorMap[pixel]; + } + + @Override + public final int getGreen(int pixel) { + return (colorMap[pixel] >> 8) & 0xff; + } + + @Override + public final int getBlue(int pixel) { + return colorMap[pixel] & 0xff; + } + + @Override + public final int getAlpha(int pixel) { + return (colorMap[pixel] >> 24) & 0xff; + } + + @Override + public int[] getComponentSize() { + return bits.clone(); + } + + /** + * Checks if this color model validates pixels. + * + * @return true, if all pixels are valid, otherwise false. + */ + public boolean isValid() { + return (validBits == null); + } + + @Override + public void finalize() { + // TODO: implement + return; + } + + /** + * Gets the index that represents the transparent pixel. + * + * @return the index that represents the transparent pixel. + */ + public final int getTransparentPixel() { + return transparentIndex; + } + + @Override + public int getTransparency() { + return transparency; + } + + /** + * Gets the size of the color map. + * + * @return the map size. + */ + public final int getMapSize() { + return mapSize; + } + + /** + * Creates the color map. + * + * @param size + * the size. + * @param r + * the r. + * @param g + * the g. + * @param b + * the b. + * @param a + * the a. + * @param trans + * the trans. + */ + private void createColorMap(int size, byte r[], byte g[], byte b[], byte a[], int trans) { + if (size < 1) { + // awt.264=Size of the color map is less than 1 + throw new IllegalArgumentException(Messages.getString("awt.264")); //$NON-NLS-1$ + } + + mapSize = size; + colorMap = new int[mapSize]; + if (trans >= 0 && trans < mapSize) { + transparency = Transparency.BITMASK; + transparentIndex = trans; + } else { + transparency = Transparency.OPAQUE; + transparentIndex = -1; + } + int alpha = 0; + + for (int i = 0; i < mapSize; i++) { + colorMap[i] = ((r[i] & 0xff) << 16) | ((g[i] & 0xff) << 8) | (b[i] & 0xff); + + if (trans == i) { + continue; + } + + if (a == null) { + colorMap[i] |= 0xff000000; + } else { + alpha = a[i] & 0xff; + if (alpha == 0xff) { + colorMap[i] |= 0xff000000; + } else if (alpha == 0) { + if (transparency == Transparency.OPAQUE) { + transparency = Transparency.BITMASK; + } + if (transparentIndex < 0) { + transparentIndex = i; + } + } else { + colorMap[i] |= (a[i] & 0xff) << 24; + if (transparency != Transparency.TRANSLUCENT) { + transparency = Transparency.TRANSLUCENT; + } + } + } + + } + + } + + /** + * This method checking, if Color Map has Gray palette. + */ + private void checkPalette() { + grayPalette = false; + if (transparency > Transparency.OPAQUE) { + return; + } + int rgb = 0; + + for (int i = 0; i < mapSize; i++) { + rgb = colorMap[i]; + if (((rgb >> 16) & 0xff) != ((rgb >> 8) & 0xff) || ((rgb >> 8) & 0xff) != (rgb & 0xff)) { + return; + } + } + grayPalette = true; + } + + /** + * Construction an array pixel representation. + * + * @param colorMapIdx + * the index into Color Map. + * @param pixel + * the pixel + * @return the pixel representation array. + */ + private Object createDataObject(int colorMapIdx, Object pixel) { + if (pixel == null) { + switch (transferType) { + case DataBuffer.TYPE_BYTE: + byte[] ba = new byte[1]; + ba[0] = (byte)colorMapIdx; + pixel = ba; + break; + case DataBuffer.TYPE_USHORT: + short[] sa = new short[1]; + sa[0] = (short)colorMapIdx; + pixel = sa; + break; + default: + // awt.267=The transferType is invalid + throw new UnsupportedOperationException(Messages.getString("awt.267")); //$NON-NLS-1$ + } + } else if (pixel instanceof byte[] && transferType == DataBuffer.TYPE_BYTE) { + byte ba[] = (byte[])pixel; + ba[0] = (byte)colorMapIdx; + pixel = ba; + } else if (pixel instanceof short[] && transferType == DataBuffer.TYPE_USHORT) { + short[] sa = (short[])pixel; + sa[0] = (short)colorMapIdx; + pixel = sa; + } else if (pixel instanceof int[]) { + int ia[] = (int[])pixel; + ia[0] = colorMapIdx; + pixel = ia; + } else { + // awt.268=The pixel is not a primitive array of type transferType + throw new ClassCastException(Messages.getString("awt.268")); //$NON-NLS-1$ + } + return pixel; + } + + /** + * Creates the bits. + * + * @param hasAlpha + * the has alpha. + * @return the int[]. + */ + private static int[] createBits(boolean hasAlpha) { + + int numChannels; + if (hasAlpha) { + numChannels = 4; + } else { + numChannels = 3; + } + + int bits[] = new int[numChannels]; + for (int i = 0; i < numChannels; i++) { + bits[i] = 8; + } + + return bits; + + } + + /** + * Validate transfer type. + * + * @param transferType + * the transfer type. + * @return the int. + */ + private static int validateTransferType(int transferType) { + if (transferType != DataBuffer.TYPE_BYTE && transferType != DataBuffer.TYPE_USHORT) { + // awt.269=The transferType is not one of DataBuffer.TYPE_BYTE or + // DataBuffer.TYPE_USHORT + throw new IllegalArgumentException(Messages.getString("awt.269")); //$NON-NLS-1$ + } + return transferType; + } + + /** + * Checks if is gray palette. + * + * @return true, if is gray palette. + */ + boolean isGrayPallete() { + return grayPalette; + } + +} diff --git a/awt/java/awt/image/Kernel.java b/awt/java/awt/image/Kernel.java new file mode 100644 index 000000000..a59d27a11 --- /dev/null +++ b/awt/java/awt/image/Kernel.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Sep 28, 2005 + */ + +package java.awt.image; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Kernel class provides a matrix. This matrix is stored as a float array + * which describes how a specified pixel affects the value calculated for the + * pixel's position in the output image of a filtering operation. The X, Y + * origins indicate the kernel matrix element which corresponds to the pixel + * position for which an output value is being calculated. + * + * @since Android 1.0 + */ +public class Kernel implements Cloneable { + + /** + * The x origin. + */ + private final int xOrigin; + + /** + * The y origin. + */ + private final int yOrigin; + + /** + * The width. + */ + private int width; + + /** + * The height. + */ + private int height; + + /** + * The data. + */ + float data[]; + + /** + * Instantiates a new Kernel with the specified float array. The + * width*height elements of the data array are copied. + * + * @param width + * the width of the Kernel. + * @param height + * the height of the Kernel. + * @param data + * the data of Kernel. + */ + public Kernel(int width, int height, float[] data) { + int dataLength = width * height; + if (data.length < dataLength) { + // awt.22B=Length of data should not be less than width*height + throw new IllegalArgumentException(Messages.getString("awt.22B")); //$NON-NLS-1$ + } + + this.width = width; + this.height = height; + + this.data = new float[dataLength]; + System.arraycopy(data, 0, this.data, 0, dataLength); + + xOrigin = (width - 1) / 2; + yOrigin = (height - 1) / 2; + } + + /** + * Gets the width of this Kernel. + * + * @return the width of this Kernel. + */ + public final int getWidth() { + return width; + } + + /** + * Gets the height of this Kernel. + * + * @return the height of this Kernel. + */ + public final int getHeight() { + return height; + } + + /** + * Gets the float data array of this Kernel. + * + * @param data + * the float array where the resulted data will be stored. + * @return the float data array of this Kernel. + */ + public final float[] getKernelData(float[] data) { + if (data == null) { + data = new float[this.data.length]; + } + System.arraycopy(this.data, 0, data, 0, this.data.length); + + return data; + } + + /** + * Gets the X origin of this Kernel. + * + * @return the X origin of this Kernel. + */ + public final int getXOrigin() { + return xOrigin; + } + + /** + * Gets the Y origin of this Kernel. + * + * @return the Y origin of this Kernel. + */ + public final int getYOrigin() { + return yOrigin; + } + + /** + * Returns a copy of this Kernel object. + * + * @return the copy of this Kernel object. + */ + @Override + public Object clone() { + return new Kernel(width, height, data); + } +} diff --git a/awt/java/awt/image/LookupOp.java b/awt/java/awt/image/LookupOp.java new file mode 100644 index 000000000..3362c5cf7 --- /dev/null +++ b/awt/java/awt/image/LookupOp.java @@ -0,0 +1,661 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Oct 14, 2005 + */ + +package java.awt.image; + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Point2D; +import java.util.Arrays; + +import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The LookupOp class performs a lookup operation which transforms a source + * image by filtering each band using a table of data. The table may contain a + * single array or it may contain a different data array for each band of the + * image. + * + * @since Android 1.0 + */ +public class LookupOp implements BufferedImageOp, RasterOp { + + /** + * The lut. + */ + private final LookupTable lut; + + /** + * The hints. + */ + private RenderingHints hints; + + // TODO remove when this field is used + /** + * The can use ipp. + */ + @SuppressWarnings("unused") + private final boolean canUseIpp; + + // We don't create levels/values when it is possible to reuse old + /** + * The cached levels. + */ + private int cachedLevels[]; + + /** + * The cached values. + */ + private int cachedValues[]; + + // Number of channels for which cache is valid. + // If negative number of channels is same as positive but skipAlpha was + // specified + /** + * The valid for channels. + */ + private int validForChannels; + + /** + * The level initializer. + */ + static int levelInitializer[] = new int[0x10000]; + + static { + // TODO + // System.loadLibrary("imageops"); + + for (int i = 1; i <= 0x10000; i++) { + levelInitializer[i - 1] = i; + } + } + + /** + * Instantiates a new LookupOp object from the specified LookupTable object + * and a RenderingHints object. + * + * @param lookup + * the specified LookupTable object. + * @param hints + * the RenderingHints object or null. + */ + public LookupOp(LookupTable lookup, RenderingHints hints) { + if (lookup == null) { + throw new NullPointerException(Messages.getString("awt.01", "lookup")); //$NON-NLS-1$ //$NON-NLS-2$ + } + lut = lookup; + this.hints = hints; + canUseIpp = lut instanceof ByteLookupTable || lut instanceof ShortLookupTable; + } + + /** + * Gets the LookupTable of the specified Object. + * + * @return the LookupTable of the specified Object. + */ + public final LookupTable getTable() { + return lut; + } + + public final RenderingHints getRenderingHints() { + return hints; + } + + public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { + if (dstPt == null) { + dstPt = new Point2D.Float(); + } + + dstPt.setLocation(srcPt); + return dstPt; + } + + public final Rectangle2D getBounds2D(Raster src) { + return src.getBounds(); + } + + public final Rectangle2D getBounds2D(BufferedImage src) { + return getBounds2D(src.getRaster()); + } + + public WritableRaster createCompatibleDestRaster(Raster src) { + return src.createCompatibleWritableRaster(); + } + + public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) { + if (dstCM == null) { + dstCM = src.getColorModel(); + + // Sync transfer type with LUT for component color model + if (dstCM instanceof ComponentColorModel) { + int transferType = dstCM.getTransferType(); + if (lut instanceof ByteLookupTable) { + transferType = DataBuffer.TYPE_BYTE; + } else if (lut instanceof ShortLookupTable) { + transferType = DataBuffer.TYPE_SHORT; + } + + dstCM = new ComponentColorModel(dstCM.cs, dstCM.hasAlpha(), + dstCM.isAlphaPremultiplied, dstCM.transparency, transferType); + } + } + + WritableRaster r = dstCM.isCompatibleSampleModel(src.getSampleModel()) ? src.getRaster() + .createCompatibleWritableRaster(src.getWidth(), src.getHeight()) : dstCM + .createCompatibleWritableRaster(src.getWidth(), src.getHeight()); + + return new BufferedImage(dstCM, r, dstCM.isAlphaPremultiplied(), null); + } + + public final WritableRaster filter(Raster src, WritableRaster dst) { + if (dst == null) { + dst = createCompatibleDestRaster(src); + } else { + if (src.getNumBands() != dst.getNumBands()) { + throw new IllegalArgumentException(Messages.getString("awt.237")); //$NON-NLS-1$ } + } + if (src.getWidth() != dst.getWidth()) { + throw new IllegalArgumentException(Messages.getString("awt.28F")); //$NON-NLS-1$ } + } + if (src.getHeight() != dst.getHeight()) { + throw new IllegalArgumentException(Messages.getString("awt.290")); //$NON-NLS-1$ } + } + } + + if (lut.getNumComponents() != 1 && lut.getNumComponents() != src.getNumBands()) { + // awt.238=The number of arrays in the LookupTable does not meet the + // restrictions + throw new IllegalArgumentException(Messages.getString("awt.238")); //$NON-NLS-1$ + } + + // TODO + // if (!canUseIpp || ippFilter(src, dst, BufferedImage.TYPE_CUSTOM, + // false) != 0) + if (slowFilter(src, dst, false) != 0) { + // awt.21F=Unable to transform source + throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$ + } + + return dst; + } + + public final BufferedImage filter(BufferedImage src, BufferedImage dst) { + ColorModel srcCM = src.getColorModel(); + + if (srcCM instanceof IndexColorModel) { + // awt.220=Source should not have IndexColorModel + throw new IllegalArgumentException(Messages.getString("awt.220")); //$NON-NLS-1$ + } + + // Check if the number of scaling factors matches the number of bands + int nComponents = srcCM.getNumComponents(); + int nLUTComponents = lut.getNumComponents(); + boolean skipAlpha; + if (srcCM.hasAlpha()) { + if (nLUTComponents == 1 || nLUTComponents == nComponents - 1) { + skipAlpha = true; + } else if (nLUTComponents == nComponents) { + skipAlpha = false; + } else { + // awt.229=Number of components in the LUT does not match the + // number of bands + throw new IllegalArgumentException(Messages.getString("awt.229")); //$NON-NLS-1$ + } + } else if (nLUTComponents == 1 || nLUTComponents == nComponents) { + skipAlpha = false; + } else { + // awt.229=Number of components in the LUT does not match the number + // of bands + throw new IllegalArgumentException(Messages.getString("awt.229")); //$NON-NLS-1$ + } + + BufferedImage finalDst = null; + if (dst == null) { + finalDst = dst; + dst = createCompatibleDestImage(src, null); + } else { + if (src.getWidth() != dst.getWidth()) { + throw new IllegalArgumentException(Messages.getString("awt.291")); //$NON-NLS-1$ + } + + if (src.getHeight() != dst.getHeight()) { + throw new IllegalArgumentException(Messages.getString("awt.292")); //$NON-NLS-1$ + } + + if (!srcCM.equals(dst.getColorModel())) { + // Treat BufferedImage.TYPE_INT_RGB and + // BufferedImage.TYPE_INT_ARGB as same + if (!((src.getType() == BufferedImage.TYPE_INT_RGB || src.getType() == BufferedImage.TYPE_INT_ARGB) && (dst + .getType() == BufferedImage.TYPE_INT_RGB || dst.getType() == BufferedImage.TYPE_INT_ARGB))) { + finalDst = dst; + dst = createCompatibleDestImage(src, null); + } + } + } + + // TODO + // if (!canUseIpp || ippFilter(src.getRaster(), dst.getRaster(), + // src.getType(), skipAlpha) != 0) + if (slowFilter(src.getRaster(), dst.getRaster(), skipAlpha) != 0) { + // awt.21F=Unable to transform source + throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$ + } + + if (finalDst != null) { + Graphics2D g = finalDst.createGraphics(); + g.setComposite(AlphaComposite.Src); + g.drawImage(dst, 0, 0, null); + } else { + finalDst = dst; + } + + return dst; + } + + /** + * Slow filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @param skipAlpha + * the skip alpha. + * @return the int. + */ + private final int slowFilter(Raster src, WritableRaster dst, boolean skipAlpha) { + int minSrcX = src.getMinX(); + int minDstX = dst.getMinX(); + int minSrcY = src.getMinY(); + int minDstY = dst.getMinY(); + + int skippingChannels = skipAlpha ? 1 : 0; + int numBands2Process = src.getNumBands() - skippingChannels; + + int numBands = src.getNumBands(); + int srcHeight = src.getHeight(); + int srcWidth = src.getWidth(); + + int[] pixels = null; + int offset = lut.getOffset(); + + if (lut instanceof ByteLookupTable) { + byte[][] byteData = ((ByteLookupTable)lut).getTable(); + pixels = src.getPixels(minSrcX, minSrcY, srcWidth, srcHeight, pixels); + + if (lut.getNumComponents() != 1) { + for (int i = 0; i < pixels.length; i += numBands) { + for (int b = 0; b < numBands2Process; b++) { + pixels[i + b] = byteData[b][pixels[i + b] - offset] & 0xFF; + } + } + } else { + for (int i = 0; i < pixels.length; i += numBands) { + for (int b = 0; b < numBands2Process; b++) { + pixels[i + b] = byteData[0][pixels[i + b] - offset] & 0xFF; + } + } + } + + dst.setPixels(minDstX, minDstY, srcWidth, srcHeight, pixels); + } else if (lut instanceof ShortLookupTable) { + short[][] shortData = ((ShortLookupTable)lut).getTable(); + pixels = src.getPixels(minSrcX, minSrcY, srcWidth, srcHeight, pixels); + + if (lut.getNumComponents() != 1) { + for (int i = 0; i < pixels.length; i += numBands) { + for (int b = 0; b < numBands2Process; b++) { + pixels[i + b] = shortData[b][pixels[i + b] - offset] & 0xFFFF; + } + } + } else { + for (int i = 0; i < pixels.length; i += numBands) { + for (int b = 0; b < numBands2Process; b++) { + pixels[i + b] = shortData[0][pixels[i + b] - offset] & 0xFFFF; + } + } + } + + dst.setPixels(minDstX, minDstY, srcWidth, srcHeight, pixels); + } else { + int pixel[] = new int[src.getNumBands()]; + int maxY = minSrcY + srcHeight; + int maxX = minSrcX + srcWidth; + for (int srcY = minSrcY, dstY = minDstY; srcY < maxY; srcY++, dstY++) { + for (int srcX = minSrcX, dstX = minDstX; srcX < maxX; srcX++, dstX++) { + src.getPixel(srcX, srcY, pixel); + lut.lookupPixel(pixel, pixel); + dst.setPixel(dstX, dstY, pixel); + } + } + } + + return 0; + } + + /** + * Creates the byte levels. + * + * @param channels + * the channels. + * @param skipAlpha + * the skip alpha. + * @param levels + * the levels. + * @param values + * the values. + * @param channelsOrder + * the channels order. + */ + private final void createByteLevels(int channels, boolean skipAlpha, int levels[], + int values[], int channelsOrder[]) { + byte data[][] = ((ByteLookupTable)lut).getTable(); + int nLevels = data[0].length; + int offset = lut.getOffset(); + + // Use one data array for all channels or use several data arrays + int dataIncrement = data.length > 1 ? 1 : 0; + + for (int ch = 0, dataIdx = 0; ch < channels; dataIdx += dataIncrement, ch++) { + int channelOffset = channelsOrder == null ? ch : channelsOrder[ch]; + int channelBase = nLevels * channelOffset; + + // Skip last channel if needed, zero values are OK - + // no changes to the channel information will be done in IPP + if ((channelOffset == channels - 1 && skipAlpha) || (dataIdx >= data.length)) { + continue; + } + + System.arraycopy(levelInitializer, offset, levels, channelBase, nLevels); + for (int from = 0, to = channelBase; from < nLevels; from++, to++) { + values[to] = data[dataIdx][from] & 0xFF; + } + } + } + + /** + * Creates the short levels. + * + * @param channels + * the channels. + * @param skipAlpha + * the skip alpha. + * @param levels + * the levels. + * @param values + * the values. + * @param channelsOrder + * the channels order. + */ + private final void createShortLevels(int channels, boolean skipAlpha, int levels[], + int values[], int channelsOrder[]) { + short data[][] = ((ShortLookupTable)lut).getTable(); + int nLevels = data[0].length; + int offset = lut.getOffset(); + + // Use one data array for all channels or use several data arrays + int dataIncrement = data.length > 1 ? 1 : 0; + + for (int ch = 0, dataIdx = 0; ch < channels; dataIdx += dataIncrement, ch++) { + int channelOffset = channelsOrder == null ? ch : channelsOrder[ch]; + + // Skip last channel if needed, zero values are OK - + // no changes to the channel information will be done in IPP + if ((channelOffset == channels - 1 && skipAlpha) || (dataIdx >= data.length)) { + continue; + } + + int channelBase = nLevels * channelOffset; + System.arraycopy(levelInitializer, offset, levels, channelBase, nLevels); + for (int from = 0, to = channelBase; from < nLevels; from++, to++) { + values[to] = data[dataIdx][from] & 0xFFFF; + } + } + } + + // TODO remove when this method is used + /** + * Ipp filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @param imageType + * the image type. + * @param skipAlpha + * the skip alpha. + * @return the int. + */ + @SuppressWarnings("unused") + private final int ippFilter(Raster src, WritableRaster dst, int imageType, boolean skipAlpha) { + int res; + + int srcStride, dstStride; + int channels; + int offsets[] = null; + int channelsOrder[] = null; + + switch (imageType) { + case BufferedImage.TYPE_INT_ARGB: + case BufferedImage.TYPE_INT_ARGB_PRE: + case BufferedImage.TYPE_INT_RGB: { + channels = 4; + srcStride = src.getWidth() * 4; + dstStride = dst.getWidth() * 4; + channelsOrder = new int[] { + 2, 1, 0, 3 + }; + break; + } + + case BufferedImage.TYPE_4BYTE_ABGR: + case BufferedImage.TYPE_4BYTE_ABGR_PRE: + case BufferedImage.TYPE_INT_BGR: { + channels = 4; + srcStride = src.getWidth() * 4; + dstStride = dst.getWidth() * 4; + break; + } + + case BufferedImage.TYPE_BYTE_GRAY: { + channels = 1; + srcStride = src.getWidth(); + dstStride = dst.getWidth(); + break; + } + + case BufferedImage.TYPE_3BYTE_BGR: { + channels = 3; + srcStride = src.getWidth() * 3; + dstStride = dst.getWidth() * 3; + channelsOrder = new int[] { + 2, 1, 0 + }; + break; + } + + case BufferedImage.TYPE_USHORT_GRAY: + case BufferedImage.TYPE_USHORT_565_RGB: + case BufferedImage.TYPE_USHORT_555_RGB: + case BufferedImage.TYPE_BYTE_BINARY: { + return slowFilter(src, dst, skipAlpha); + } + + default: { + SampleModel srcSM = src.getSampleModel(); + SampleModel dstSM = dst.getSampleModel(); + + if (srcSM instanceof PixelInterleavedSampleModel + && dstSM instanceof PixelInterleavedSampleModel) { + // Check PixelInterleavedSampleModel + if (srcSM.getDataType() != DataBuffer.TYPE_BYTE + || dstSM.getDataType() != DataBuffer.TYPE_BYTE) { + return slowFilter(src, dst, skipAlpha); + } + + // Have IPP functions for 1, 3 and 4 channels + channels = srcSM.getNumBands(); + if (!(channels == 1 || channels == 3 || channels == 4)) { + return slowFilter(src, dst, skipAlpha); + } + + srcStride = ((ComponentSampleModel)srcSM).getScanlineStride(); + dstStride = ((ComponentSampleModel)dstSM).getScanlineStride(); + + channelsOrder = ((ComponentSampleModel)srcSM).getBandOffsets(); + } else if (srcSM instanceof SinglePixelPackedSampleModel + && dstSM instanceof SinglePixelPackedSampleModel) { + // Check SinglePixelPackedSampleModel + SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel)srcSM; + SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel)dstSM; + + channels = sppsm1.getNumBands(); + + // TYPE_INT_RGB, TYPE_INT_ARGB... + if (sppsm1.getDataType() != DataBuffer.TYPE_INT + || sppsm2.getDataType() != DataBuffer.TYPE_INT + || !(channels == 3 || channels == 4)) { + return slowFilter(src, dst, skipAlpha); + } + + // Check compatibility of sample models + if (!Arrays.equals(sppsm1.getBitOffsets(), sppsm2.getBitOffsets()) + || !Arrays.equals(sppsm1.getBitMasks(), sppsm2.getBitMasks())) { + return slowFilter(src, dst, skipAlpha); + } + + for (int i = 0; i < channels; i++) { + if (sppsm1.getSampleSize(i) != 8) { + return slowFilter(src, dst, skipAlpha); + } + } + + channelsOrder = new int[channels]; + int bitOffsets[] = sppsm1.getBitOffsets(); + for (int i = 0; i < channels; i++) { + channelsOrder[i] = bitOffsets[i] / 8; + } + + if (channels == 3) { // Don't skip channel now, could be + // optimized + channels = 4; + } + + srcStride = sppsm1.getScanlineStride() * 4; + dstStride = sppsm2.getScanlineStride() * 4; + } else { + return slowFilter(src, dst, skipAlpha); + } + + // Fill offsets if there's a child raster + if (src.getParent() != null || dst.getParent() != null) { + if (src.getSampleModelTranslateX() != 0 || src.getSampleModelTranslateY() != 0 + || dst.getSampleModelTranslateX() != 0 + || dst.getSampleModelTranslateY() != 0) { + offsets = new int[4]; + offsets[0] = -src.getSampleModelTranslateX() + src.getMinX(); + offsets[1] = -src.getSampleModelTranslateY() + src.getMinY(); + offsets[2] = -dst.getSampleModelTranslateX() + dst.getMinX(); + offsets[3] = -dst.getSampleModelTranslateY() + dst.getMinY(); + } + } + } + } + + int levels[] = null, values[] = null; + int channelMultiplier = skipAlpha ? -1 : 1; + if (channelMultiplier * channels == validForChannels) { // use existing + // levels/values + levels = cachedLevels; + values = cachedValues; + } else { // create new levels/values + if (lut instanceof ByteLookupTable) { + byte data[][] = ((ByteLookupTable)lut).getTable(); + levels = new int[channels * data[0].length]; + values = new int[channels * data[0].length]; + createByteLevels(channels, skipAlpha, levels, values, channelsOrder); + } else if (lut instanceof ShortLookupTable) { + short data[][] = ((ShortLookupTable)lut).getTable(); + levels = new int[channels * data[0].length]; + values = new int[channels * data[0].length]; + createShortLevels(channels, skipAlpha, levels, values, channelsOrder); + } + + // cache levels/values + validForChannels = channelMultiplier * channels; + cachedLevels = levels; + cachedValues = values; + } + + Object srcData, dstData; + AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance(); + try { + srcData = dbAccess.getData(src.getDataBuffer()); + dstData = dbAccess.getData(dst.getDataBuffer()); + } catch (IllegalArgumentException e) { + return -1; // Unknown data buffer type + } + + res = ippLUT(srcData, src.getWidth(), src.getHeight(), srcStride, dstData, dst.getWidth(), + dst.getHeight(), dstStride, levels, values, channels, offsets, false); + + return res; + } + + /** + * Ipp lut. + * + * @param src + * the src. + * @param srcWidth + * the src width. + * @param srcHeight + * the src height. + * @param srcStride + * the src stride. + * @param dst + * the dst. + * @param dstWidth + * the dst width. + * @param dstHeight + * the dst height. + * @param dstStride + * the dst stride. + * @param levels + * the levels. + * @param values + * the values. + * @param channels + * the channels. + * @param offsets + * the offsets. + * @param linear + * the linear. + * @return the int. + */ + final static native int ippLUT(Object src, int srcWidth, int srcHeight, int srcStride, + Object dst, int dstWidth, int dstHeight, int dstStride, int levels[], int values[], + int channels, int offsets[], boolean linear); +} diff --git a/awt/java/awt/image/LookupTable.java b/awt/java/awt/image/LookupTable.java new file mode 100644 index 000000000..e465a54b2 --- /dev/null +++ b/awt/java/awt/image/LookupTable.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Oct 14, 2005 + */ + +package java.awt.image; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * This abstract LookupTable class represents lookup table which is defined with + * the number of components and offset value. ByteLookupTable and + * ShortLookupTable classes are subclasses of LookupTable which contains byte + * and short data tables as an input arrays for bands or components of image. + * + * @since Android 1.0 + */ +public abstract class LookupTable { + + /** + * The offset. + */ + private int offset; + + /** + * The num components. + */ + private int numComponents; + + /** + * Instantiates a new LookupTable with the specified offset value and number + * of components. + * + * @param offset + * the offset value. + * @param numComponents + * the number of components. + */ + protected LookupTable(int offset, int numComponents) { + if (offset < 0) { + // awt.232=Offset should be not less than zero + throw new IllegalArgumentException(Messages.getString("awt.232")); //$NON-NLS-1$ + } + if (numComponents < 1) { + // awt.233=Number of components should be positive + throw new IllegalArgumentException(Messages.getString("awt.233")); //$NON-NLS-1$ + } + + this.offset = offset; + this.numComponents = numComponents; + } + + /** + * Gets the offset value of this Lookup table. + * + * @return the offset value of this Lookup table. + */ + public int getOffset() { + return offset; + } + + /** + * Gets the number of components of this Lookup table. + * + * @return the number components of this Lookup table. + */ + public int getNumComponents() { + return numComponents; + } + + /** + * Returns an integer array which contains samples of the specified pixel which + * is translated with the lookup table of this LookupTable. The resulted + * array is stored to the dst array. + * + * @param src + * the source array. + * @param dst + * the destination array where the result can be stored. + * @return the integer array of translated samples of a pixel. + */ + public abstract int[] lookupPixel(int[] src, int[] dst); +} diff --git a/awt/java/awt/image/MemoryImageSource.java b/awt/java/awt/image/MemoryImageSource.java new file mode 100644 index 000000000..644fd40fe --- /dev/null +++ b/awt/java/awt/image/MemoryImageSource.java @@ -0,0 +1,603 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.util.Hashtable; +import java.util.Vector; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The MemoryImageSource class is used to produces pixels of an image from an + * array. This class can manage a memory image which contains an animation or + * custom rendering. + * + * @since Android 1.0 + */ +public class MemoryImageSource implements ImageProducer { + + /** + * The width. + */ + int width; + + /** + * The height. + */ + int height; + + /** + * The cm. + */ + ColorModel cm; + + /** + * The b data. + */ + byte bData[]; + + /** + * The i data. + */ + int iData[]; + + /** + * The offset. + */ + int offset; + + /** + * The scanline. + */ + int scanline; + + /** + * The properties. + */ + Hashtable properties; + + /** + * The consumers. + */ + Vector consumers; + + /** + * The animated. + */ + boolean animated; + + /** + * The fullbuffers. + */ + boolean fullbuffers; + + /** + * The data type. + */ + int dataType; + + /** + * The Constant DATA_TYPE_BYTE. + */ + static final int DATA_TYPE_BYTE = 0; + + /** + * The Constant DATA_TYPE_INT. + */ + static final int DATA_TYPE_INT = 1; + + /** + * Instantiates a new MemoryImageSource with the specified parameters. + * + * @param w + * the width of the rectangular area of pixels. + * @param h + * the height of the rectangular area of pixels. + * @param cm + * the specified ColorModel. + * @param pix + * the pixel array. + * @param off + * the offset in the pixel array. + * @param scan + * the distance from one pixel's row to the next in the pixel + * array. + * @param props + * the set of properties to be used for image processing. + */ + public MemoryImageSource(int w, int h, ColorModel cm, int pix[], int off, int scan, + Hashtable props) { + init(w, h, cm, pix, off, scan, props); + } + + /** + * Instantiates a new MemoryImageSource with the specified parameters. + * + * @param w + * the width of the rectangular area of pixels. + * @param h + * the height of the rectangular area of pixels. + * @param cm + * the specified ColorModel. + * @param pix + * the pixel array. + * @param off + * the offset in the pixel array. + * @param scan + * the distance from one pixel's row to the next in the pixel + * array. + * @param props + * the set of properties to be used for image processing. + */ + public MemoryImageSource(int w, int h, ColorModel cm, byte pix[], int off, int scan, + Hashtable props) { + init(w, h, cm, pix, off, scan, props); + } + + /** + * Instantiates a new MemoryImageSource with the specified parameters and + * default RGB ColorModel. + * + * @param w + * the width of the rectangular area of pixels. + * @param h + * the height of the rectangular area of pixels. + * @param pix + * the pixel array. + * @param off + * the offset in the pixel array. + * @param scan + * the distance from one pixel's row to the next in the pixel + * array. + * @param props + * the set of properties to be used for image processing. + */ + public MemoryImageSource(int w, int h, int pix[], int off, int scan, Hashtable props) { + init(w, h, ColorModel.getRGBdefault(), pix, off, scan, props); + } + + /** + * Instantiates a new MemoryImageSource with the specified parameters. + * + * @param w + * the width of the rectangular area of pixels. + * @param h + * the height of the rectangular area of pixels. + * @param cm + * the specified ColorModel. + * @param pix + * the pixel array. + * @param off + * the offset in the pixel array. + * @param scan + * the distance from one pixel's row to the next in the pixel + * array. + */ + public MemoryImageSource(int w, int h, ColorModel cm, int pix[], int off, int scan) { + init(w, h, cm, pix, off, scan, null); + } + + /** + * Instantiates a new MemoryImageSource with the specified parameters. + * + * @param w + * the width of the rectangular area of pixels. + * @param h + * the height of the rectangular area of pixels. + * @param cm + * the specified ColorModel. + * @param pix + * the pixel array. + * @param off + * the offset in the pixel array. + * @param scan + * the distance from one pixel's row to the next in the pixel + * array. + */ + public MemoryImageSource(int w, int h, ColorModel cm, byte pix[], int off, int scan) { + init(w, h, cm, pix, off, scan, null); + } + + /** + * Instantiates a new MemoryImageSource with the specified parameters and + * default RGB ColorModel. + * + * @param w + * the width of the rectangular area of pixels. + * @param h + * the height of the rectangular area of pixels. + * @param pix + * the pixels array. + * @param off + * the offset in the pixel array. + * @param scan + * the distance from one pixel's row to the next in the pixel + * array. + */ + public MemoryImageSource(int w, int h, int pix[], int off, int scan) { + init(w, h, ColorModel.getRGBdefault(), pix, off, scan, null); + } + + public synchronized boolean isConsumer(ImageConsumer ic) { + return consumers.contains(ic); + } + + public void startProduction(ImageConsumer ic) { + if (!isConsumer(ic) && ic != null) { + consumers.addElement(ic); + } + try { + setHeader(ic); + setPixels(ic, 0, 0, width, height); + if (animated) { + ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE); + } else { + ic.imageComplete(ImageConsumer.STATICIMAGEDONE); + if (isConsumer(ic)) { + removeConsumer(ic); + } + } + } catch (Exception e) { + if (isConsumer(ic)) { + ic.imageComplete(ImageConsumer.IMAGEERROR); + } + if (isConsumer(ic)) { + removeConsumer(ic); + } + } + } + + public void requestTopDownLeftRightResend(ImageConsumer ic) { + } + + public synchronized void removeConsumer(ImageConsumer ic) { + consumers.removeElement(ic); + } + + public synchronized void addConsumer(ImageConsumer ic) { + if (ic == null || consumers.contains(ic)) { + return; + } + consumers.addElement(ic); + } + + /** + * Replaces the pixel data with a new pixel array for holding the pixels for + * this image. If an animation flag is set to true value by the + * setAnimated() method, the new pixels will be immediately delivered to the + * ImageConsumers. + * + * @param newpix + * the new pixel array. + * @param newmodel + * the new ColorModel. + * @param offset + * the offset in the array. + * @param scansize + * the distance from one row of pixels to the next row in the + * pixel array. + */ + public synchronized void newPixels(int newpix[], ColorModel newmodel, int offset, int scansize) { + this.dataType = DATA_TYPE_INT; + this.iData = newpix; + this.cm = newmodel; + this.offset = offset; + this.scanline = scansize; + newPixels(); + } + + /** + * Replaces the pixel data with a new pixel array for holding the pixels for + * this image. If an animation flag is set to true value by the + * setAnimated() method, the new pixels will be immediately delivered to the + * ImageConsumers. + * + * @param newpix + * the new pixel array. + * @param newmodel + * the new ColorModel. + * @param offset + * the offset in the array. + * @param scansize + * the distance from one row of pixels to the next row in the + * pixel array. + */ + public synchronized void newPixels(byte newpix[], ColorModel newmodel, int offset, int scansize) { + this.dataType = DATA_TYPE_BYTE; + this.bData = newpix; + this.cm = newmodel; + this.offset = offset; + this.scanline = scansize; + newPixels(); + } + + /** + * Sets the full buffer updates flag to true. If this is an animated image, + * the image consumers hints are updated accordingly. + * + * @param fullbuffers + * the true if the pixel buffer should be sent always. + */ + public synchronized void setFullBufferUpdates(boolean fullbuffers) { + if (this.fullbuffers == fullbuffers) { + return; + } + this.fullbuffers = fullbuffers; + if (animated) { + Object consAr[] = consumers.toArray(); + for (Object element : consAr) { + ImageConsumer con = (ImageConsumer)element; + try { + if (fullbuffers) { + con.setHints(ImageConsumer.TOPDOWNLEFTRIGHT + | ImageConsumer.COMPLETESCANLINES); + } else { + con.setHints(ImageConsumer.RANDOMPIXELORDER); + } + } catch (Exception e) { + if (isConsumer(con)) { + con.imageComplete(ImageConsumer.IMAGEERROR); + } + if (isConsumer(con)) { + removeConsumer(con); + } + } + } + } + } + + /** + * Sets the flag that tells whether this memory image has more than one + * frame (for animation): true for multiple frames, false if this class + * represents a single frame image. + * + * @param animated + * whether this image represents an animation. + */ + public synchronized void setAnimated(boolean animated) { + if (this.animated == animated) { + return; + } + Object consAr[] = consumers.toArray(); + for (Object element : consAr) { + ImageConsumer con = (ImageConsumer)element; + try { + con.imageComplete(ImageConsumer.STATICIMAGEDONE); + } catch (Exception e) { + if (isConsumer(con)) { + con.imageComplete(ImageConsumer.IMAGEERROR); + } + } + if (isConsumer(con)) { + removeConsumer(con); + } + } + this.animated = animated; + } + + /** + * Sends the specified rectangular area of the buffer to ImageConsumers and + * notifies them that an animation frame is completed only if the {@code + * framenotify} parameter is true. That works only if the animated flag has + * been set to true by the setAnimated() method. If the full buffer update + * flag has been set to true by the setFullBufferUpdates() method, then the + * entire buffer will always be sent ignoring parameters. + * + * @param x + * the X coordinate of the rectangular area. + * @param y + * the Y coordinate of the rectangular area. + * @param w + * the width of the rectangular area. + * @param h + * the height of the rectangular area. + * @param framenotify + * true if a SINGLEFRAMEDONE notification should be sent to the + * registered consumers, false otherwise. + */ + public synchronized void newPixels(int x, int y, int w, int h, boolean framenotify) { + if (animated) { + if (fullbuffers) { + x = 0; + y = 0; + w = width; + h = height; + } else { + if (x < 0) { + w += x; + x = 0; + } + if (w > width) { + w = width - x; + } + if (y < 0) { + h += y; + y = 0; + } + } + if (h > height) { + h = height - y; + } + Object consAr[] = consumers.toArray(); + for (Object element : consAr) { + ImageConsumer con = (ImageConsumer)element; + try { + if (w > 0 && h > 0) { + setPixels(con, x, y, w, h); + } + if (framenotify) { + con.imageComplete(ImageConsumer.SINGLEFRAMEDONE); + } + } catch (Exception ex) { + if (isConsumer(con)) { + con.imageComplete(ImageConsumer.IMAGEERROR); + } + if (isConsumer(con)) { + removeConsumer(con); + } + } + } + } + } + + /** + * Sends the specified rectangular area of the buffer to the ImageConsumers + * and notifies them that an animation frame is completed if the animated + * flag has been set to true by the setAnimated() method. If the full buffer + * update flag has been set to true by the setFullBufferUpdates() method, + * then the entire buffer will always be sent ignoring parameters. + * + * @param x + * the X coordinate of the rectangular area. + * @param y + * the Y coordinate of the rectangular area. + * @param w + * the width of the rectangular area. + * @param h + * the height of the rectangular area. + */ + public synchronized void newPixels(int x, int y, int w, int h) { + newPixels(x, y, w, h, true); + } + + /** + * Sends a new buffer of pixels to the ImageConsumers and notifies them that + * an animation frame is completed if the animated flag has been set to true + * by the setAnimated() method. + */ + public void newPixels() { + newPixels(0, 0, width, height, true); + } + + /** + * Inits the. + * + * @param width + * the width. + * @param height + * the height. + * @param model + * the model. + * @param pixels + * the pixels. + * @param off + * the off. + * @param scan + * the scan. + * @param prop + * the prop. + */ + private void init(int width, int height, ColorModel model, byte pixels[], int off, int scan, + Hashtable prop) { + + this.width = width; + this.height = height; + this.cm = model; + this.bData = pixels; + this.offset = off; + this.scanline = scan; + this.properties = prop; + this.dataType = DATA_TYPE_BYTE; + this.consumers = new Vector(); + + } + + /** + * Inits the. + * + * @param width + * the width. + * @param height + * the height. + * @param model + * the model. + * @param pixels + * the pixels. + * @param off + * the off. + * @param scan + * the scan. + * @param prop + * the prop. + */ + private void init(int width, int height, ColorModel model, int pixels[], int off, int scan, + Hashtable prop) { + + this.width = width; + this.height = height; + this.cm = model; + this.iData = pixels; + this.offset = off; + this.scanline = scan; + this.properties = prop; + this.dataType = DATA_TYPE_INT; + this.consumers = new Vector(); + } + + /** + * Sets the pixels. + * + * @param con + * the con. + * @param x + * the x. + * @param y + * the y. + * @param w + * the w. + * @param h + * the h. + */ + private void setPixels(ImageConsumer con, int x, int y, int w, int h) { + int pixelOff = scanline * y + offset + x; + + switch (dataType) { + case DATA_TYPE_BYTE: + con.setPixels(x, y, w, h, cm, bData, pixelOff, scanline); + break; + case DATA_TYPE_INT: + con.setPixels(x, y, w, h, cm, iData, pixelOff, scanline); + break; + default: + // awt.22A=Wrong type of pixels array + throw new IllegalArgumentException(Messages.getString("awt.22A")); //$NON-NLS-1$ + } + } + + /** + * Sets the header. + * + * @param con + * the new header. + */ + private synchronized void setHeader(ImageConsumer con) { + con.setDimensions(width, height); + con.setProperties(properties); + con.setColorModel(cm); + con + .setHints(animated ? (fullbuffers ? (ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES) + : ImageConsumer.RANDOMPIXELORDER) + : (ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES + | ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME)); + } + +} diff --git a/awt/java/awt/image/MultiPixelPackedSampleModel.java b/awt/java/awt/image/MultiPixelPackedSampleModel.java new file mode 100644 index 000000000..3dc13d8f4 --- /dev/null +++ b/awt/java/awt/image/MultiPixelPackedSampleModel.java @@ -0,0 +1,479 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The MultiPixelPackedSampleModel class represents image data with one band. + * This class packs multiple pixels with one sample in one data element and + * supports the following data types: DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT. + * + * @since Android 1.0 + */ +public class MultiPixelPackedSampleModel extends SampleModel { + + /** + * The pixel bit stride. + */ + private int pixelBitStride; + + /** + * The scanline stride. + */ + private int scanlineStride; + + /** + * The data bit offset. + */ + private int dataBitOffset; + + /** + * The bit mask. + */ + private int bitMask; + + /** + * The data element size. + */ + private int dataElementSize; + + /** + * The pixels per data element. + */ + private int pixelsPerDataElement; + + /** + * Instantiates a new MultiPixelPackedSampleModel with the specified + * parameters. + * + * @param dataType + * the data type of the samples. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param numberOfBits + * the number of bits per pixel. + * @param scanlineStride + * the scanline stride of the of the image data. + * @param dataBitOffset + * the array of the band offsets. + */ + public MultiPixelPackedSampleModel(int dataType, int w, int h, int numberOfBits, + int scanlineStride, int dataBitOffset) { + + super(dataType, w, h, 1); + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT + && dataType != DataBuffer.TYPE_INT) { + // awt.61=Unsupported data type: {0} + throw new IllegalArgumentException(Messages.getString("awt.61", //$NON-NLS-1$ + dataType)); + } + + this.scanlineStride = scanlineStride; + if (numberOfBits == 0) { + // awt.20C=Number of Bits equals to zero + throw new RasterFormatException(Messages.getString("awt.20C")); //$NON-NLS-1$ + } + this.pixelBitStride = numberOfBits; + this.dataElementSize = DataBuffer.getDataTypeSize(dataType); + if (dataElementSize % pixelBitStride != 0) { + // awt.20D=The number of bits per pixel is not a power of 2 or + // pixels span data element boundaries + throw new RasterFormatException(Messages.getString("awt.20D")); //$NON-NLS-1$ + } + + if (dataBitOffset % numberOfBits != 0) { + // awt.20E=Data Bit offset is not a multiple of pixel bit stride + throw new RasterFormatException(Messages.getString("awt.20E")); //$NON-NLS-1$ + } + this.dataBitOffset = dataBitOffset; + + this.pixelsPerDataElement = dataElementSize / pixelBitStride; + this.bitMask = (1 << numberOfBits) - 1; + } + + /** + * Instantiates a new MultiPixelPackedSampleModel with the specified + * parameters. + * + * @param dataType + * the data type of the samples. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param numberOfBits + * the number of bits per pixel. + */ + public MultiPixelPackedSampleModel(int dataType, int w, int h, int numberOfBits) { + + this(dataType, w, h, numberOfBits, + (numberOfBits * w + DataBuffer.getDataTypeSize(dataType) - 1) + / DataBuffer.getDataTypeSize(dataType), 0); + } + + @Override + public Object getDataElements(int x, int y, Object obj, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + switch (getTransferType()) { + case DataBuffer.TYPE_BYTE: + byte bdata[]; + if (obj == null) { + bdata = new byte[1]; + } else { + bdata = (byte[])obj; + } + bdata[0] = (byte)getSample(x, y, 0, data); + obj = bdata; + break; + case DataBuffer.TYPE_USHORT: + short sdata[]; + if (obj == null) { + sdata = new short[1]; + } else { + sdata = (short[])obj; + } + sdata[0] = (short)getSample(x, y, 0, data); + obj = sdata; + break; + case DataBuffer.TYPE_INT: + int idata[]; + if (obj == null) { + idata = new int[1]; + } else { + idata = (int[])obj; + } + idata[0] = getSample(x, y, 0, data); + obj = idata; + break; + } + + return obj; + } + + @Override + public void setDataElements(int x, int y, Object obj, DataBuffer data) { + setSample(x, y, obj, data, 1, 0); + } + + /** + * Compares this MultiPixelPackedSampleModel object with the specified + * object. + * + * @param o + * the Object to be compared. + * @return true, if the object is a MultiPixelPackedSampleModel with the + * same data parameter values as this MultiPixelPackedSampleModel, + * false otherwise. + */ + @Override + public boolean equals(Object o) { + if ((o == null) || !(o instanceof MultiPixelPackedSampleModel)) { + return false; + } + + MultiPixelPackedSampleModel model = (MultiPixelPackedSampleModel)o; + return this.width == model.width && this.height == model.height + && this.numBands == model.numBands && this.dataType == model.dataType + && this.pixelBitStride == model.pixelBitStride && this.bitMask == model.bitMask + && this.pixelsPerDataElement == model.pixelsPerDataElement + && this.dataElementSize == model.dataElementSize + && this.dataBitOffset == model.dataBitOffset + && this.scanlineStride == model.scanlineStride; + } + + @Override + public SampleModel createSubsetSampleModel(int bands[]) { + if (bands != null && bands.length != 1) { + // awt.20F=Number of bands must be only 1 + throw new RasterFormatException(Messages.getString("awt.20F")); //$NON-NLS-1$ + } + return createCompatibleSampleModel(width, height); + } + + @Override + public SampleModel createCompatibleSampleModel(int w, int h) { + return new MultiPixelPackedSampleModel(dataType, w, h, pixelBitStride); + } + + @Override + public int[] getPixel(int x, int y, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int pixel[]; + if (iArray == null) { + pixel = new int[numBands]; + } else { + pixel = iArray; + } + + pixel[0] = getSample(x, y, 0, data); + return pixel; + } + + @Override + public void setPixel(int x, int y, int iArray[], DataBuffer data) { + setSample(x, y, iArray, data, 2, 0); + } + + @Override + public int getSample(int x, int y, int b, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height || b != 0) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + int bitnum = dataBitOffset + x * pixelBitStride; + int elem = data.getElem(y * scanlineStride + bitnum / dataElementSize); + int shift = dataElementSize - (bitnum & (dataElementSize - 1)) - pixelBitStride; + + return (elem >> shift) & bitMask; + } + + @Override + public void setSample(int x, int y, int b, int s, DataBuffer data) { + if (b != 0) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + setSample(x, y, null, data, 3, s); + } + + @Override + public DataBuffer createDataBuffer() { + DataBuffer dataBuffer = null; + int size = scanlineStride * height; + + switch (dataType) { + case DataBuffer.TYPE_BYTE: + dataBuffer = new DataBufferByte(size + (dataBitOffset + 7) / 8); + break; + case DataBuffer.TYPE_USHORT: + dataBuffer = new DataBufferUShort(size + (dataBitOffset + 15) / 16); + break; + case DataBuffer.TYPE_INT: + dataBuffer = new DataBufferInt(size + (dataBitOffset + 31) / 32); + break; + } + return dataBuffer; + } + + /** + * Gets the offset of the specified pixel in the data array. + * + * @param x + * the X coordinate of the specified pixel. + * @param y + * the Y coordinate of the specified pixel. + * @return the offset of the specified pixel. + */ + public int getOffset(int x, int y) { + return y * scanlineStride + (x * pixelBitStride + dataBitOffset) / dataElementSize; + } + + @Override + public int getSampleSize(int band) { + return pixelBitStride; + } + + /** + * Gets the bit offset in the data element which is stored for the specified + * pixel of a scanline. + * + * @param x + * the pixel. + * @return the bit offset of the pixel in the data element. + */ + public int getBitOffset(int x) { + return (x * pixelBitStride + dataBitOffset) % dataElementSize; + } + + @Override + public int[] getSampleSize() { + int sampleSizes[] = { + pixelBitStride + }; + return sampleSizes; + } + + /** + * Returns a hash code of this MultiPixelPackedSampleModel class. + * + * @return the hash code of this MultiPixelPackedSampleModel class. + */ + @Override + public int hashCode() { + int hash = 0; + int tmp = 0; + + hash = width; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= height; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= numBands; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= dataType; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= scanlineStride; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= pixelBitStride; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= dataBitOffset; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= bitMask; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= dataElementSize; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= pixelsPerDataElement; + return hash; + } + + @Override + public int getTransferType() { + if (pixelBitStride > 16) { + return DataBuffer.TYPE_INT; + } else if (pixelBitStride > 8) { + return DataBuffer.TYPE_USHORT; + } else { + return DataBuffer.TYPE_BYTE; + } + } + + /** + * Gets the scanline stride of this MultiPixelPackedSampleModel. + * + * @return the scanline stride of this MultiPixelPackedSampleModel. + */ + public int getScanlineStride() { + return scanlineStride; + } + + /** + * Gets the pixel bit stride of this MultiPixelPackedSampleModel. + * + * @return the pixel bit stride of this MultiPixelPackedSampleModel. + */ + public int getPixelBitStride() { + return pixelBitStride; + } + + @Override + public int getNumDataElements() { + return 1; + } + + /** + * Gets the data bit offset. + * + * @return the data bit offset. + */ + public int getDataBitOffset() { + return dataBitOffset; + } + + /** + * This method is used by other methods of this class. The behavior of this + * method depends on the method which has been invoke this one. The argument + * methodId is used to choose valid behavior in a particular case. If + * methodId is equal to 1 it means that this method has been invoked by the + * setDataElements() method, 2 - means setPixel(), and setSample() in any + * other cases. + * + * @param x + * the x. + * @param y + * the y. + * @param obj + * the obj. + * @param data + * the data. + * @param methodId + * the method id. + * @param s + * the s. + */ + private void setSample(final int x, final int y, final Object obj, final DataBuffer data, + final int methodId, int s) { + if ((x < 0) || (y < 0) || (x >= this.width) || (y >= this.height)) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + final int bitnum = dataBitOffset + x * pixelBitStride; + final int idx = y * scanlineStride + bitnum / dataElementSize; + final int shift = dataElementSize - (bitnum & (dataElementSize - 1)) - pixelBitStride; + final int mask = ~(bitMask << shift); + int elem = data.getElem(idx); + + switch (methodId) { + case 1: { // Invoked from setDataElements() + switch (getTransferType()) { + case DataBuffer.TYPE_BYTE: + s = ((byte[])obj)[0] & 0xff; + break; + case DataBuffer.TYPE_USHORT: + s = ((short[])obj)[0] & 0xffff; + break; + case DataBuffer.TYPE_INT: + s = ((int[])obj)[0]; + break; + } + break; + } + case 2: { // Invoked from setPixel() + s = ((int[])obj)[0]; + break; + } + } + + elem &= mask; + elem |= (s & bitMask) << shift; + data.setElem(idx, elem); + } +} diff --git a/awt/java/awt/image/PackedColorModel.java b/awt/java/awt/image/PackedColorModel.java new file mode 100644 index 000000000..4d1c2e500 --- /dev/null +++ b/awt/java/awt/image/PackedColorModel.java @@ -0,0 +1,402 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.util.Arrays; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The class PackedColorModel represents a color model where the components are + * just the red, green, and blue bands, plus an alpha band if alpha is + * supported. + * + * @since Android 1.0 + */ +public abstract class PackedColorModel extends ColorModel { + + /** + * The component masks. + */ + int componentMasks[]; + + /** + * The offsets. + */ + int offsets[]; + + /** + * The scales. + */ + float scales[]; + + /** + * Instantiates a new packed color model. + * + * @param space + * the color space. + * @param bits + * the array of component masks. + * @param colorMaskArray + * the array that gives the bitmask corresponding to each color + * band (red, green, and blue). + * @param alphaMask + * the bitmask corresponding to the alpha band. + * @param isAlphaPremultiplied + * whether the alpha is pre-multiplied in this color model. + * @param trans + * the transparency strategy, @see java.awt.Transparency. + * @param transferType + * the transfer type (primitive java type to use for the + * components). + * @throws IllegalArgumentException + * if the number of bits in the combined bitmasks for the color + * bands is less than one or greater than 32. + */ + public PackedColorModel(ColorSpace space, int bits, int colorMaskArray[], int alphaMask, + boolean isAlphaPremultiplied, int trans, int transferType) { + + super(bits, createBits(colorMaskArray, alphaMask), space, (alphaMask == 0 ? false : true), + isAlphaPremultiplied, trans, validateTransferType(transferType)); + + if (pixel_bits < 1 || pixel_bits > 32) { + // awt.236=The bits is less than 1 or greater than 32 + throw new IllegalArgumentException(Messages.getString("awt.236")); //$NON-NLS-1$ + } + + componentMasks = new int[numComponents]; + for (int i = 0; i < numColorComponents; i++) { + componentMasks[i] = colorMaskArray[i]; + } + + if (hasAlpha) { + componentMasks[numColorComponents] = alphaMask; + if (this.bits[numColorComponents] == 1) { + transparency = Transparency.BITMASK; + } + } + + parseComponents(); + } + + /** + * Instantiates a new packed color model. + * + * @param space + * the color space. + * @param bits + * the array of component masks. + * @param rmask + * the bitmask corresponding to the red band. + * @param gmask + * the bitmask corresponding to the green band. + * @param bmask + * the bitmask corresponding to the blue band. + * @param amask + * the bitmask corresponding to the alpha band. + * @param isAlphaPremultiplied + * whether the alpha is pre-multiplied in this color model. + * @param trans + * the transparency strategy, @see java.awt.Transparency. + * @param transferType + * the transfer type (primitive java type to use for the + * components). + * @throws IllegalArgumentException + * if the number of bits in the combined bitmasks for the color + * bands is less than one or greater than 32. + */ + public PackedColorModel(ColorSpace space, int bits, int rmask, int gmask, int bmask, int amask, + boolean isAlphaPremultiplied, int trans, int transferType) { + + super(bits, createBits(rmask, gmask, bmask, amask), space, (amask == 0 ? false : true), + isAlphaPremultiplied, trans, validateTransferType(transferType)); + + if (pixel_bits < 1 || pixel_bits > 32) { + // awt.236=The bits is less than 1 or greater than 32 + throw new IllegalArgumentException(Messages.getString("awt.236")); //$NON-NLS-1$ + } + + if (cs.getType() != ColorSpace.TYPE_RGB) { + // awt.239=The space is not a TYPE_RGB space + throw new IllegalArgumentException(Messages.getString("awt.239")); //$NON-NLS-1$ + } + + for (int i = 0; i < numColorComponents; i++) { + if (cs.getMinValue(i) != 0.0f || cs.getMaxValue(i) != 1.0f) { + // awt.23A=The min/max normalized component values are not + // 0.0/1.0 + throw new IllegalArgumentException(Messages.getString("awt.23A")); //$NON-NLS-1$ + } + } + componentMasks = new int[numComponents]; + componentMasks[0] = rmask; + componentMasks[1] = gmask; + componentMasks[2] = bmask; + + if (hasAlpha) { + componentMasks[3] = amask; + if (this.bits[3] == 1) { + transparency = Transparency.BITMASK; + } + } + + parseComponents(); + } + + @Override + public WritableRaster getAlphaRaster(WritableRaster raster) { + if (!hasAlpha) { + return null; + } + + int x = raster.getMinX(); + int y = raster.getMinY(); + int w = raster.getWidth(); + int h = raster.getHeight(); + int band[] = new int[1]; + band[0] = raster.getNumBands() - 1; + return raster.createWritableChild(x, y, w, h, x, y, band); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof PackedColorModel)) { + return false; + } + PackedColorModel cm = (PackedColorModel)obj; + + return (pixel_bits == cm.getPixelSize() && transferType == cm.getTransferType() + && cs.getType() == cm.getColorSpace().getType() && hasAlpha == cm.hasAlpha() + && isAlphaPremultiplied == cm.isAlphaPremultiplied() + && transparency == cm.getTransparency() + && numColorComponents == cm.getNumColorComponents() + && numComponents == cm.getNumComponents() + && Arrays.equals(bits, cm.getComponentSize()) && Arrays.equals(componentMasks, cm + .getMasks())); + } + + @Override + public boolean isCompatibleSampleModel(SampleModel sm) { + if (sm == null) { + return false; + } + if (!(sm instanceof SinglePixelPackedSampleModel)) { + return false; + } + SinglePixelPackedSampleModel esm = (SinglePixelPackedSampleModel)sm; + + return ((esm.getNumBands() == numComponents) && (esm.getTransferType() == transferType) && Arrays + .equals(esm.getBitMasks(), componentMasks)); + } + + @Override + public SampleModel createCompatibleSampleModel(int w, int h) { + return new SinglePixelPackedSampleModel(transferType, w, h, componentMasks); + } + + /** + * Gets the bitmask corresponding to the specified color component. + * + * @param index + * the index of the desired color. + * @return the mask. + */ + public final int getMask(int index) { + return componentMasks[index]; + } + + /** + * Gets the bitmasks of the components. + * + * @return the masks. + */ + public final int[] getMasks() { + return (componentMasks.clone()); + } + + /** + * Creates the bits. + * + * @param colorMaskArray + * the color mask array. + * @param alphaMask + * the alpha mask. + * @return the int[]. + */ + private static int[] createBits(int colorMaskArray[], int alphaMask) { + int bits[]; + int numComp; + if (alphaMask == 0) { + numComp = colorMaskArray.length; + } else { + numComp = colorMaskArray.length + 1; + } + + bits = new int[numComp]; + int i = 0; + for (; i < colorMaskArray.length; i++) { + bits[i] = countCompBits(colorMaskArray[i]); + if (bits[i] < 0) { + // awt.23B=The mask of the {0} component is not contiguous + throw new IllegalArgumentException(Messages.getString("awt.23B", i)); //$NON-NLS-1$ + } + } + + if (i < numComp) { + bits[i] = countCompBits(alphaMask); + + if (bits[i] < 0) { + // awt.23C=The mask of the alpha component is not contiguous + throw new IllegalArgumentException(Messages.getString("awt.23C")); //$NON-NLS-1$ + } + } + + return bits; + } + + /** + * Creates the bits. + * + * @param rmask + * the rmask. + * @param gmask + * the gmask. + * @param bmask + * the bmask. + * @param amask + * the amask. + * @return the int[]. + */ + private static int[] createBits(int rmask, int gmask, int bmask, int amask) { + + int numComp; + if (amask == 0) { + numComp = 3; + } else { + numComp = 4; + } + int bits[] = new int[numComp]; + + bits[0] = countCompBits(rmask); + if (bits[0] < 0) { + // awt.23D=The mask of the red component is not contiguous + throw new IllegalArgumentException(Messages.getString("awt.23D")); //$NON-NLS-1$ + } + + bits[1] = countCompBits(gmask); + if (bits[1] < 0) { + // awt.23E=The mask of the green component is not contiguous + throw new IllegalArgumentException(Messages.getString("awt.23E")); //$NON-NLS-1$ + } + + bits[2] = countCompBits(bmask); + if (bits[2] < 0) { + // awt.23F=The mask of the blue component is not contiguous + throw new IllegalArgumentException(Messages.getString("awt.23F")); //$NON-NLS-1$ + } + + if (amask != 0) { + bits[3] = countCompBits(amask); + if (bits[3] < 0) { + // awt.23C=The mask of the alpha component is not contiguous + throw new IllegalArgumentException(Messages.getString("awt.23C")); //$NON-NLS-1$ + } + } + + return bits; + } + + /** + * Count comp bits. + * + * @param compMask + * the comp mask. + * @return the int. + */ + private static int countCompBits(int compMask) { + int bits = 0; + if (compMask != 0) { + // Deleting final zeros + while ((compMask & 1) == 0) { + compMask >>>= 1; + } + // Counting component bits + while ((compMask & 1) == 1) { + compMask >>>= 1; + bits++; + } + } + + if (compMask != 0) { + return -1; + } + + return bits; + } + + /** + * Validate transfer type. + * + * @param transferType + * the transfer type. + * @return the int. + */ + private static int validateTransferType(int transferType) { + if (transferType != DataBuffer.TYPE_BYTE && transferType != DataBuffer.TYPE_USHORT + && transferType != DataBuffer.TYPE_INT) { + // awt.240=The transferType not is one of DataBuffer.TYPE_BYTE, + // DataBuffer.TYPE_USHORT or DataBuffer.TYPE_INT + throw new IllegalArgumentException(Messages.getString("awt.240")); //$NON-NLS-1$ + } + return transferType; + } + + /** + * Parses the components. + */ + private void parseComponents() { + offsets = new int[numComponents]; + scales = new float[numComponents]; + for (int i = 0; i < numComponents; i++) { + int off = 0; + int mask = componentMasks[i]; + while ((mask & 1) == 0) { + mask >>>= 1; + off++; + } + offsets[i] = off; + if (bits[i] == 0) { + scales[i] = 256.0f; // May be any value different from zero, + // because will dividing by zero + } else { + scales[i] = 255.0f / maxValues[i]; + } + } + + } + +} diff --git a/awt/java/awt/image/PixelGrabber.java b/awt/java/awt/image/PixelGrabber.java new file mode 100644 index 000000000..cecd5c853 --- /dev/null +++ b/awt/java/awt/image/PixelGrabber.java @@ -0,0 +1,408 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ +package java.awt.image; + +import java.awt.Image; +import java.util.Hashtable; + +import org.apache.harmony.awt.internal.nls.Messages; + +public class PixelGrabber implements ImageConsumer { + + int width; + int height; + int X; + int Y; + int offset; + int scanline; + ImageProducer producer; + + byte bData[]; + int iData[]; + ColorModel cm; + + private int grabberStatus; + private int dataType; + private boolean isGrabbing; + private boolean isRGB; + + + private static final int DATA_TYPE_BYTE = 0; + private static final int DATA_TYPE_INT = 1; + private static final int DATA_TYPE_UNDEFINED = 2; + + private static final int ALL_BITS = (ImageObserver.FRAMEBITS | + ImageObserver.ALLBITS); + + private static final int GRABBING_STOP = ALL_BITS | ImageObserver.ERROR; + + + + public PixelGrabber(ImageProducer ip, int x, int y, int w, int h, int[] pix, + int off, int scansize) { + initialize(ip, x, y, w, h, pix, off, scansize, true); + } + + public PixelGrabber(Image img, int x, int y, int w, int h, int[] pix, + int off, int scansize) { + initialize(img.getSource(), x, y, w, h, pix, off, scansize, true); + } + + public PixelGrabber(Image img, int x, int y, int w, int h, boolean forceRGB) { + initialize(img.getSource(), x, y, w, h, null, 0, 0, forceRGB); + } + + public void setProperties(Hashtable props) { + return; + } + + public synchronized Object getPixels() { + switch(dataType){ + case DATA_TYPE_BYTE: + return bData; + case DATA_TYPE_INT: + return iData; + default: + return null; + } + } + + public void setColorModel(ColorModel model) { + return; + } + + public void setPixels(int srcX, int srcY, int srcW, int srcH, + ColorModel model, byte[] pixels, int srcOff, int srcScan) { + if(srcY < Y){ + int delta = Y - srcY; + if(delta >= height) { + return; + } + srcY += delta; + srcH -= delta; + srcOff += srcScan * delta; + } + + if(srcY + srcH > Y + height){ + srcH = Y + height - srcY; + if(srcH <= 0) { + return; + } + } + + if(srcX < X){ + int delta = X - srcX; + if(delta >= width) { + return; + } + srcW -= delta; + srcX += delta; + srcOff += delta; + } + + if(srcX + srcW > X + width){ + srcW = X + width - srcX; + if(srcW <= 0) { + return; + } + } + if(scanline == 0) { + scanline = width; + } + int realOff = offset + (srcY - Y) * scanline + (srcX - X); + switch(dataType){ + case DATA_TYPE_UNDEFINED: + cm = model; + if(model != ColorModel.getRGBdefault()){ + bData = new byte[width * height]; + isRGB = false; + dataType = DATA_TYPE_BYTE; + }else{ + iData = new int[width * height]; + isRGB = true; + dataType = DATA_TYPE_INT; + } + case DATA_TYPE_BYTE: + if(!isRGB && cm == model){ + for(int y = 0; y < srcH; y++){ + System.arraycopy(pixels, srcOff, bData, realOff, srcW); + srcOff += srcScan; + realOff += scanline; + } + break; + } + forceToRGB(); + case DATA_TYPE_INT: + for(int y = 0; y < srcH; y++){ + for(int x = 0; x < srcW; x++){ + iData[realOff + x] = cm.getRGB(pixels[srcOff + x] & 0xff); + } + srcOff += srcScan; + realOff += scanline; + } + } + + return; + } + + public void setPixels(int srcX, int srcY, int srcW, int srcH, + ColorModel model, int[] pixels, int srcOff, int srcScan) { + + if(srcY < Y){ + int delta = Y - srcY; + if(delta >= height) { + return; + } + srcY += delta; + srcH -= delta; + srcOff += srcScan * delta; + } + + if(srcY + srcH > Y + height){ + srcH = Y + height - srcY; + if(srcH <= 0) { + return; + } + } + + if(srcX < X){ + int delta = X - srcX; + if(delta >= width) { + return; + } + srcW -= delta; + srcX += delta; + srcOff += delta; + } + + if(srcX + srcW > X + width){ + srcW = X + width - srcX; + if(srcW <= 0) { + return; + } + } + if(scanline == 0) { + scanline = width; + } + int realOff = offset + (srcY - Y) * scanline + (srcX - X); + + int mask = 0xFF; + + switch(dataType){ + case DATA_TYPE_UNDEFINED: + cm = model; + iData = new int[width * height]; + dataType = DATA_TYPE_INT; + isRGB = (cm == ColorModel.getRGBdefault()); + + case DATA_TYPE_INT: + if(cm == model){ + for(int y = 0; y < srcH; y++){ + System.arraycopy(pixels, srcOff, iData, realOff, srcW); + srcOff += srcScan; + realOff += scanline; + } + break; + } + mask = 0xFFFFFFFF; + + case DATA_TYPE_BYTE: + forceToRGB(); + for(int y = 0; y < srcH; y++){ + for(int x = 0; x < srcW; x++){ + iData[realOff+x] = cm.getRGB(pixels[srcOff+x] & mask); + } + srcOff += srcScan; + realOff += scanline; + } + } + } + + public synchronized ColorModel getColorModel() { + return cm; + } + + public synchronized boolean grabPixels(long ms) + throws InterruptedException { + if((grabberStatus & GRABBING_STOP) != 0){ + return ((grabberStatus & ALL_BITS) != 0); + } + + long start = System.currentTimeMillis(); + + if(!isGrabbing){ + isGrabbing = true; + grabberStatus &= ~ImageObserver.ABORT; + producer.startProduction(this); + } + while((grabberStatus & GRABBING_STOP) == 0){ + if(ms != 0){ + ms = start + ms - System.currentTimeMillis(); + if(ms <= 0) { + break; + } + } + wait(ms); + } + + return ((grabberStatus & ALL_BITS) != 0); + } + + public void setDimensions(int w, int h) { + if(width < 0) { + width = w - X; + } + if(height < 0) { + height = h - Y; + } + + grabberStatus |= ImageObserver.WIDTH | ImageObserver.HEIGHT; + + if(width <=0 || height <=0){ + imageComplete(STATICIMAGEDONE); + return; + } + + if(isRGB && dataType == DATA_TYPE_UNDEFINED){ + iData = new int[width * height]; + dataType = DATA_TYPE_INT; + scanline = width; + } + } + + public void setHints(int hints) { + return; + } + + public synchronized void imageComplete(int status) { + switch(status){ + case IMAGEABORTED: + grabberStatus |= ImageObserver.ABORT; + break; + case IMAGEERROR: + grabberStatus |= ImageObserver.ERROR | ImageObserver.ABORT; + break; + case SINGLEFRAMEDONE: + grabberStatus |= ImageObserver.FRAMEBITS; + break; + case STATICIMAGEDONE: + grabberStatus |= ImageObserver.ALLBITS; + break; + default: + // awt.26A=Incorrect ImageConsumer completion status + throw new IllegalArgumentException(Messages.getString("awt.26A")); //$NON-NLS-1$ + } + isGrabbing = false; + producer.removeConsumer(this); + notifyAll(); + } + + public boolean grabPixels() throws InterruptedException { + return grabPixels(0); + } + + public synchronized void startGrabbing() { + if((grabberStatus & GRABBING_STOP) != 0){ + return; + } + if(!isGrabbing){ + isGrabbing = true; + grabberStatus &= ~ImageObserver.ABORT; + producer.startProduction(this); + } + } + + public synchronized void abortGrabbing() { + imageComplete(IMAGEABORTED); + } + + public synchronized int status() { + return grabberStatus; + } + + public synchronized int getWidth() { + if(width < 0) { + return -1; + } + return width; + } + + public synchronized int getStatus() { + return grabberStatus; + } + + public synchronized int getHeight() { + if(height < 0) { + return -1; + } + return height; + } + + private void initialize(ImageProducer ip, int x, int y, int w, int h, + int pixels[], int off, int scansize, boolean forceRGB){ + + producer = ip; + X = x; + Y = y; + width = w; + height = h; + iData = pixels; + dataType = (pixels == null) ? DATA_TYPE_UNDEFINED : DATA_TYPE_INT; + offset = off; + scanline = scansize; + if(forceRGB){ + cm = ColorModel.getRGBdefault(); + isRGB = true; + } + } + + /** + * Force pixels to INT RGB mode + */ + private void forceToRGB(){ + if (isRGB) + return; + + switch(dataType){ + case DATA_TYPE_BYTE: + iData = new int[width * height]; + for(int i = 0; i < iData.length; i++){ + iData[i] = cm.getRGB(bData[i] & 0xff); + } + dataType = DATA_TYPE_INT; + bData = null; + break; + + case DATA_TYPE_INT: + int buff[] = new int[width * height]; + for(int i = 0; i < iData.length; i++){ + buff[i] = cm.getRGB(iData[i]); + } + iData = buff; + break; + } + offset = 0; + scanline = width; + cm = ColorModel.getRGBdefault(); + isRGB = true; + } + +} diff --git a/awt/java/awt/image/PixelInterleavedSampleModel.java b/awt/java/awt/image/PixelInterleavedSampleModel.java new file mode 100644 index 000000000..8e646f80b --- /dev/null +++ b/awt/java/awt/image/PixelInterleavedSampleModel.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The PixelInterleavedSampleModel class represents image data as represented as + * interleaved pixels and for which each sample of a pixel takes one data + * element of the DataBuffer. + * + * @since Android 1.0 + */ +public class PixelInterleavedSampleModel extends ComponentSampleModel { + + /** + * Instantiates a new PixelInterleavedSampleModel with the specified + * parameters. + * + * @param dataType + * the data type of the samples. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param pixelStride + * the pixel stride of the image data. + * @param scanlineStride + * the scanline stride of the of the image data. + * @param bandOffsets + * the array of the band offsets. + */ + public PixelInterleavedSampleModel(int dataType, int w, int h, int pixelStride, + int scanlineStride, int bandOffsets[]) { + + super(dataType, w, h, pixelStride, scanlineStride, bandOffsets); + + int maxOffset = bandOffsets[0]; + int minOffset = bandOffsets[0]; + for (int i = 1; i < bandOffsets.length; i++) { + if (bandOffsets[i] > maxOffset) { + maxOffset = bandOffsets[i]; + } + if (bandOffsets[i] < minOffset) { + minOffset = bandOffsets[i]; + } + } + + maxOffset -= minOffset; + + if (maxOffset > scanlineStride) { + // awt.241=Any offset between bands is greater than the Scanline + // stride + throw new IllegalArgumentException(Messages.getString("awt.241")); //$NON-NLS-1$ + } + + if (maxOffset > pixelStride) { + // awt.242=Pixel stride is less than any offset between bands + throw new IllegalArgumentException(Messages.getString("awt.242")); //$NON-NLS-1$ + } + + if (pixelStride * w > scanlineStride) { + // awt.243=Product of Pixel stride and w is greater than Scanline + // stride + throw new IllegalArgumentException(Messages.getString("awt.243")); //$NON-NLS-1$ + } + + } + + @Override + public SampleModel createSubsetSampleModel(int bands[]) { + int newOffsets[] = new int[bands.length]; + for (int i = 0; i < bands.length; i++) { + newOffsets[i] = bandOffsets[bands[i]]; + } + + return new PixelInterleavedSampleModel(dataType, width, height, pixelStride, + scanlineStride, newOffsets); + } + + @Override + public SampleModel createCompatibleSampleModel(int w, int h) { + int newOffsets[]; + int minOffset = bandOffsets[0]; + + for (int i = 1; i < numBands; i++) { + if (bandOffsets[i] < minOffset) { + minOffset = bandOffsets[i]; + } + } + + if (minOffset > 0) { + newOffsets = new int[numBands]; + for (int i = 0; i < numBands; i++) { + newOffsets[i] = bandOffsets[i] - minOffset; + } + } else { + newOffsets = bandOffsets; + } + + return new PixelInterleavedSampleModel(dataType, w, h, pixelStride, pixelStride * w, + newOffsets); + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + int tmp = hash >>> 8; + hash <<= 8; + hash |= tmp; + + return hash ^ 0x66; + } + +} diff --git a/awt/java/awt/image/RGBImageFilter.java b/awt/java/awt/image/RGBImageFilter.java new file mode 100644 index 000000000..f5fe5d9ff --- /dev/null +++ b/awt/java/awt/image/RGBImageFilter.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +/** + * The RGBImageFilter class represents a filter which modifies pixels of an + * image in the default RGB ColorModel. + * + * @since Android 1.0 + */ +public abstract class RGBImageFilter extends ImageFilter { + + /** + * The original model is the ColorModel to be replaced by the new model when + * substituteColorModel is called. + */ + protected ColorModel origmodel; + + /** + * The new model is the ColorModel with which to replace the original model + * when substituteColorModel is called. + */ + protected ColorModel newmodel; + + /** + * The canFilterIndexColorModel indicates if it is acceptable to apply the + * color filtering of the filterRGB method to the color table entries of an + * IndexColorModel object. + */ + protected boolean canFilterIndexColorModel; + + /** + * Instantiates a new RGBImageFilter. + */ + public RGBImageFilter() { + } + + /** + * Filters an IndexColorModel object by calling filterRGB function for each + * entry of IndexColorModel. + * + * @param icm + * the IndexColorModel to be filtered. + * @return the IndexColorModel. + */ + public IndexColorModel filterIndexColorModel(IndexColorModel icm) { + int transferType = icm.getTransferType(); + int bits = icm.getPixelSize(); + int mapSize = icm.getMapSize(); + int colorMap[] = new int[mapSize]; + int filteredColorMap[] = new int[mapSize]; + icm.getRGBs(colorMap); + int trans = -1; + boolean hasAlpha = false; + for (int i = 0; i < mapSize; i++) { + filteredColorMap[i] = filterRGB(-1, -1, colorMap[i]); + int alpha = filteredColorMap[i] >>> 24; + if (alpha != 0xff) { + if (!hasAlpha) { + hasAlpha = true; + } + if (alpha == 0 && trans < 0) { + trans = i; + } + } + } + + return new IndexColorModel(bits, mapSize, filteredColorMap, 0, hasAlpha, trans, + transferType); + } + + /** + * Replaces the original color model and the new one. + * + * @param oldcm + * the old ColorModel. + * @param newcm + * the new ColorModel. + */ + public void substituteColorModel(ColorModel oldcm, ColorModel newcm) { + origmodel = oldcm; + newmodel = newcm; + } + + @Override + public void setColorModel(ColorModel model) { + if (model instanceof IndexColorModel && canFilterIndexColorModel) { + IndexColorModel icm = (IndexColorModel)model; + ColorModel filteredModel = filterIndexColorModel(icm); + substituteColorModel(model, filteredModel); + consumer.setColorModel(filteredModel); + } else { + consumer.setColorModel(ColorModel.getRGBdefault()); + } + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, + int scansize) { + + if (model == null || model == origmodel) { + consumer.setPixels(x, y, w, h, newmodel, pixels, off, scansize); + } else { + int rgbPixels[] = new int[w]; + for (int sy = y, pixelsOff = off; sy < y + h; sy++, pixelsOff += scansize) { + + for (int sx = x, idx = 0; sx < x + w; sx++, idx++) { + rgbPixels[idx] = model.getRGB(pixels[pixelsOff + idx]); + } + filterRGBPixels(x, sy, w, 1, rgbPixels, 0, w); + } + } + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, + int scansize) { + + if (model == null || model == origmodel) { + consumer.setPixels(x, y, w, h, newmodel, pixels, off, scansize); + } else { + int rgbPixels[] = new int[w]; + for (int sy = y, pixelsOff = off; sy < y + h; sy++, pixelsOff += scansize) { + + for (int sx = x, idx = 0; sx < x + w; sx++, idx++) { + rgbPixels[idx] = model.getRGB(pixels[pixelsOff + idx] & 0xff); + } + filterRGBPixels(x, sy, w, 1, rgbPixels, 0, w); + } + } + } + + /** + * Filters a region of pixels in the default RGB ColorModel by calling the + * filterRGB method for them. + * + * @param x + * the X coordinate of region. + * @param y + * the Y coordinate of region. + * @param w + * the width of region. + * @param h + * the height of region. + * @param pixels + * the pixels array. + * @param off + * the offset of array. + * @param scansize + * the distance between rows of pixels in the array. + */ + public void filterRGBPixels(int x, int y, int w, int h, int[] pixels, int off, int scansize) { + + for (int sy = y, lineOff = off; sy < y + h; sy++, lineOff += scansize) { + for (int sx = x, idx = 0; sx < x + w; sx++, idx++) { + pixels[lineOff + idx] = filterRGB(sx, sy, pixels[lineOff + idx]); + } + } + consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), pixels, off, scansize); + } + + /** + * Converts a single input pixel in the default RGB ColorModel to a single + * output pixel. + * + * @param x + * the X pixel's coordinate. + * @param y + * the Y pixel's coordinate. + * @param rgb + * a pixel in the default RGB color model. + * @return a filtered pixel in the default RGB color model. + */ + public abstract int filterRGB(int x, int y, int rgb); + +} diff --git a/awt/java/awt/image/Raster.java b/awt/java/awt/image/Raster.java new file mode 100644 index 000000000..6749fde9e --- /dev/null +++ b/awt/java/awt/image/Raster.java @@ -0,0 +1,1515 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.Point; +import java.awt.Rectangle; + +import org.apache.harmony.awt.gl.image.OrdinaryWritableRaster; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Raster class represents a rectangular area of pixels. This class is + * defined by DataBuffer and SampleModel objects. The DataBuffer object stores + * sample values and DSampleModel defines the location of sample in this + * DataBuffer. + * + * @since Android 1.0 + */ +public class Raster { + + /** + * The DataBuffer of this Raster. + */ + protected DataBuffer dataBuffer; + + /** + * The height of this Raster. + */ + protected int height; + + /** + * The X coordinate of the upper left pixel in this Raster. + */ + protected int minX; + + /** + * The Y coordinate of the upper left pixel in this Raster. + */ + protected int minY; + + /** + * The number of bands in this Raster. + */ + protected int numBands; + + /** + * The number of data elements. + */ + protected int numDataElements; + + /** + * The parent of this Raster. + */ + protected Raster parent; + + /** + * The SampleModel of this Raster. + */ + protected SampleModel sampleModel; + + /** + * The X translation from the coordinate space of the SampleModel of this + * Raster. + */ + protected int sampleModelTranslateX; + + /** + * The Y translation from the coordinate space of the SampleModel of this + * Raster. + */ + protected int sampleModelTranslateY; + + /** + * The width of this Raster. + */ + protected int width; + + /** + * Creates a Raster object with a BandedSampleModel and the specified + * DataBuffer. The number of bands is defined by the length of bandOffsets + * or bankIndices arrays. + * + * @param dataBuffer + * the specified DataBuffer. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param scanlineStride + * the scanline stride of the image data. + * @param bankIndices + * the bank indices of bands. + * @param bandOffsets + * the band offsets of bands. + * @param location + * the location which defines the upper left corner of Raster. + * @return the WritableRaster object. + */ + public static WritableRaster createBandedRaster(DataBuffer dataBuffer, int w, int h, + int scanlineStride, int bankIndices[], int bandOffsets[], Point location) { + + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + if ((long)location.x + w > Integer.MAX_VALUE || (long)location.y + h > Integer.MAX_VALUE) { + // awt.276=location.x + w or location.y + h results in integer + // overflow + throw new RasterFormatException(Messages.getString("awt.276")); //$NON-NLS-1$ + } + + if (bankIndices == null || bandOffsets == null) { + // awt.277=bankIndices or bandOffsets is null + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.277")); //$NON-NLS-1$ + } + + if (dataBuffer == null) { + // awt.278=dataBuffer is null + throw new NullPointerException(Messages.getString("awt.278")); //$NON-NLS-1$ + } + + int dataType = dataBuffer.getDataType(); + + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT + && dataType != DataBuffer.TYPE_INT) { + // awt.230=dataType is not one of the supported data types + throw new IllegalArgumentException(Messages.getString("awt.230")); //$NON-NLS-1$ + } + + BandedSampleModel sampleModel = new BandedSampleModel(dataType, w, h, scanlineStride, + bankIndices, bandOffsets); + + return new OrdinaryWritableRaster(sampleModel, dataBuffer, location); + } + + /** + * Creates a Raster object with a BandedSampleModel and the specified data + * type. The Data type can be one of the following values: TYPE_BYTE, + * TYPE_USHORT, or TYPE_INT. + * + * @param dataType + * the data type of the samples: TYPE_BYTE, TYPE_USHORT, or + * TYPE_INT. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param scanlineStride + * the scanline stride of the image data. + * @param bankIndices + * the bank indices of bands. + * @param bandOffsets + * the band offsets of bands. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster object. + */ + public static WritableRaster createBandedRaster(int dataType, int w, int h, int scanlineStride, + int bankIndices[], int bandOffsets[], Point location) { + + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + if ((long)location.x + w > Integer.MAX_VALUE || (long)location.y + h > Integer.MAX_VALUE) { + // awt.276=location.x + w or location.y + h results in integer + // overflow + throw new RasterFormatException(Messages.getString("awt.276")); //$NON-NLS-1$ + } + + if (bankIndices == null || bandOffsets == null) { + // awt.277=bankIndices or bandOffsets is null + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.277")); //$NON-NLS-1$ + } + + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT + && dataType != DataBuffer.TYPE_INT) { + // awt.230=dataType is not one of the supported data types + throw new IllegalArgumentException(Messages.getString("awt.230")); //$NON-NLS-1$ + } + + int maxOffset = bandOffsets[0]; + int maxBank = bankIndices[0]; + + for (int i = 0; i < bankIndices.length; i++) { + if (bandOffsets[i] > maxOffset) { + maxOffset = bandOffsets[i]; + } + if (bankIndices[i] > maxBank) { + maxBank = bankIndices[i]; + } + } + + int numBanks = maxBank + 1; + int dataSize = scanlineStride * (h - 1) + w + maxOffset; + + DataBuffer data = null; + + switch (dataType) { + case DataBuffer.TYPE_BYTE: + data = new DataBufferByte(dataSize, numBanks); + break; + case DataBuffer.TYPE_USHORT: + data = new DataBufferUShort(dataSize, numBanks); + break; + case DataBuffer.TYPE_INT: + data = new DataBufferInt(dataSize, numBanks); + break; + } + return createBandedRaster(data, w, h, scanlineStride, bankIndices, bandOffsets, location); + } + + /** + * Creates a Raster object with a BandedSampleModel and the specified data + * type. The Data type can be one of the following values: TYPE_BYTE, + * TYPE_USHORT, or TYPE_INT. + * + * @param dataType + * the data type of the samples: TYPE_BYTE, TYPE_USHORT, or + * TYPE_INT. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param bands + * the number of bands. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster object. + */ + public static WritableRaster createBandedRaster(int dataType, int w, int h, int bands, + Point location) { + + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + if ((long)location.x + w > Integer.MAX_VALUE || (long)location.y + h > Integer.MAX_VALUE) { + // awt.276=location.x + w or location.y + h results in integer + // overflow + throw new RasterFormatException(Messages.getString("awt.276")); //$NON-NLS-1$ + } + + if (bands < 1) { + // awt.279=bands is less than 1 + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.279")); //$NON-NLS-1$ + } + + int bandOffsets[] = new int[bands]; + int bankIndices[] = new int[bands]; + + for (int i = 0; i < bands; i++) { + bandOffsets[i] = 0; + bankIndices[i] = i; + } + return createBandedRaster(dataType, w, h, w, bankIndices, bandOffsets, location); + } + + /** + * Creates a Raster object with a PixelInterleavedSampleModel and the + * specified DataBuffer. + * + * @param dataBuffer + * the DataBuffer. + * @param w + * the width of image data. + * @param h + * the height of image data. + * @param scanlineStride + * the scanline stride of the image data. + * @param pixelStride + * the pixel stride of image data. + * @param bandOffsets + * the band offsets of bands. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster object. + */ + public static WritableRaster createInterleavedRaster(DataBuffer dataBuffer, int w, int h, + int scanlineStride, int pixelStride, int bandOffsets[], Point location) { + + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + if ((long)location.x + w > Integer.MAX_VALUE || (long)location.y + h > Integer.MAX_VALUE) { + // awt.276=location.x + w or location.y + h results in integer + // overflow + throw new RasterFormatException(Messages.getString("awt.276")); //$NON-NLS-1$ + } + + if (dataBuffer == null) { + // awt.278=dataBuffer is null + throw new NullPointerException(Messages.getString("awt.278")); //$NON-NLS-1$ + } + + int dataType = dataBuffer.getDataType(); + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT) { + // awt.230=dataType is not one of the supported data types + throw new IllegalArgumentException(Messages.getString("awt.230")); //$NON-NLS-1$ + } + + if (dataBuffer.getNumBanks() > 1) { + // awt.27A=dataBuffer has more than one bank + throw new RasterFormatException(Messages.getString("awt.27A")); //$NON-NLS-1$ + } + + if (bandOffsets == null) { + // awt.27B=bandOffsets is null + throw new NullPointerException(Messages.getString("awt.27B")); //$NON-NLS-1$ + } + + PixelInterleavedSampleModel sampleModel = new PixelInterleavedSampleModel(dataType, w, h, + pixelStride, scanlineStride, bandOffsets); + + return new OrdinaryWritableRaster(sampleModel, dataBuffer, location); + + } + + /** + * Creates a Raster object with a PixelInterleavedSampleModel and the + * specified data type. The Data type can be one of the following values: + * TYPE_BYTE, TYPE_USHORT, or TYPE_INT. + * + * @param dataType + * the data type of the samples: TYPE_BYTE, TYPE_USHORT, or + * TYPE_INT. + * @param w + * the width of image data. + * @param h + * the height of image data. + * @param scanlineStride + * the scanline stride of the image data. + * @param pixelStride + * the pixel stride of image data. + * @param bandOffsets + * the band offsets of bands. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster object. + */ + public static WritableRaster createInterleavedRaster(int dataType, int w, int h, + int scanlineStride, int pixelStride, int bandOffsets[], Point location) { + + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + if ((long)location.x + w > Integer.MAX_VALUE || (long)location.y + h > Integer.MAX_VALUE) { + // awt.276=location.x + w or location.y + h results in integer + // overflow + throw new RasterFormatException(Messages.getString("awt.276")); //$NON-NLS-1$ + } + + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT) { + // awt.230=dataType is not one of the supported data types + throw new IllegalArgumentException(Messages.getString("awt.230")); //$NON-NLS-1$ + } + + if (bandOffsets == null) { + // awt.27B=bandOffsets is null + throw new NullPointerException(Messages.getString("awt.27B")); //$NON-NLS-1$ + } + + int minOffset = bandOffsets[0]; + for (int i = 1; i < bandOffsets.length; i++) { + if (bandOffsets[i] < minOffset) { + minOffset = bandOffsets[i]; + } + } + int size = (h - 1) * scanlineStride + w * pixelStride + minOffset; + DataBuffer data = null; + + switch (dataType) { + case DataBuffer.TYPE_BYTE: + data = new DataBufferByte(size); + break; + case DataBuffer.TYPE_USHORT: + data = new DataBufferUShort(size); + break; + } + + return createInterleavedRaster(data, w, h, scanlineStride, pixelStride, bandOffsets, + location); + } + + /** + * Creates a Raster object with a PixelInterleavedSampleModel and the + * specified data type. The Data type can be one of the following values: + * TYPE_BYTE, TYPE_USHORT, or TYPE_INT. + * + * @param dataType + * the data type of samples: TYPE_BYTE, TYPE_USHORT, or TYPE_INT. + * @param w + * the width of image data. + * @param h + * the height of image data. + * @param bands + * the number of bands. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster. + */ + public static WritableRaster createInterleavedRaster(int dataType, int w, int h, int bands, + Point location) { + + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + if ((long)location.x + w > Integer.MAX_VALUE || (long)location.y + h > Integer.MAX_VALUE) { + // awt.276=location.x + w or location.y + h results in integer + // overflow + throw new RasterFormatException(Messages.getString("awt.276")); //$NON-NLS-1$ + } + + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT) { + // awt.230=dataType is not one of the supported data types + throw new IllegalArgumentException(Messages.getString("awt.230")); //$NON-NLS-1$ + } + + int bandOffsets[] = new int[bands]; + for (int i = 0; i < bands; i++) { + bandOffsets[i] = i; + } + + return createInterleavedRaster(dataType, w, h, w * bands, bands, bandOffsets, location); + } + + /** + * Creates a Raster object with a SinglePixelPackedSampleModel and the + * specified DataBuffer. + * + * @param dataBuffer + * the DataBuffer. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param scanlineStride + * the scanline stride of the image data. + * @param bandMasks + * the band masks. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster. + */ + public static WritableRaster createPackedRaster(DataBuffer dataBuffer, int w, int h, + int scanlineStride, int bandMasks[], Point location) { + if (dataBuffer == null) { + // awt.278=dataBuffer is null + throw new NullPointerException(Messages.getString("awt.278")); //$NON-NLS-1$ + } + + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + if ((long)location.x + w > Integer.MAX_VALUE || (long)location.y + h > Integer.MAX_VALUE) { + // awt.276=location.x + w or location.y + h results in integer + // overflow + throw new RasterFormatException(Messages.getString("awt.276")); //$NON-NLS-1$ + } + + if (bandMasks == null) { + // awt.27C=bandMasks is null + throw new RasterFormatException(Messages.getString("awt.27C")); //$NON-NLS-1$ + } + + if (dataBuffer.getNumBanks() > 1) { + // awt.27A=dataBuffer has more than one bank + throw new RasterFormatException(Messages.getString("awt.27A")); //$NON-NLS-1$ + } + + int dataType = dataBuffer.getDataType(); + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT + && dataType != DataBuffer.TYPE_INT) { + // awt.230=dataType is not one of the supported data types + throw new IllegalArgumentException(Messages.getString("awt.230")); //$NON-NLS-1$ + } + + SinglePixelPackedSampleModel sampleModel = new SinglePixelPackedSampleModel(dataType, w, h, + scanlineStride, bandMasks); + + return new OrdinaryWritableRaster(sampleModel, dataBuffer, location); + } + + /** + * Creates a Raster object with a MultiPixelPackedSampleModel and the + * specified DataBuffer. + * + * @param dataBuffer + * the DataBuffer. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param bitsPerPixel + * the number of bits per pixel. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster. + */ + public static WritableRaster createPackedRaster(DataBuffer dataBuffer, int w, int h, + int bitsPerPixel, Point location) { + + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + if ((long)location.x + w > Integer.MAX_VALUE || (long)location.y + h > Integer.MAX_VALUE) { + // awt.276=location.x + w or location.y + h results in integer + // overflow + throw new RasterFormatException(Messages.getString("awt.276")); //$NON-NLS-1$ + } + + if (dataBuffer == null) { + // awt.278=dataBuffer is null + throw new NullPointerException(Messages.getString("awt.278")); //$NON-NLS-1$ + } + + if (dataBuffer.getNumBanks() > 1) { + // awt.27A=dataBuffer has more than one bank + throw new RasterFormatException(Messages.getString("awt.27A")); //$NON-NLS-1$ + } + + int dataType = dataBuffer.getDataType(); + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT + && dataType != DataBuffer.TYPE_INT) { + // awt.230=dataType is not one of the supported data types + throw new IllegalArgumentException(Messages.getString("awt.230")); //$NON-NLS-1$ + } + + MultiPixelPackedSampleModel sampleModel = new MultiPixelPackedSampleModel(dataType, w, h, + bitsPerPixel); + + return new OrdinaryWritableRaster(sampleModel, dataBuffer, location); + + } + + /** + * Creates a Raster object with a MultiPixelPackedSampleModel and the + * specified DataBuffer. + * + * @param dataType + * the data type of samples: TYPE_BYTE, TYPE_USHORT, or TYPE_INT. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param bands + * the number of bands. + * @param bitsPerBand + * the number of bits per band. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster. + */ + public static WritableRaster createPackedRaster(int dataType, int w, int h, int bands, + int bitsPerBand, Point location) { + + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + if ((long)location.x + w > Integer.MAX_VALUE || (long)location.y + h > Integer.MAX_VALUE) { + // awt.276=location.x + w or location.y + h results in integer + // overflow + throw new RasterFormatException(Messages.getString("awt.276")); //$NON-NLS-1$ + } + + if (bands < 1 || bitsPerBand < 1) { + // awt.27D=bitsPerBand or bands is not greater than zero + throw new IllegalArgumentException(Messages.getString("awt.27D")); //$NON-NLS-1$ + } + + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT + && dataType != DataBuffer.TYPE_INT) { + // awt.230=dataType is not one of the supported data types + throw new IllegalArgumentException(Messages.getString("awt.230")); //$NON-NLS-1$ + } + + if (bitsPerBand * bands > DataBuffer.getDataTypeSize(dataType)) { + // awt.27E=The product of bitsPerBand and bands is greater than the + // number of bits held by dataType + throw new IllegalArgumentException(Messages.getString("awt.27E")); //$NON-NLS-1$ + } + + if (bands > 1) { + + int bandMasks[] = new int[bands]; + int mask = (1 << bitsPerBand) - 1; + + for (int i = 0; i < bands; i++) { + bandMasks[i] = mask << (bitsPerBand * (bands - 1 - i)); + } + + return createPackedRaster(dataType, w, h, bandMasks, location); + } + DataBuffer data = null; + int size = ((bitsPerBand * w + DataBuffer.getDataTypeSize(dataType) - 1) / DataBuffer + .getDataTypeSize(dataType)) + * h; + + switch (dataType) { + case DataBuffer.TYPE_BYTE: + data = new DataBufferByte(size); + break; + case DataBuffer.TYPE_USHORT: + data = new DataBufferUShort(size); + break; + case DataBuffer.TYPE_INT: + data = new DataBufferInt(size); + break; + } + return createPackedRaster(data, w, h, bitsPerBand, location); + } + + /** + * Creates a Raster object with a SinglePixelPackedSampleModel and the + * specified DataBuffer. + * + * @param dataType + * the data type of samples: TYPE_BYTE, TYPE_USHORT, or TYPE_INT. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param bandMasks + * the band masks. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster. + */ + public static WritableRaster createPackedRaster(int dataType, int w, int h, int bandMasks[], + Point location) { + + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT + && dataType != DataBuffer.TYPE_INT) { + // awt.230=dataType is not one of the supported data types + throw new IllegalArgumentException(Messages.getString("awt.230")); //$NON-NLS-1$ + } + + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + if ((long)location.x + w > Integer.MAX_VALUE || (long)location.y + h > Integer.MAX_VALUE) { + // awt.276=location.x + w or location.y + h results in integer + // overflow + throw new RasterFormatException(Messages.getString("awt.276")); //$NON-NLS-1$ + } + + if (bandMasks == null) { + // awt.27C=bandMasks is null + throw new NullPointerException(Messages.getString("awt.27C")); //$NON-NLS-1$ + } + + DataBuffer data = null; + + switch (dataType) { + case DataBuffer.TYPE_BYTE: + data = new DataBufferByte(w * h); + break; + case DataBuffer.TYPE_USHORT: + data = new DataBufferUShort(w * h); + break; + case DataBuffer.TYPE_INT: + data = new DataBufferInt(w * h); + break; + } + + return createPackedRaster(data, w, h, w, bandMasks, location); + } + + /** + * Creates a Raster object with the specified DataBuffer and SampleModel. + * + * @param sm + * the specified SampleModel. + * @param db + * the specified DataBuffer. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the Raster. + */ + public static Raster createRaster(SampleModel sm, DataBuffer db, Point location) { + + if (sm == null || db == null) { + // awt.27F=SampleModel or DataBuffer is null + throw new NullPointerException(Messages.getString("awt.27F")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + return new Raster(sm, db, location); + } + + /** + * Creates a WritableRaster with the specified SampleModel and DataBuffer. + * + * @param sm + * the specified SampleModel. + * @param db + * the specified DataBuffer. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster. + */ + public static WritableRaster createWritableRaster(SampleModel sm, DataBuffer db, Point location) { + + if (sm == null || db == null) { + // awt.27F=SampleModel or DataBuffer is null + throw new NullPointerException(Messages.getString("awt.27F")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + return new OrdinaryWritableRaster(sm, db, location); + } + + /** + * Creates a WritableRaster with the specified SampleModel. + * + * @param sm + * the specified SampleModel. + * @param location + * the location which defines the upper left corner of the + * Raster. + * @return the WritableRaster. + */ + public static WritableRaster createWritableRaster(SampleModel sm, Point location) { + + if (sm == null) { + // awt.280=SampleModel is null + throw new NullPointerException(Messages.getString("awt.280")); //$NON-NLS-1$ + } + + if (location == null) { + location = new Point(0, 0); + } + + return createWritableRaster(sm, sm.createDataBuffer(), location); + } + + /** + * Instantiates a new Raster object with the specified SampleModel and + * DataBuffer. + * + * @param sampleModel + * the specified SampleModel. + * @param dataBuffer + * the specified DataBuffer. + * @param origin + * the specified origin. + */ + protected Raster(SampleModel sampleModel, DataBuffer dataBuffer, Point origin) { + + this(sampleModel, dataBuffer, new Rectangle(origin.x, origin.y, sampleModel.getWidth(), + sampleModel.getHeight()), origin, null); + } + + /** + * Instantiates a new Raster object with the specified SampleModel, + * DataBuffer, rectangular region and parent Raster. + * + * @param sampleModel + * the specified SampleModel. + * @param dataBuffer + * the specified DataBuffer. + * @param aRegion + * the a rectangular region which defines the new image bounds. + * @param sampleModelTranslate + * this point defines the translation point from the SampleModel + * coordinates to the new Raster coordinates. + * @param parent + * the parent of this Raster. + */ + protected Raster(SampleModel sampleModel, DataBuffer dataBuffer, Rectangle aRegion, + Point sampleModelTranslate, Raster parent) { + + if (sampleModel == null || dataBuffer == null || aRegion == null + || sampleModelTranslate == null) { + // awt.281=sampleModel, dataBuffer, aRegion or sampleModelTranslate + // is null + throw new NullPointerException(Messages.getString("awt.281")); //$NON-NLS-1$ + } + + if (aRegion.width <= 0 || aRegion.height <= 0) { + // awt.282=aRegion has width or height less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.282")); //$NON-NLS-1$ + } + + if ((long)aRegion.x + (long)aRegion.width > Integer.MAX_VALUE) { + // awt.283=Overflow X coordinate of Raster + throw new RasterFormatException(Messages.getString("awt.283")); //$NON-NLS-1$ + } + + if ((long)aRegion.y + (long)aRegion.height > Integer.MAX_VALUE) { + // awt.284=Overflow Y coordinate of Raster + throw new RasterFormatException(Messages.getString("awt.284")); //$NON-NLS-1$ + } + + if (sampleModel instanceof ComponentSampleModel) { + validateDataBuffer(dataBuffer, aRegion.width, aRegion.height, + ((ComponentSampleModel)sampleModel).getScanlineStride()); + } else if (sampleModel instanceof MultiPixelPackedSampleModel) { + validateDataBuffer(dataBuffer, aRegion.width, aRegion.height, + ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride()); + } else if (sampleModel instanceof SinglePixelPackedSampleModel) { + validateDataBuffer(dataBuffer, aRegion.width, aRegion.height, + ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride()); + } + + this.sampleModel = sampleModel; + this.dataBuffer = dataBuffer; + this.minX = aRegion.x; + this.minY = aRegion.y; + this.width = aRegion.width; + this.height = aRegion.height; + this.sampleModelTranslateX = sampleModelTranslate.x; + this.sampleModelTranslateY = sampleModelTranslate.y; + this.parent = parent; + this.numBands = sampleModel.getNumBands(); + this.numDataElements = sampleModel.getNumDataElements(); + + } + + /** + * Instantiates a new Raster with the specified SampleModel. + * + * @param sampleModel + * the specified SampleModel. + * @param origin + * the origin. + */ + protected Raster(SampleModel sampleModel, Point origin) { + this(sampleModel, sampleModel.createDataBuffer(), new Rectangle(origin.x, origin.y, + sampleModel.getWidth(), sampleModel.getHeight()), origin, null); + } + + /** + * Creates the child of this Raster by sharing the specified rectangular + * area in this raster. The parentX, parentY, width and height parameters + * specify the rectangular area to be shared. + * + * @param parentX + * the X coordinate of the upper left corner of this Raster. + * @param parentY + * the Y coordinate of the upper left corner of this Raster. + * @param width + * the width of the child area. + * @param height + * the height of the child area. + * @param childMinX + * the X coordinate of child area mapped to the parentX + * coordinate. + * @param childMinY + * the Y coordinate of child area mapped to the parentY + * coordinate. + * @param bandList + * the array of band indices. + * @return the Raster. + */ + public Raster createChild(int parentX, int parentY, int width, int height, int childMinX, + int childMinY, int bandList[]) { + if (width <= 0 || height <= 0) { + // awt.285=Width or Height of child Raster is less than or equal to + // zero + throw new RasterFormatException(Messages.getString("awt.285")); //$NON-NLS-1$ + } + + if (parentX < this.minX || parentX + width > this.minX + this.width) { + // awt.286=parentX disposes outside Raster + throw new RasterFormatException(Messages.getString("awt.286")); //$NON-NLS-1$ + } + + if (parentY < this.minY || parentY + height > this.minY + this.height) { + // awt.287=parentY disposes outside Raster + throw new RasterFormatException(Messages.getString("awt.287")); //$NON-NLS-1$ + } + + if ((long)parentX + width > Integer.MAX_VALUE) { + // awt.288=parentX + width results in integer overflow + throw new RasterFormatException(Messages.getString("awt.288")); //$NON-NLS-1$ + } + + if ((long)parentY + height > Integer.MAX_VALUE) { + // awt.289=parentY + height results in integer overflow + throw new RasterFormatException(Messages.getString("awt.289")); //$NON-NLS-1$ + } + + if ((long)childMinX + width > Integer.MAX_VALUE) { + // awt.28A=childMinX + width results in integer overflow + throw new RasterFormatException(Messages.getString("awt.28A")); //$NON-NLS-1$ + } + + if ((long)childMinY + height > Integer.MAX_VALUE) { + // awt.28B=childMinY + height results in integer overflow + throw new RasterFormatException(Messages.getString("awt.28B")); //$NON-NLS-1$ + } + + SampleModel childModel; + + if (bandList == null) { + childModel = sampleModel; + } else { + childModel = sampleModel.createSubsetSampleModel(bandList); + } + + int childTranslateX = childMinX - parentX; + int childTranslateY = childMinY - parentY; + + return new Raster(childModel, dataBuffer, + new Rectangle(childMinX, childMinY, width, height), new Point(childTranslateX + + sampleModelTranslateX, childTranslateY + sampleModelTranslateY), this); + } + + /** + * Create a compatible WritableRaster with the same parameters as this + * Raster. + * + * @return the WritableRaster. + */ + public WritableRaster createCompatibleWritableRaster() { + return new OrdinaryWritableRaster(sampleModel, new Point(0, 0)); + } + + /** + * Create a compatible WritableRaster with the same parameters as this + * Raster and the specified size. + * + * @param w + * the width of the new WritableRaster. + * @param h + * the height of the new WritableRaster. + * @return the WritableRaster. + */ + public WritableRaster createCompatibleWritableRaster(int w, int h) { + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new RasterFormatException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + SampleModel sm = sampleModel.createCompatibleSampleModel(w, h); + + return new OrdinaryWritableRaster(sm, new Point(0, 0)); + } + + /** + * Create a compatible WritableRaster with the same parameters as this + * Raster and the specified size and location. + * + * @param x + * the X coordinate of the new WritableRaster. + * @param y + * the Y coordinate of the new WritableRaster. + * @param w + * the width of the new WritableRaster. + * @param h + * the height of the new WritableRaster. + * @return the WritableRaster. + */ + public WritableRaster createCompatibleWritableRaster(int x, int y, int w, int h) { + + WritableRaster raster = createCompatibleWritableRaster(w, h); + + return raster.createWritableChild(0, 0, w, h, x, y, null); + } + + /** + * Create a compatible WritableRaster with the same parameters as this + * Raster and the specified rectangle which determines new WritableRaster's + * location and size. + * + * @param rect + * the specified Rectangle. + * @return the WritableRaster. + */ + public WritableRaster createCompatibleWritableRaster(Rectangle rect) { + if (rect == null) { + // awt.28C=Rect is null + throw new NullPointerException(Messages.getString("awt.28C")); //$NON-NLS-1$ + } + + return createCompatibleWritableRaster(rect.x, rect.y, rect.width, rect.height); + } + + /** + * Creates the translated child of this Raster. The New Raster object is a + * reference to the this Raster with a different location. + * + * @param childMinX + * the X coordinate of the new Raster. + * @param childMinY + * the Y coordinate of the new Raster. + * @return the Raster. + */ + public Raster createTranslatedChild(int childMinX, int childMinY) { + return createChild(minX, minY, width, height, childMinX, childMinY, null); + } + + /** + * Gets the bounds of this Raster as a rectangle. + * + * @return the bounds of this Raster. + */ + public Rectangle getBounds() { + return new Rectangle(minX, minY, width, height); + } + + /** + * Gets the DataBuffer associated with this Raster. + * + * @return the DataBuffer associated with this Raster. + */ + public DataBuffer getDataBuffer() { + return dataBuffer; + } + + /** + * Gets the data elements which represent the pixel data of the specified + * rectangle area as a primitive array. The following image data types are + * supported: DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, + * DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, or + * DataBuffer.TYPE_DOUBLE. + * + * @param x + * the X coordinate of the area of pixels. + * @param y + * the Y coordinate of the area of pixels. + * @param w + * the width of the area of pixels. + * @param h + * the height of the area of pixels. + * @param outData + * the resulting array. + * @return the data elements of the specified area of this Raster. + */ + public Object getDataElements(int x, int y, int w, int h, Object outData) { + return sampleModel.getDataElements(x - sampleModelTranslateX, y - sampleModelTranslateY, w, + h, outData, dataBuffer); + } + + /** + * Gets the data elements which represent the specified pixel of this Raster + * as a primitive array. The following image data types are supported: + * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, + * DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, or DataBuffer.TYPE_DOUBLE. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param outData + * the resulting data. + * @return the data elements of the specified pixel of this Raster. + */ + public Object getDataElements(int x, int y, Object outData) { + return sampleModel.getDataElements(x - sampleModelTranslateX, y - sampleModelTranslateY, + outData, dataBuffer); + } + + /** + * Gets the height of this Raster. + * + * @return the height of this Raster. + */ + public final int getHeight() { + return height; + } + + /** + * Gets the minimum X coordinate of this Raster. + * + * @return the minimum X coordinate of this Raster. + */ + public final int getMinX() { + return minX; + } + + /** + * Gets the minimum Y coordinate of this Raster. + * + * @return the minimum Y coordinate of this Raster. + */ + public final int getMinY() { + return minY; + } + + /** + * Gets the number of bands in this Raster. + * + * @return the number of bands in this Raster. + */ + public final int getNumBands() { + return numBands; + } + + /** + * Gets the number of data elements for one pixel. + * + * @return the number of data elements for one pixel. + */ + public final int getNumDataElements() { + return numDataElements; + } + + /** + * Gets the parent Raster for this Raster object. + * + * @return the parent Raster for this Raster object. + */ + public Raster getParent() { + return parent; + } + + /** + * Gets a double array of samples for the specified pixel in this Raster. + * + * @param x + * the pixel's X coordinate. + * @param y + * the pixel's Y coordinate. + * @param dArray + * the double array where result array will be stored. + * @return the double array of samples for the specified pixel in this + * Raster. + */ + public double[] getPixel(int x, int y, double dArray[]) { + return sampleModel.getPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, dArray, + dataBuffer); + } + + /** + * Gets a float array of samples for the specified pixel in this Raster. + * + * @param x + * the pixel's X coordinate. + * @param y + * the pixel's Y coordinate. + * @param fArray + * the float array where the result array will be stored. + * @return the float array of samples for the specified pixel in this + * Raster. + */ + public float[] getPixel(int x, int y, float fArray[]) { + return sampleModel.getPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, fArray, + dataBuffer); + } + + /** + * Gets an integer array of samples for the specified pixel in this Raster. + * + * @param x + * the pixel's X coordinate. + * @param y + * the pixel's Y coordinate. + * @param iArray + * the integer array where the result array will be stored. + * @return the integer array of samples for the specified pixel in this + * Raster. + */ + public int[] getPixel(int x, int y, int iArray[]) { + return sampleModel.getPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, iArray, + dataBuffer); + } + + /** + * Gets an double array of samples for the specified rectangular area of + * pixels in this Raster. + * + * @param x + * the X coordinate of the area of pixels. + * @param y + * the Y coordinate of the area of pixels. + * @param w + * the width of the area of pixels. + * @param h + * the height of the area of pixels. + * @param dArray + * the resulting array. + * @return the double array of samples for the specified rectangular area of + * pixels in this Raster. + */ + public double[] getPixels(int x, int y, int w, int h, double dArray[]) { + return sampleModel.getPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, + dArray, dataBuffer); + } + + /** + * Gets an float array of samples for the specified rectangular area of + * pixels in this Raster. + * + * @param x + * the X coordinate of the area of pixels. + * @param y + * the Y coordinate of the area of pixels. + * @param w + * the width of the area of pixels. + * @param h + * the height of the area of pixels. + * @param fArray + * the resulting array. + * @return the float array of samples for the specified rectangular area of + * pixels in this Raster. + */ + public float[] getPixels(int x, int y, int w, int h, float fArray[]) { + return sampleModel.getPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, + fArray, dataBuffer); + } + + /** + * Gets an integer array of samples for the specified rectangular area of + * pixels in this raster. + * + * @param x + * the X coordinate of the area of pixels. + * @param y + * the Y coordinate of the area of pixels. + * @param w + * the width of pixel's the area of pixels. + * @param h + * the height of pixel's the area of pixels. + * @param iArray + * the resulting array. + * @return the integer array of samples for the specified rectangular area + * of pixels in this Raster. + */ + public int[] getPixels(int x, int y, int w, int h, int iArray[]) { + return sampleModel.getPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, + iArray, dataBuffer); + } + + /** + * Gets the sample for the specified band of the specified pixel as an + * integer. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param b + * the band. + * @return the sample for the specified band of the specified pixel as an + * integer. + */ + public int getSample(int x, int y, int b) { + return sampleModel.getSample(x - sampleModelTranslateX, y - sampleModelTranslateY, b, + dataBuffer); + } + + /** + * Gets the sample for the specified band of the specified pixel as a + * double. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param b + * the band. + * @return the sample for the specified band of the specified pixel as a + * double. + */ + public double getSampleDouble(int x, int y, int b) { + return sampleModel.getSampleDouble(x - sampleModelTranslateX, y - sampleModelTranslateY, b, + dataBuffer); + } + + /** + * Gets the sample for the specified band of the specified pixel as a float. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param b + * the band. + * @return the sample for the specified band of the specified pixel as a + * float. + */ + public float getSampleFloat(int x, int y, int b) { + return sampleModel.getSampleFloat(x - sampleModelTranslateX, y - sampleModelTranslateY, b, + dataBuffer); + } + + /** + * Gets the SampleModel associated with this Raster. + * + * @return the SampleModel associated with this Raster. + */ + public SampleModel getSampleModel() { + return sampleModel; + } + + /** + * Gets the translation of the X coordinate from the SampleModel coordinate + * system to the Rasters's coordinate system. + * + * @return the value of the translation of the X coordinate from the + * SampleModel coordinate system to the Rasters's coordinate system. + */ + public final int getSampleModelTranslateX() { + return sampleModelTranslateX; + } + + /** + * Gets the translation of the Y coordinate from the SampleModel coordinate + * system to the Rasters's coordinate system. + * + * @return the value of the translation of the Y coordinate from the + * SampleModel coordinate system to the Rasters's coordinate system. + */ + public final int getSampleModelTranslateY() { + return sampleModelTranslateY; + } + + /** + * Gets the double array of samples for the specified band of the specified + * rectangular area of pixels in this Raster as a double array. + * + * @param x + * the X coordinate of the rectangular area of pixels. + * @param y + * the Y coordinate of the rectangular area of pixels. + * @param w + * the width of the rectangular area of pixels. + * @param h + * the height of the rectangular area of pixels. + * @param b + * the band. + * @param dArray + * the resulting double array. + * @return the double array of samples for the specified band of the + * specified rectangular area of pixels. + */ + public double[] getSamples(int x, int y, int w, int h, int b, double dArray[]) { + + return sampleModel.getSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, + b, dArray, dataBuffer); + } + + /** + * Gets the float array of samples for the specified band of the specified + * rectangular area of pixels in this Raster as a float array. + * + * @param x + * the X coordinate of the rectangular area of pixels. + * @param y + * the Y coordinate of the rectangular area of pixels. + * @param w + * the width of the rectangular area of pixels. + * @param h + * the height of the rectangular area of pixels. + * @param b + * the band. + * @param fArray + * the resulting float array. + * @return the float array of samples for the specified band of the + * specified rectangular area of pixels. + */ + public float[] getSamples(int x, int y, int w, int h, int b, float fArray[]) { + + return sampleModel.getSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, + b, fArray, dataBuffer); + } + + /** + * Gets the integer array of samples for the specified band of the specified + * rectangular area of pixels in this Raster as a integer array. + * + * @param x + * the X coordinate of the rectangular area of pixels. + * @param y + * the Y coordinate of the rectangular area of pixels. + * @param w + * the width of the rectangular area of pixels. + * @param h + * the height of the rectangular area of pixels. + * @param b + * the band. + * @param iArray + * the resulting integer array. + * @return the integer array of samples for the specified band of the + * specified rectangular area of pixels. + */ + public int[] getSamples(int x, int y, int w, int h, int b, int iArray[]) { + return sampleModel.getSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, + b, iArray, dataBuffer); + } + + /** + * Gets the transfer type for pixels of this Raster. + * + * @see SampleModel#getTransferType() + * @return the transfer type for pixels of this Raster. + */ + public final int getTransferType() { + return sampleModel.getTransferType(); + } + + /** + * Gets the width of this Raster. + * + * @return the width of this Raster. + */ + public final int getWidth() { + return width; + } + + /** + * Validate data buffer. + * + * @param dataBuffer + * the data buffer. + * @param w + * the w. + * @param h + * the h. + * @param scanlineStride + * the scanline stride. + */ + private static void validateDataBuffer(final DataBuffer dataBuffer, final int w, final int h, + final int scanlineStride) { + if (dataBuffer.getSize() < (scanlineStride * (h - 1) + w - 1)) { + // awt.298=dataBuffer is too small + throw new RasterFormatException(Messages.getString("awt.298")); //$NON-NLS-1$ + } + } +} diff --git a/awt/java/awt/image/RasterFormatException.java b/awt/java/awt/image/RasterFormatException.java new file mode 100644 index 000000000..c667141ff --- /dev/null +++ b/awt/java/awt/image/RasterFormatException.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +/** + * The RasterFormatException class represents the exception that is thrown when + * there's an invalid layout in the Raster. + * + * @since Android 1.0 + */ +public class RasterFormatException extends RuntimeException { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = 96598996116164315L; + + /** + * Instantiates a new RasterFormatException with the specified detail + * message. + * + * @param s + * the detail message. + */ + public RasterFormatException(String s) { + super(s); + } + +} diff --git a/awt/java/awt/image/RasterOp.java b/awt/java/awt/image/RasterOp.java new file mode 100644 index 000000000..19a84c9df --- /dev/null +++ b/awt/java/awt/image/RasterOp.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * The RasterOp interface provides methods for performing transformations from + * source data to destination data for Raster objects. The source and + * destination objects should contain the appropriate number of bands for the + * particular classes which implement this interface. + * + * @since Android 1.0 + */ +public interface RasterOp { + + /** + * Creates a destination WritableRaster with the specified Raster; this + * destination image data is empty and has the correct size and number of + * bands. + * + * @param src + * the source Raster. + * @return the WritableRaster. + */ + public WritableRaster createCompatibleDestRaster(Raster src); + + /** + * Performs a filter operation on the source Raster and stores the resulting + * image data to the destination WritableRaster. + * + * @param src + * the source Raster. + * @param dst + * the destination WritableRaster, where the result is stored. + * @return the filtered WritableRaster. + */ + public WritableRaster filter(Raster src, WritableRaster dst); + + /** + * Gets the bounds of the filtered Raster. + * + * @param src + * the source Raster to be filtered. + * @return the rectangle bounds of the filtered Raster. + */ + public Rectangle2D getBounds2D(Raster src); + + /** + * Gets the point of the destination image which corresponds to the + * specified point in the source raster. + * + * @param srcPoint + * the point of the source raster. + * @param dstPoint + * the point where the result will be stored. + * @return the destination point. + */ + public Point2D getPoint2D(Point2D srcPoint, Point2D dstPoint); + + /** + * Gets the RenderingHints of the RasterOp. + * + * @return the RenderingHints of the RasterOp. + */ + public RenderingHints getRenderingHints(); +} diff --git a/awt/java/awt/image/RenderedImage.java b/awt/java/awt/image/RenderedImage.java new file mode 100644 index 000000000..5eafa649a --- /dev/null +++ b/awt/java/awt/image/RenderedImage.java @@ -0,0 +1,198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.Rectangle; +import java.util.Vector; + +/** + * The RenderedImage interface should be implemented by all objects which + * contains image data. The image data is represented as a single tile or an + * array of tiles. + * + * @since Android 1.0 + */ +public interface RenderedImage { + + /** + * Gets the property with the specified name from the property set of this + * RenderedImage. + * + * @param name + * the property's name. + * @return the property value corresponded to this property's name. + */ + public Object getProperty(String name); + + /** + * Copies the region of this RenderedImage to the specified WritableRaster. + * The bounds of the region are the bounds of the WritableRaster. + * + * @param raster + * the WritableRaster. + * @return the created WritableRaster. + */ + public WritableRaster copyData(WritableRaster raster); + + /** + * Gets the image data of the image's region as one tile. + * + * @param rect + * the rectangular region of RenderedImage. + * @return the image data of the image's region as one tile. + */ + public Raster getData(Rectangle rect); + + /** + * Gets all RenderedImage objects which are the source of this RenderedImage + * object. + * + * @return a Vector of RenderedImage objects which are the source of this + * RenderedImage object or null, if there is no information about + * them. + */ + public Vector getSources(); + + /** + * Gets the set of all property names for this RenderedImage. + * + * @return the array of all property names for this RenderedImage. + */ + public String[] getPropertyNames(); + + /** + * Gets the SampleModel of this RenderedImage. + * + * @return the SampleModel of this RenderedImage. + */ + public SampleModel getSampleModel(); + + /** + * Gets the tile corresponded to the specified indices in the tile array. + * + * @param tileX + * the X index of the tile. + * @param tileY + * the Y index of the tile. + * @return the tile corresponded to the specified indices in the tile array. + */ + public Raster getTile(int tileX, int tileY); + + /** + * Gets the image data of this image as one tile. + * + * @return the image data of this image as one tile. + */ + public Raster getData(); + + /** + * Gets the ColorModel of this RenderedImage. + * + * @return the ColorModel of this RenderedImage. + */ + public ColorModel getColorModel(); + + /** + * Gets the width of the RenderedImage. + * + * @return the width of the RenderedImage. + */ + public int getWidth(); + + /** + * Gets the tile width. + * + * @return the tile width in pixels. + */ + public int getTileWidth(); + + /** + * Gets the tile height. + * + * @return the tile height in pixels. + */ + public int getTileHeight(); + + /** + * Gets the Y offset of the tile grid. + * + * @return the Y offset of the tile grid. + */ + public int getTileGridYOffset(); + + /** + * Gets the X offset of the tile grid. + * + * @return the X offset of the tile grid. + */ + public int getTileGridXOffset(); + + /** + * Gets the number of tiles along Y direction. + * + * @return the number of tiles along Y direction. + */ + public int getNumYTiles(); + + /** + * Gets the number of tiles along X direction. + * + * @return the number of tiles along X direction. + */ + public int getNumXTiles(); + + /** + * Gets the minimum Y coordinate of this RenderedImage. + * + * @return the minimum Y coordinate of this RenderedImage. + */ + public int getMinY(); + + /** + * Gets the minimum X coordinate of this RenderedImage. + * + * @return the minimum X coordinate of this RenderedImage. + */ + public int getMinX(); + + /** + * Gets the minimum tile's index along the Y direction. + * + * @return the minimum tile's index along the Y direction. + */ + public int getMinTileY(); + + /** + * Gets the minimum tile's index along the X direction. + * + * @return the minimum tile's index along the X direction. + */ + public int getMinTileX(); + + /** + * Gets the height of the RenderedImage. + * + * @return the height of the RenderedImage. + */ + public int getHeight(); + +} diff --git a/awt/java/awt/image/ReplicateScaleFilter.java b/awt/java/awt/image/ReplicateScaleFilter.java new file mode 100644 index 000000000..51c0f4994 --- /dev/null +++ b/awt/java/awt/image/ReplicateScaleFilter.java @@ -0,0 +1,225 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.util.Hashtable; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The ReplicateScaleFilter class scales an source image by replicating rows and + * columns of pixels to scale up or omitting rows and columns of pixels to scale + * down. + * + * @since Android 1.0 + */ +public class ReplicateScaleFilter extends ImageFilter { + + /** + * The width of a source image. + */ + protected int srcWidth; + + /** + * The height of a source image. + */ + protected int srcHeight; + + /** + * The width of a destination image. + */ + protected int destWidth; + + /** + * The height of a destination image. + */ + protected int destHeight; + + /** + * The integer array of source rows. + */ + protected int[] srcrows; + + /** + * The integer array of source columns. + */ + protected int[] srccols; + + /** + * An Object (byte array with a destination width) provides a row of pixel + * data to the ImageConsumer. + */ + protected Object outpixbuf; + + /** + * Instantiates a new ReplicateScaleFilter that filters the image with the + * specified width and height. + * + * @param width + * the width of scaled image. + * @param height + * the height of scaled image. + */ + public ReplicateScaleFilter(int width, int height) { + if (width == 0 || height == 0) { + // awt.234=Width or Height equals zero + throw new IllegalArgumentException(Messages.getString("awt.234")); //$NON-NLS-1$ + } + + this.destWidth = width; + this.destHeight = height; + } + + @SuppressWarnings("unchecked") + @Override + public void setProperties(Hashtable props) { + Hashtable fprops; + if (props == null) { + fprops = new Hashtable(); + } else { + fprops = (Hashtable)props.clone(); + } + String propName = "Rescale Filters"; //$NON-NLS-1$ + String prop = "destWidth=" + destWidth + "; " + //$NON-NLS-1$ //$NON-NLS-2$ + "destHeight=" + destHeight; //$NON-NLS-1$ + Object o = fprops.get(propName); + if (o != null) { + if (o instanceof String) { + prop = (String)o + "; " + prop; //$NON-NLS-1$ + } else { + prop = o.toString() + "; " + prop; //$NON-NLS-1$ + } + } + fprops.put(propName, prop); + consumer.setProperties(fprops); + } + + // setPixels methods produce pixels according to Java API Spacification + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, + int scansize) { + + if (srccols == null) { + initArrays(); + } + int buff[]; + if (outpixbuf == null || !(outpixbuf instanceof int[])) { + buff = new int[destWidth]; + outpixbuf = buff; + } else { + buff = (int[])outpixbuf; + } + + int wa = (srcWidth - 1) >>> 1; + int ha = (srcHeight - 1) >>> 1; + int dstX = (x * destWidth + wa) / srcWidth; + int dstY = (y * destHeight + ha) / srcHeight; + + int sx, sy, dx, dy; + dy = dstY; + while ((dy < destHeight) && ((sy = srcrows[dy]) < y + h)) { + dx = dstX; + int srcOff = off + (sy - y) * scansize; + while ((dx < destWidth) && ((sx = srccols[dx]) < x + w)) { + buff[dx] = pixels[srcOff + (sx - x)]; + dx++; + } + + consumer.setPixels(dstX, dy, dx - dstX, 1, model, buff, dstX, destWidth); + dy++; + } + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, + int scansize) { + + if (srccols == null) { + initArrays(); + } + byte buff[]; + if (outpixbuf == null || !(outpixbuf instanceof byte[])) { + buff = new byte[destWidth]; + outpixbuf = buff; + } else { + buff = (byte[])outpixbuf; + } + + int wa = (srcWidth - 1) >>> 1; + int ha = (srcHeight - 1) >>> 1; + int dstX = (x * destWidth + wa) / srcWidth; + int dstY = (y * destHeight + ha) / srcHeight; + + int sx, sy, dx, dy; + dy = dstY; + while ((dy < destHeight) && ((sy = srcrows[dy]) < y + h)) { + dx = dstX; + int srcOff = off + (sy - y) * scansize; + while ((dx < destWidth) && ((sx = srccols[dx]) < x + w)) { + buff[dx] = pixels[srcOff + (sx - x)]; + dx++; + } + + consumer.setPixels(dstX, dy, dx - dstX, 1, model, buff, dstX, destWidth); + dy++; + } + } + + @Override + public void setDimensions(int w, int h) { + srcWidth = w; + srcHeight = h; + + if (destWidth < 0 && destHeight < 0) { + destWidth = srcWidth; + destHeight = srcHeight; + } else if (destWidth < 0) { + destWidth = destHeight * srcWidth / srcHeight; + } else if (destHeight < 0) { + destHeight = destWidth * srcHeight / srcWidth; + } + consumer.setDimensions(destWidth, destHeight); + } + + /** + * Initialization of srccols and srcrows arrays. + */ + private void initArrays() { + if ((destWidth < 0) || (destHeight < 0)) { + throw new IndexOutOfBoundsException(); + } + + srccols = new int[destWidth]; + int ca = srcWidth >>> 1; + for (int i = 0; i < destWidth; i++) { + srccols[i] = (i * srcWidth + ca) / destWidth; + } + + srcrows = new int[destHeight]; + int ra = srcHeight >>> 1; + for (int i = 0; i < destHeight; i++) { + srcrows[i] = (i * srcHeight + ra) / destHeight; + } + } + +} diff --git a/awt/java/awt/image/RescaleOp.java b/awt/java/awt/image/RescaleOp.java new file mode 100644 index 000000000..d7e2bd768 --- /dev/null +++ b/awt/java/awt/image/RescaleOp.java @@ -0,0 +1,659 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Oct 6, 2005 + */ + +package java.awt.image; + +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.*; +import java.util.Arrays; + +import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class RescaleOp performs rescaling of the source image data by + * multiplying the pixel values with a scale factor and then adding an offset. + * + * @since Android 1.0 + */ +public class RescaleOp implements BufferedImageOp, RasterOp { + + /** + * The scale factors. + */ + private float scaleFactors[]; + + /** + * The offsets. + */ + private float offsets[]; + + /** + * The hints. + */ + private RenderingHints hints; + + static { + // TODO + // System.loadLibrary("imageops"); + } + + /** + * Instantiates a new RescaleOp object with the specified scale factors and + * offsets. + * + * @param scaleFactors + * the array of scale factor values. + * @param offsets + * the array of offset values. + * @param hints + * the RenderingHints or null. + */ + public RescaleOp(float[] scaleFactors, float[] offsets, RenderingHints hints) { + int numFactors = Math.min(scaleFactors.length, offsets.length); + + this.scaleFactors = new float[numFactors]; + this.offsets = new float[numFactors]; + + System.arraycopy(scaleFactors, 0, this.scaleFactors, 0, numFactors); + System.arraycopy(offsets, 0, this.offsets, 0, numFactors); + + this.hints = hints; + } + + /** + * Instantiates a new RescaleOp object with the specified scale factor and + * offset. + * + * @param scaleFactor + * the scale factor. + * @param offset + * the offset. + * @param hints + * the RenderingHints or null. + */ + public RescaleOp(float scaleFactor, float offset, RenderingHints hints) { + scaleFactors = new float[1]; + offsets = new float[1]; + + scaleFactors[0] = scaleFactor; + offsets[0] = offset; + + this.hints = hints; + } + + /** + * Gets the number of scaling factors. + * + * @return the number of scaling factors. + */ + public final int getNumFactors() { + return scaleFactors.length; + } + + public final RenderingHints getRenderingHints() { + return hints; + } + + /** + * Gets the scale factors of this RescaleOp. + * + * @param scaleFactors + * the desired scale factors array will be copied to this array. + * @return the scale factors array. + */ + public final float[] getScaleFactors(float[] scaleFactors) { + if (scaleFactors == null) { + scaleFactors = new float[this.scaleFactors.length]; + } + + int minLength = Math.min(scaleFactors.length, this.scaleFactors.length); + System.arraycopy(this.scaleFactors, 0, scaleFactors, 0, minLength); + return scaleFactors; + } + + /** + * Gets the offsets array of this RescaleOp. + * + * @param offsets + * the desired offsets array will be copied to this array. + * @return the offsets array of this RescaleOp. + */ + public final float[] getOffsets(float[] offsets) { + if (offsets == null) { + offsets = new float[this.offsets.length]; + } + + int minLength = Math.min(offsets.length, this.offsets.length); + System.arraycopy(this.offsets, 0, offsets, 0, minLength); + return offsets; + } + + public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { + if (dstPt == null) { + dstPt = new Point2D.Float(); + } + + dstPt.setLocation(srcPt); + return dstPt; + } + + public final Rectangle2D getBounds2D(Raster src) { + return src.getBounds(); + } + + public final Rectangle2D getBounds2D(BufferedImage src) { + return getBounds2D(src.getRaster()); + } + + public WritableRaster createCompatibleDestRaster(Raster src) { + return src.createCompatibleWritableRaster(); + } + + public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) { + if (dstCM == null) { + dstCM = src.getColorModel(); + } + + if (dstCM instanceof IndexColorModel) { + dstCM = ColorModel.getRGBdefault(); + } + + WritableRaster r = dstCM.isCompatibleSampleModel(src.getSampleModel()) ? src.getRaster() + .createCompatibleWritableRaster(src.getWidth(), src.getHeight()) : dstCM + .createCompatibleWritableRaster(src.getWidth(), src.getHeight()); + + return new BufferedImage(dstCM, r, dstCM.isAlphaPremultiplied(), null); + } + + public final WritableRaster filter(Raster src, WritableRaster dst) { + if (dst == null) { + dst = createCompatibleDestRaster(src); + } else { + if (src.getNumBands() != dst.getNumBands()) { + // awt.21D=Number of src bands ({0}) does not match number of + // dst bands ({1}) + throw new IllegalArgumentException(Messages.getString("awt.21D", //$NON-NLS-1$ + src.getNumBands(), dst.getNumBands())); + } + } + + if (this.scaleFactors.length != 1 && this.scaleFactors.length != src.getNumBands()) { + // awt.21E=Number of scaling constants is not equal to the number of + // bands + throw new IllegalArgumentException(Messages.getString("awt.21E")); //$NON-NLS-1$ + } + + // TODO + // if (ippFilter(src, dst, BufferedImage.TYPE_CUSTOM, false) != 0) + if (slowFilter(src, dst, false) != 0) { + // awt.21F=Unable to transform source + throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$ + } + + return dst; + } + + /** + * Slow filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @param skipAlpha + * the skip alpha. + * @return the int. + */ + private final int slowFilter(Raster src, WritableRaster dst, boolean skipAlpha) { + SampleModel sm = src.getSampleModel(); + + int numBands = src.getNumBands(); + int srcHeight = src.getHeight(); + int srcWidth = src.getWidth(); + + int srcMinX = src.getMinX(); + int srcMinY = src.getMinY(); + int dstMinX = dst.getMinX(); + int dstMinY = dst.getMinY(); + + int[] maxValues = new int[numBands]; + int[] masks = new int[numBands]; + int[] sampleSizes = sm.getSampleSize(); + + for (int i = 0; i < numBands; i++) { + maxValues[i] = (1 << sampleSizes[i]) - 1; + masks[i] = ~(maxValues[i]); + } + + // Processing bounds + float[] pixels = null; + pixels = src.getPixels(srcMinX, srcMinY, srcWidth, srcHeight, pixels); + + // Cycle over pixels to be calculated + if (skipAlpha) { // Always suppose that alpha channel is the last band + if (scaleFactors.length > 1) { + for (int i = 0; i < pixels.length;) { + for (int bandIdx = 0; bandIdx < numBands - 1; bandIdx++, i++) { + pixels[i] = pixels[i] * scaleFactors[bandIdx] + offsets[bandIdx]; + // Check for overflow now + if (((int)pixels[i] & masks[bandIdx]) != 0) { + if (pixels[i] < 0) { + pixels[i] = 0; + } else { + pixels[i] = maxValues[bandIdx]; + } + } + } + + i++; + } + } else { + for (int i = 0; i < pixels.length;) { + for (int bandIdx = 0; bandIdx < numBands - 1; bandIdx++, i++) { + pixels[i] = pixels[i] * scaleFactors[0] + offsets[0]; + // Check for overflow now + if (((int)pixels[i] & masks[bandIdx]) != 0) { + if (pixels[i] < 0) { + pixels[i] = 0; + } else { + pixels[i] = maxValues[bandIdx]; + } + } + } + + i++; + } + } + } else { + if (scaleFactors.length > 1) { + for (int i = 0; i < pixels.length;) { + for (int bandIdx = 0; bandIdx < numBands; bandIdx++, i++) { + pixels[i] = pixels[i] * scaleFactors[bandIdx] + offsets[bandIdx]; + // Check for overflow now + if (((int)pixels[i] & masks[bandIdx]) != 0) { + if (pixels[i] < 0) { + pixels[i] = 0; + } else { + pixels[i] = maxValues[bandIdx]; + } + } + } + } + } else { + for (int i = 0; i < pixels.length;) { + for (int bandIdx = 0; bandIdx < numBands; bandIdx++, i++) { + pixels[i] = pixels[i] * scaleFactors[0] + offsets[0]; + // Check for overflow now + if (((int)pixels[i] & masks[bandIdx]) != 0) { + if (pixels[i] < 0) { + pixels[i] = 0; + } else { + pixels[i] = maxValues[bandIdx]; + } + } + } + } + } + } + + dst.setPixels(dstMinX, dstMinY, srcWidth, srcHeight, pixels); + + return 0; + } + + public final BufferedImage filter(BufferedImage src, BufferedImage dst) { + ColorModel srcCM = src.getColorModel(); + + if (srcCM instanceof IndexColorModel) { + // awt.220=Source should not have IndexColorModel + throw new IllegalArgumentException(Messages.getString("awt.220")); //$NON-NLS-1$ + } + + // Check if the number of scaling factors matches the number of bands + int nComponents = srcCM.getNumComponents(); + boolean skipAlpha; + if (srcCM.hasAlpha()) { + if (scaleFactors.length == 1 || scaleFactors.length == nComponents - 1) { + skipAlpha = true; + } else if (scaleFactors.length == nComponents) { + skipAlpha = false; + } else { + // awt.21E=Number of scaling constants is not equal to the + // number of bands + throw new IllegalArgumentException(Messages.getString("awt.21E")); //$NON-NLS-1$ + } + } else if (scaleFactors.length == 1 || scaleFactors.length == nComponents) { + skipAlpha = false; + } else { + // awt.21E=Number of scaling constants is not equal to the number of + // bands + throw new IllegalArgumentException(Messages.getString("awt.21E")); //$NON-NLS-1$ + } + + BufferedImage finalDst = null; + if (dst == null) { + finalDst = dst; + dst = createCompatibleDestImage(src, srcCM); + } else if (!srcCM.equals(dst.getColorModel())) { + // Treat BufferedImage.TYPE_INT_RGB and BufferedImage.TYPE_INT_ARGB + // as same + if (!((src.getType() == BufferedImage.TYPE_INT_RGB || src.getType() == BufferedImage.TYPE_INT_ARGB) && (dst + .getType() == BufferedImage.TYPE_INT_RGB || dst.getType() == BufferedImage.TYPE_INT_ARGB))) { + finalDst = dst; + dst = createCompatibleDestImage(src, srcCM); + } + } + + // TODO + // if (ippFilter(src.getRaster(), dst.getRaster(), src.getType(), + // skipAlpha) != 0) + if (slowFilter(src.getRaster(), dst.getRaster(), skipAlpha) != 0) { + // awt.21F=Unable to transform source + throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$ + } + + if (finalDst != null) { + Graphics2D g = finalDst.createGraphics(); + g.setComposite(AlphaComposite.Src); + g.drawImage(dst, 0, 0, null); + } else { + finalDst = dst; + } + + return finalDst; + } + + // Don't forget to pass allocated arrays for levels and values, size should + // be numBands*4 + /** + * Creates the levels. + * + * @param sm + * the sm. + * @param numBands + * the num bands. + * @param skipAlpha + * the skip alpha. + * @param levels + * the levels. + * @param values + * the values. + * @param channelsOrder + * the channels order. + */ + private final void createLevels(SampleModel sm, int numBands, boolean skipAlpha, int levels[], + int values[], int channelsOrder[]) { + // Suppose same sample size for all channels, otherwise use slow filter + int maxValue = (1 << sm.getSampleSize(0)) - 1; + + // For simplicity introduce these arrays + float extScaleFactors[] = new float[numBands]; + float extOffsets[] = new float[numBands]; + + if (scaleFactors.length != 1) { + System.arraycopy(scaleFactors, 0, extScaleFactors, 0, scaleFactors.length); + System.arraycopy(offsets, 0, extOffsets, 0, scaleFactors.length); + } else { + for (int i = 0; i < numBands; i++) { + extScaleFactors[i] = scaleFactors[0]; + extOffsets[i] = offsets[0]; + } + } + + if (skipAlpha) { + extScaleFactors[numBands - 1] = 1; + extOffsets[numBands - 1] = 0; + } + + // Create a levels + for (int i = 0; i < numBands; i++) { + if (extScaleFactors[i] == 0) { + levels[i * 4] = 0; + levels[i * 4 + 1] = 0; + levels[i * 4 + 2] = maxValue + 1; + levels[i * 4 + 3] = maxValue + 1; + } + + float minLevel = -extOffsets[i] / extScaleFactors[i]; + float maxLevel = (maxValue - extOffsets[i]) / extScaleFactors[i]; + + if (minLevel < 0) { + minLevel = 0; + } else if (minLevel > maxValue) { + minLevel = maxValue; + } + + if (maxLevel < 0) { + maxLevel = 0; + } else if (maxLevel > maxValue) { + maxLevel = maxValue; + } + + levels[i * 4] = 0; + if (minLevel > maxLevel) { + levels[i * 4 + 1] = (int)maxLevel; + levels[i * 4 + 2] = (int)minLevel; + } else { + levels[i * 4 + 1] = (int)minLevel; + levels[i * 4 + 2] = (int)maxLevel; + } + levels[i * 4 + 3] = maxValue + 1; + + // Fill values + for (int k = 0; k < 4; k++) { + int idx = i * 4 + k; + values[idx] = (int)(extScaleFactors[i] * levels[idx] + extOffsets[i]); + if (values[idx] < 0) { + values[idx] = 0; + } else if (values[idx] > maxValue) { + values[idx] = maxValue; + } + } + } + + // Reorder data if channels are stored in different order + if (channelsOrder != null) { + int len = numBands * 4; + int savedLevels[] = new int[len]; + int savedValues[] = new int[len]; + System.arraycopy(levels, 0, savedLevels, 0, len); + System.arraycopy(values, 0, savedValues, 0, len); + for (int i = 0; i < channelsOrder.length; i++) { + System.arraycopy(savedLevels, i * 4, levels, channelsOrder[i] * 4, 4); + System.arraycopy(savedValues, i * 4, values, channelsOrder[i] * 4, 4); + } + } + } + + // TODO remove when this method is used + /** + * Ipp filter. + * + * @param src + * the src. + * @param dst + * the dst. + * @param imageType + * the image type. + * @param skipAlpha + * the skip alpha. + * @return the int. + */ + @SuppressWarnings("unused") + private final int ippFilter(Raster src, WritableRaster dst, int imageType, boolean skipAlpha) { + int res; + + int srcStride, dstStride; + int channels; + int offsets[] = null; + int channelsOrder[] = null; + + switch (imageType) { + case BufferedImage.TYPE_INT_ARGB: + case BufferedImage.TYPE_INT_ARGB_PRE: + case BufferedImage.TYPE_INT_RGB: { + channels = 4; + srcStride = src.getWidth() * 4; + dstStride = dst.getWidth() * 4; + channelsOrder = new int[] { + 2, 1, 0, 3 + }; + break; + } + + case BufferedImage.TYPE_4BYTE_ABGR: + case BufferedImage.TYPE_4BYTE_ABGR_PRE: + case BufferedImage.TYPE_INT_BGR: { + channels = 4; + srcStride = src.getWidth() * 4; + dstStride = dst.getWidth() * 4; + break; + } + + case BufferedImage.TYPE_BYTE_GRAY: { + channels = 1; + srcStride = src.getWidth(); + dstStride = dst.getWidth(); + break; + } + + case BufferedImage.TYPE_3BYTE_BGR: { + channels = 3; + srcStride = src.getWidth() * 3; + dstStride = dst.getWidth() * 3; + channelsOrder = new int[] { + 2, 1, 0 + }; + break; + } + + case BufferedImage.TYPE_USHORT_GRAY: + case BufferedImage.TYPE_USHORT_565_RGB: + case BufferedImage.TYPE_USHORT_555_RGB: + case BufferedImage.TYPE_BYTE_BINARY: { + return slowFilter(src, dst, skipAlpha); + } + + default: { + SampleModel srcSM = src.getSampleModel(); + SampleModel dstSM = dst.getSampleModel(); + + if (srcSM instanceof PixelInterleavedSampleModel + && dstSM instanceof PixelInterleavedSampleModel) { + // Check PixelInterleavedSampleModel + if (srcSM.getDataType() != DataBuffer.TYPE_BYTE + || dstSM.getDataType() != DataBuffer.TYPE_BYTE) { + return slowFilter(src, dst, skipAlpha); + } + + channels = srcSM.getNumBands(); // Have IPP functions for 1, + // 3 and 4 channels + if (!(channels == 1 || channels == 3 || channels == 4)) { + return slowFilter(src, dst, skipAlpha); + } + + srcStride = ((ComponentSampleModel)srcSM).getScanlineStride(); + dstStride = ((ComponentSampleModel)dstSM).getScanlineStride(); + + channelsOrder = ((ComponentSampleModel)srcSM).getBandOffsets(); + } else if (srcSM instanceof SinglePixelPackedSampleModel + && dstSM instanceof SinglePixelPackedSampleModel) { + // Check SinglePixelPackedSampleModel + SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel)srcSM; + SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel)dstSM; + + channels = sppsm1.getNumBands(); + + // TYPE_INT_RGB, TYPE_INT_ARGB... + if (sppsm1.getDataType() != DataBuffer.TYPE_INT + || sppsm2.getDataType() != DataBuffer.TYPE_INT + || !(channels == 3 || channels == 4)) { + return slowFilter(src, dst, skipAlpha); + } + + // Check compatibility of sample models + if (!Arrays.equals(sppsm1.getBitOffsets(), sppsm2.getBitOffsets()) + || !Arrays.equals(sppsm1.getBitMasks(), sppsm2.getBitMasks())) { + return slowFilter(src, dst, skipAlpha); + } + + for (int i = 0; i < channels; i++) { + if (sppsm1.getSampleSize(i) != 8) { + return slowFilter(src, dst, skipAlpha); + } + } + + channelsOrder = new int[channels]; + int bitOffsets[] = sppsm1.getBitOffsets(); + for (int i = 0; i < channels; i++) { + channelsOrder[i] = bitOffsets[i] / 8; + } + + if (channels == 3) { // Don't skip channel now, could be + // optimized + channels = 4; + } + + srcStride = sppsm1.getScanlineStride() * 4; + dstStride = sppsm2.getScanlineStride() * 4; + } else { + return slowFilter(src, dst, skipAlpha); + } + + // Fill offsets if there's a child raster + if (src.getParent() != null || dst.getParent() != null) { + if (src.getSampleModelTranslateX() != 0 || src.getSampleModelTranslateY() != 0 + || dst.getSampleModelTranslateX() != 0 + || dst.getSampleModelTranslateY() != 0) { + offsets = new int[4]; + offsets[0] = -src.getSampleModelTranslateX() + src.getMinX(); + offsets[1] = -src.getSampleModelTranslateY() + src.getMinY(); + offsets[2] = -dst.getSampleModelTranslateX() + dst.getMinX(); + offsets[3] = -dst.getSampleModelTranslateY() + dst.getMinY(); + } + } + } + } + + int levels[] = new int[4 * channels]; + int values[] = new int[4 * channels]; + + createLevels(src.getSampleModel(), channels, skipAlpha, levels, values, channelsOrder); + + Object srcData, dstData; + AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance(); + try { + srcData = dbAccess.getData(src.getDataBuffer()); + dstData = dbAccess.getData(dst.getDataBuffer()); + } catch (IllegalArgumentException e) { + return -1; // Unknown data buffer type + } + + res = LookupOp.ippLUT(srcData, src.getWidth(), src.getHeight(), srcStride, dstData, dst + .getWidth(), dst.getHeight(), dstStride, levels, values, channels, offsets, true); + + return res; + } +} diff --git a/awt/java/awt/image/SampleModel.java b/awt/java/awt/image/SampleModel.java new file mode 100644 index 000000000..c967fa6e9 --- /dev/null +++ b/awt/java/awt/image/SampleModel.java @@ -0,0 +1,1166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The SampleModel class is abstract class for retrieving pixel's samples in the + * data of an image. Each pixel contains several samples. A sample is the set of + * values of the bands for single pixel. For example, each pixel in the RGB + * model contains three samples and there are three corresponding bands in the + * image data of such pixels representing red, green and blue components. + *

+ * The image data is represented as a Raster with a DataBuffer and a + * SampleModel. The SampleModel allows access to the samples in the DataBuffer. + * + * @since Android 1.0 + */ +public abstract class SampleModel { + + /** + * The width of the image data which this SampleModel describes. + */ + protected int width; + + /** + * The height of the image data which this SampleModel describes. + */ + protected int height; + + /** + * The number of bands of image data which this SampleModel describes. + */ + protected int numBands; + + /** + * The data type of the image data which this SampleModel describes. + */ + protected int dataType; + + /** + * Instantiates a new SampleModel with the specified data type, width, + * height and number of bands. + * + * @param dataType + * the data type of the image data. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param numBands + * the number of bands of the image data. + */ + public SampleModel(int dataType, int w, int h, int numBands) { + if (w <= 0 || h <= 0) { + // awt.22E=w or h is less than or equal to zero + throw new IllegalArgumentException(Messages.getString("awt.22E")); //$NON-NLS-1$ + } + + double squre = ((double)w) * ((double)h); + if (squre >= Integer.MAX_VALUE) { + // awt.22F=The product of w and h is greater than Integer.MAX_VALUE + throw new IllegalArgumentException(Messages.getString("awt.22F")); //$NON-NLS-1$ + } + + if (dataType < DataBuffer.TYPE_BYTE || dataType > DataBuffer.TYPE_DOUBLE + && dataType != DataBuffer.TYPE_UNDEFINED) { + // awt.230=dataType is not one of the supported data types + throw new IllegalArgumentException(Messages.getString("awt.230")); //$NON-NLS-1$ + } + + if (numBands < 1) { + // awt.231=Number of bands must be more then 0 + throw new IllegalArgumentException(Messages.getString("awt.231")); //$NON-NLS-1$ + } + + this.dataType = dataType; + this.width = w; + this.height = h; + this.numBands = numBands; + + } + + /** + * Gets the data array for the specified pixel of the specified DataBuffer + * with one of the following types: DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, + * DataBuffer.TYPE_FLOAT, or DataBuffer.TYPE_DOUBLE. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param obj + * the Object is a data where the result will be stored. + * @param data + * the image data. + * @return the data array for the specified pixel of the specified + * DataBuffer. + */ + public abstract Object getDataElements(int x, int y, Object obj, DataBuffer data); + + /** + * Gets the array of pixel data for the specified rectangular area of pixels + * of the specified DataBuffer with one of the following types: + * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, + * DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, or DataBuffer.TYPE_DOUBLE. + * + * @param x + * the X coordinate of the rectangular pixel area. + * @param y + * the Y coordinate of the rectangular pixel area. + * @param w + * the width of the rectangular pixel area. + * @param h + * the height of the rectangular pixel area. + * @param obj + * the Object is an array with the primitive type, where the + * result array will be stored. + * @param data + * the image data. + * @return the array of pixel data for the specified rectangular area of + * pixels of the specified DataBuffer object. + */ + public Object getDataElements(int x, int y, int w, int h, Object obj, DataBuffer data) { + int numDataElements = getNumDataElements(); + int idx = 0; + + switch (getTransferType()) { + case DataBuffer.TYPE_BYTE: + byte bdata[]; + byte bbuf[] = null; + + if (obj == null) { + bdata = new byte[numDataElements * w * h]; + } else { + bdata = (byte[])obj; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + bbuf = (byte[])getDataElements(j, i, bbuf, data); + for (int n = 0; n < numDataElements; n++) { + bdata[idx++] = bbuf[n]; + } + } + } + obj = bdata; + break; + + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + short sdata[]; + short sbuf[] = null; + + if (obj == null) { + sdata = new short[numDataElements * w * h]; + } else { + sdata = (short[])obj; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + sbuf = (short[])getDataElements(j, i, sbuf, data); + for (int n = 0; n < numDataElements; n++) { + sdata[idx++] = sbuf[n]; + } + } + } + obj = sdata; + break; + + case DataBuffer.TYPE_INT: + int idata[]; + int ibuf[] = null; + + if (obj == null) { + idata = new int[numDataElements * w * h]; + } else { + idata = (int[])obj; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + ibuf = (int[])getDataElements(j, i, ibuf, data); + for (int n = 0; n < numDataElements; n++) { + idata[idx++] = ibuf[n]; + } + } + } + obj = idata; + break; + + case DataBuffer.TYPE_FLOAT: + float fdata[]; + float fbuf[] = null; + + if (obj == null) { + fdata = new float[numDataElements * w * h]; + } else { + fdata = (float[])obj; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + fbuf = (float[])getDataElements(j, i, fbuf, data); + for (int n = 0; n < numDataElements; n++) { + fdata[idx++] = fbuf[n]; + } + } + } + obj = fdata; + break; + + case DataBuffer.TYPE_DOUBLE: + double ddata[]; + double dbuf[] = null; + + if (obj == null) { + ddata = new double[numDataElements * w * h]; + } else { + ddata = (double[])obj; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + dbuf = (double[])getDataElements(j, i, dbuf, data); + for (int n = 0; n < numDataElements; n++) { + ddata[idx++] = dbuf[n]; + } + } + } + obj = ddata; + break; + + } + + return obj; + } + + /** + * Sets the data for a single pixel in the specified DataBuffer from a + * primitive array with one of the following types: DataBuffer.TYPE_BYTE, + * DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, + * DataBuffer.TYPE_FLOAT, or DataBuffer.TYPE_DOUBLE. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param obj + * the Object - the array of primitive pixel data to be set. + * @param data + * the image data. + */ + public abstract void setDataElements(int x, int y, Object obj, DataBuffer data); + + /** + * Sets the data elements for a rectangular area of pixels in the specified + * DataBuffer from a primitive array with one of the following types: + * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, + * DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, or DataBuffer.TYPE_DOUBLE. + * + * @param x + * the X coordinate of the specified rectangular area. + * @param y + * the Y coordinate of the specified rectangular area. + * @param w + * the width of rectangle. + * @param h + * the height of rectangle. + * @param obj + * the Object - the array of primitive pixel data to be set. + * @param data + * the image data. + */ + public void setDataElements(int x, int y, int w, int h, Object obj, DataBuffer data) { + int numDataElements = getNumDataElements(); + int idx = 0; + + switch (getTransferType()) { + case DataBuffer.TYPE_BYTE: + byte bbuf[] = new byte[numDataElements]; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numDataElements; n++) { + bbuf[n] = ((byte[])obj)[idx++]; + } + setDataElements(j, i, bbuf, data); + } + } + + break; + + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + short sbuf[] = new short[numDataElements]; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numDataElements; n++) { + sbuf[n] = ((short[])obj)[idx++]; + } + setDataElements(j, i, sbuf, data); + } + } + break; + + case DataBuffer.TYPE_INT: + int ibuf[] = new int[numDataElements]; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numDataElements; n++) { + ibuf[n] = ((int[])obj)[idx++]; + } + setDataElements(j, i, ibuf, data); + } + } + break; + + case DataBuffer.TYPE_FLOAT: + float fbuf[] = new float[numDataElements]; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numDataElements; n++) { + fbuf[n] = ((float[])obj)[idx++]; + } + setDataElements(j, i, fbuf, data); + } + } + break; + + case DataBuffer.TYPE_DOUBLE: + double dbuf[] = new double[numDataElements]; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numDataElements; n++) { + dbuf[n] = ((double[])obj)[idx++]; + } + setDataElements(j, i, dbuf, data); + } + } + break; + + } + } + + /** + * Creates a new SampleModel with the specified bands of this SampleModel. + * + * @param bands + * the array of bands from this SampleModel. + * @return the SampleModel with the specified bands of this SampleModel. + */ + public abstract SampleModel createSubsetSampleModel(int bands[]); + + /** + * Creates the SampleModel which has the same data as in this SampleModel + * with a different width and height. + * + * @param a0 + * the width of the image data. + * @param a1 + * the height of the image data. + * @return the SampleModel which has the same data as in this SampleModel + * with a different width and height. + */ + public abstract SampleModel createCompatibleSampleModel(int a0, int a1); + + /** + * Gets the samples of the specified pixel as an integer array. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param iArray + * the integer array where result will be stored. + * @param data + * the image data. + * @return the integer array with the samples of the specified pixel. + */ + public int[] getPixel(int x, int y, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int pixel[]; + + if (iArray == null) { + pixel = new int[numBands]; + } else { + pixel = iArray; + } + + for (int i = 0; i < numBands; i++) { + pixel[i] = getSample(x, y, i, data); + } + + return pixel; + } + + /** + * Sets a pixel of the DataBuffer from a integer array of samples. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param iArray + * the integer array. + * @param data + * the image data. + */ + public void setPixel(int x, int y, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, iArray[i], data); + } + } + + /** + * Gets the samples of the specified pixel as a float array. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param fArray + * the float array where result will be stored. + * @param data + * the image data. + * @return the float array with the samples of the specified pixel. + */ + public float[] getPixel(int x, int y, float fArray[], DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + float pixel[]; + + if (fArray == null) { + pixel = new float[numBands]; + } else { + pixel = fArray; + } + + for (int i = 0; i < numBands; i++) { + pixel[i] = getSampleFloat(x, y, i, data); + } + + return pixel; + } + + /** + * Sets a pixel of the DataBuffer from a float array of samples. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param fArray + * the float array. + * @param data + * the image data. + */ + public void setPixel(int x, int y, float fArray[], DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, fArray[i], data); + } + } + + /** + * Gets the samples of the specified pixel as a double array. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param dArray + * the double array where result will be stored. + * @param data + * the image data. + * @return the double array with the samples of the specified pixel. + */ + public double[] getPixel(int x, int y, double dArray[], DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + double pixel[]; + + if (dArray == null) { + pixel = new double[numBands]; + } else { + pixel = dArray; + } + + for (int i = 0; i < numBands; i++) { + pixel[i] = getSampleDouble(x, y, i, data); + } + + return pixel; + } + + /** + * Sets a pixel of the DataBuffer from a double array of samples. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param dArray + * the double array. + * @param data + * the image data. + */ + public void setPixel(int x, int y, double dArray[], DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + for (int i = 0; i < numBands; i++) { + setSample(x, y, i, dArray[i], data); + } + } + + /** + * Gets the sample of a specified band for the specified pixel as an + * integer. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param b + * the specified band. + * @param data + * the image data. + * @return the sample of a specified band for the specified pixel. + */ + public abstract int getSample(int x, int y, int b, DataBuffer data); + + /** + * Gets the sample of a specified band for the specified pixel as a float. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param b + * the specified band. + * @param data + * the image data. + * @return the sample of a specified band for the specified pixel. + */ + public float getSampleFloat(int x, int y, int b, DataBuffer data) { + return getSample(x, y, b, data); + } + + /** + * Gets the sample of a specified band for the specified pixel as a double. + * + * @param x + * the X coordinate of pixel. + * @param y + * the Y coordinate of pixel. + * @param b + * the specified band. + * @param data + * the image data. + * @return the sample of a specified band for the specified pixel. + */ + public double getSampleDouble(int x, int y, int b, DataBuffer data) { + return getSample(x, y, b, data); + } + + /** + * Gets the samples of the specified rectangular area of pixels as an + * integer array. + * + * @param x + * the X coordinate of the rectangle of pixels. + * @param y + * the Y coordinate of the rectangle of pixels. + * @param w + * the width of the rectangle of pixels. + * @param h + * the height of the rectangle of pixels. + * @param iArray + * the integer array where result will be stored. + * @param data + * the image data. + * @return the integer array with the samples of the specified rectangular + * area of pixels. + */ + public int[] getPixels(int x, int y, int w, int h, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int pixels[]; + int idx = 0; + + if (iArray == null) { + pixels = new int[w * h * numBands]; + } else { + pixels = iArray; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numBands; n++) { + pixels[idx++] = getSample(j, i, n, data); + } + } + } + return pixels; + } + + /** + * Sets all of the samples for a rectangular area of pixels of the + * DataBuffer from an integer array. + * + * @param x + * the X coordinate of the rectangle of pixels. + * @param y + * the Y coordinate of the rectangle of pixels. + * @param w + * the width of the rectangle of pixels. + * @param h + * the height of the rectangle of pixels. + * @param iArray + * the integer array. + * @param data + * the image data. + */ + public void setPixels(int x, int y, int w, int h, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int idx = 0; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numBands; n++) { + setSample(j, i, n, iArray[idx++], data); + } + } + } + } + + /** + * Gets the samples of the specified rectangular area of pixels as a float + * array. + * + * @param x + * the X coordinate of the rectangle of pixels. + * @param y + * the Y coordinate of the rectangle of pixels. + * @param w + * the width of the rectangle of pixels. + * @param h + * the height of the rectangle of pixels. + * @param fArray + * the float array where result will be stored. + * @param data + * the image data. + * @return the float array with the samples of the specified rectangular + * area of pixels. + */ + public float[] getPixels(int x, int y, int w, int h, float fArray[], DataBuffer data) { + if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + float pixels[]; + int idx = 0; + + if (fArray == null) { + pixels = new float[w * h * numBands]; + } else { + pixels = fArray; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numBands; n++) { + pixels[idx++] = getSampleFloat(j, i, n, data); + } + } + } + return pixels; + } + + /** + * Sets all of the samples for a rectangular area of pixels of the + * DataBuffer from a float array. + * + * @param x + * the X coordinate of the rectangle of pixels. + * @param y + * the Y coordinate of the rectangle of pixels. + * @param w + * the width of the rectangle of pixels. + * @param h + * the height of the rectangle of pixels. + * @param fArray + * the float array. + * @param data + * the image data. + */ + public void setPixels(int x, int y, int w, int h, float fArray[], DataBuffer data) { + if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int idx = 0; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numBands; n++) { + setSample(j, i, n, fArray[idx++], data); + } + } + } + } + + /** + * Gets the samples of the specified rectangular area of pixels as a double + * array. + * + * @param x + * the X coordinate of the rectangle of pixels. + * @param y + * the Y coordinate of the rectangle of pixels. + * @param w + * the width of the rectangle of pixels. + * @param h + * the height of the rectangle of pixels. + * @param dArray + * the double array where result will be stored. + * @param data + * the image data. + * @return the double array with the samples of the specified rectangular + * area of pixels. + */ + public double[] getPixels(int x, int y, int w, int h, double dArray[], DataBuffer data) { + if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + double pixels[]; + int idx = 0; + + if (dArray == null) { + pixels = new double[w * h * numBands]; + } else { + pixels = dArray; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numBands; n++) { + pixels[idx++] = getSampleDouble(j, i, n, data); + } + } + } + return pixels; + } + + /** + * Sets all of the samples for a rectangular area of pixels of the + * DataBuffer from a double array. + * + * @param x + * the X coordinate of the rectangle of pixels. + * @param y + * the Y coordinate of the rectangle of pixels. + * @param w + * the width of the rectangle of pixels. + * @param h + * the height of the rectangle of pixels. + * @param dArray + * the double array. + * @param data + * the image data. + */ + public void setPixels(int x, int y, int w, int h, double dArray[], DataBuffer data) { + if (x < 0 || y < 0 || x + w > this.width || y + h > this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int idx = 0; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < numBands; n++) { + setSample(j, i, n, dArray[idx++], data); + } + } + } + } + + /** + * Sets a sample of the specified band for the specified pixel in the + * DataBuffer as integer value. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param b + * the specified band. + * @param s + * the sample as an integer value. + * @param data + * the image data. + */ + public abstract void setSample(int x, int y, int b, int s, DataBuffer data); + + /** + * Gets the samples of a specified band for a specified rectangular area of + * pixels as a integer array. + * + * @param x + * the X coordinate of the rectangle. + * @param y + * the Y coordinate of the rectangle. + * @param w + * the width of the rectangle. + * @param h + * the height of the rectangle. + * @param b + * the specified band. + * @param iArray + * the integer array where result will be stored. + * @param data + * the image data. + * @return the samples of a specified band for a specified rectangular area + * of pixels. + */ + public int[] getSamples(int x, int y, int w, int h, int b, int iArray[], DataBuffer data) { + int samples[]; + int idx = 0; + + if (iArray == null) { + samples = new int[w * h]; + } else { + samples = iArray; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + samples[idx++] = getSample(j, i, b, data); + } + } + + return samples; + } + + /** + * Sets the samples from an integer array in the specified band for the + * specified rectangle of pixels. + * + * @param x + * the X coordinate of the rectangle. + * @param y + * the Y coordinate of the rectangle. + * @param w + * the width of the rectangle. + * @param h + * the height of the rectangle. + * @param b + * the specified band. + * @param iArray + * the integer array. + * @param data + * the image data. + */ + public void setSamples(int x, int y, int w, int h, int b, int iArray[], DataBuffer data) { + int idx = 0; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + setSample(j, i, b, iArray[idx++], data); + } + } + } + + /** + * Gets the samples of a specified band for a specified rectangular area of + * pixels as a float array. + * + * @param x + * the X coordinate of the rectangle. + * @param y + * the Y coordinate of the rectangle. + * @param w + * the width of the rectangle. + * @param h + * the height of the rectangle. + * @param b + * the specified band. + * @param fArray + * the float array where result will be stored. + * @param data + * the image data. + * @return the samples of a specified band for a specified rectangular area + * of pixels. + */ + public float[] getSamples(int x, int y, int w, int h, int b, float fArray[], DataBuffer data) { + float samples[]; + int idx = 0; + + if (fArray == null) { + samples = new float[w * h]; + } else { + samples = fArray; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + samples[idx++] = getSampleFloat(j, i, b, data); + } + } + + return samples; + } + + /** + * Sets the samples from an float array in the specified band for the + * specified rectangle of pixels. + * + * @param x + * the X coordinate of the rectangle. + * @param y + * the Y coordinate of the rectangle. + * @param w + * the width of the rectangle. + * @param h + * the height of the rectangle. + * @param b + * the specified band. + * @param fArray + * the float array. + * @param data + * the image data. + */ + public void setSamples(int x, int y, int w, int h, int b, float fArray[], DataBuffer data) { + int idx = 0; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + setSample(j, i, b, fArray[idx++], data); + } + } + } + + /** + * Gets the samples of a specified band for a specified rectangular area of + * pixels as a double array. + * + * @param x + * the X coordinate of the rectangle. + * @param y + * the Y coordinate of the rectangle. + * @param w + * the width of the rectangle. + * @param h + * the height of the rectangle. + * @param b + * the specified band. + * @param dArray + * the double array where result will be stored. + * @param data + * the image data. + * @return the samples of a specified band for a specified rectangular area + * of pixels. + */ + public double[] getSamples(int x, int y, int w, int h, int b, double dArray[], DataBuffer data) { + double samples[]; + int idx = 0; + + if (dArray == null) { + samples = new double[w * h]; + } else { + samples = dArray; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + samples[idx++] = getSampleDouble(j, i, b, data); + } + } + + return samples; + } + + /** + * Sets the samples from an double array in the specified band for the + * specified rectangle of pixels. + * + * @param x + * the X coordinate of the rectangle. + * @param y + * the Y coordinate of the rectangle. + * @param w + * the width of the rectangle. + * @param h + * the height of the rectangle. + * @param b + * the specified band. + * @param dArray + * the double array. + * @param data + * the image data. + */ + public void setSamples(int x, int y, int w, int h, int b, double dArray[], DataBuffer data) { + int idx = 0; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + setSample(j, i, b, dArray[idx++], data); + } + } + } + + /** + * Sets a sample of the specified band for the specified pixel in the + * DataBuffer as float value. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param b + * the specified band. + * @param s + * the sample as float value. + * @param data + * the image data. + */ + public void setSample(int x, int y, int b, float s, DataBuffer data) { + setSample(x, y, b, (int)s, data); + } + + /** + * Sets a sample of the specified band for the specified pixel in the + * DataBuffer as double value. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param b + * the specified band. + * @param s + * the sample as double value. + * @param data + * the image data. + */ + public void setSample(int x, int y, int b, double s, DataBuffer data) { + setSample(x, y, b, (int)s, data); + } + + /** + * Creates a DataBuffer object which corresponds to the SampleModel. + * + * @return the DataBuffer object which corresponds to the SampleModel. + */ + public abstract DataBuffer createDataBuffer(); + + /** + * Gets the sample size in bits for the specified band. + * + * @param band + * the specified band. + * @return the sample size in bits for the specified band. + */ + public abstract int getSampleSize(int band); + + /** + * Gets an array of the sample size in bits for all bands. + * + * @return an array of the sample size in bits for all bands. + */ + public abstract int[] getSampleSize(); + + /** + * Gets the width of the image data of this SampleModel object. + * + * @return the width of the image data of this SampleModel object. + */ + public final int getWidth() { + return width; + } + + /** + * Gets the transfer type used to transfer pixels via the getDataElements + * and setDataElements methods. Transfer type value can be one of the + * predefined type from DataBuffer class or not. + * + * @return the transfer type. + */ + public int getTransferType() { + return dataType; + } + + /** + * Returns the number of data elements for pixel transferring via the + * getDataElements and setDataElements methods. + * + * @return the number of data elements for pixel transferring via the + * getDataElements and setDataElements methods. + */ + public abstract int getNumDataElements(); + + /** + * Gets the number of bands in the image data of this SampleModel object. + * + * @return the number of bands in the image data of this SampleModel object. + */ + public final int getNumBands() { + return numBands; + } + + /** + * Gets the height of the image data of this SampleModel object. + * + * @return the height of the image data of this SampleModel object. + */ + public final int getHeight() { + return height; + } + + /** + * Gets the data type of image data of this SampleModel object. + * + * @return the data type of image data of this SampleModel object. + */ + public final int getDataType() { + return dataType; + } + +} diff --git a/awt/java/awt/image/ShortLookupTable.java b/awt/java/awt/image/ShortLookupTable.java new file mode 100644 index 000000000..4319d5805 --- /dev/null +++ b/awt/java/awt/image/ShortLookupTable.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Oct 14, 2005 + */ + +package java.awt.image; + +/** + * The ShortLookupTable class provides provides functionality for lookup + * operations, and is defined by an input short array for bands or components of + * image and an offset value. The offset value will be subtracted from the input + * values before indexing the input arrays. The output of a lookup operation is + * represented as an unsigned short array. + * + * @since Android 1.0 + */ +public class ShortLookupTable extends LookupTable { + + /** + * The data. + */ + private short data[][]; + + /** + * Instantiates a new ShortLookupTable with the specified offset value and + * the specified short array which represents lookup table for all bands. + * + * @param offset + * the offset value. + * @param data + * the data array. + */ + public ShortLookupTable(int offset, short[] data) { + super(offset, 1); + this.data = new short[1][data.length]; + // The data array stored as a reference + this.data[0] = data; + } + + /** + * Instantiates a new ShortLookupTable with the specified offset value and + * the specified short array of arrays which represents lookup table for + * each band. + * + * @param offset + * the offset value. + * @param data + * the data array of arrays for each band. + */ + public ShortLookupTable(int offset, short[][] data) { + super(offset, data.length); + this.data = new short[data.length][data[0].length]; + for (int i = 0; i < data.length; i++) { + // The data array for each band stored as a reference + this.data[i] = data[i]; + } + } + + /** + * Gets the lookup table of this ShortLookupTable object. If this + * ShortLookupTable object has one short array for all bands, the returned + * array length is one. + * + * @return the lookup table of this ShortLookupTable object. + */ + public final short[][] getTable() { + return data; + } + + /** + * Returns a short array which contains samples of the specified pixel which + * is translated with the lookup table of this ShortLookupTable object. The + * resulted array is stored to the dst array. + * + * @param src + * the source array. + * @param dst + * the destination array where the result can be stored. + * @return the short array of translated samples of a pixel. + */ + public short[] lookupPixel(short[] src, short[] dst) { + if (dst == null) { + dst = new short[src.length]; + } + + int offset = getOffset(); + if (getNumComponents() == 1) { + for (int i = 0; i < src.length; i++) { + dst[i] = data[0][src[i] - offset]; + } + } else { + for (int i = 0; i < getNumComponents(); i++) { + dst[i] = data[i][src[i] - offset]; + } + } + + return dst; + } + + @Override + public int[] lookupPixel(int[] src, int[] dst) { + if (dst == null) { + dst = new int[src.length]; + } + + int offset = getOffset(); + if (getNumComponents() == 1) { + for (int i = 0; i < src.length; i++) { + dst[i] = data[0][src[i] - offset]; + } + } else { + for (int i = 0; i < getNumComponents(); i++) { + dst[i] = data[i][src[i] - offset]; + } + } + + return dst; + } +} diff --git a/awt/java/awt/image/SinglePixelPackedSampleModel.java b/awt/java/awt/image/SinglePixelPackedSampleModel.java new file mode 100644 index 000000000..69f3353b3 --- /dev/null +++ b/awt/java/awt/image/SinglePixelPackedSampleModel.java @@ -0,0 +1,519 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.util.Arrays; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The SinglePixelPackedSampleModel class represents pixel data where several + * samples combine to create a single pixel and are stored in a single data + * array element. This class supports TYPE_BYTE, TYPE_USHORT, TYPE_INT data + * types. + * + * @since Android 1.0 + */ +public class SinglePixelPackedSampleModel extends SampleModel { + + /** + * The bit masks. + */ + private int bitMasks[]; + + /** + * The bit offsets. + */ + private int bitOffsets[]; + + /** + * The bit sizes. + */ + private int bitSizes[]; + + /** + * The scanline stride. + */ + private int scanlineStride; + + /** + * The max bit size. + */ + private int maxBitSize; + + /** + * Instantiates a new SinglePixelPackedSampleModel with the specified + * parameters. + * + * @param dataType + * the data type of samples. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param bitMasks + * the bit masks for all the bands. + */ + public SinglePixelPackedSampleModel(int dataType, int w, int h, int bitMasks[]) { + this(dataType, w, h, w, bitMasks); + } + + /** + * Instantiates a new SinglePixelPackedSampleModel with the specified + * parameters. + * + * @param dataType + * the data type of the samples. + * @param w + * the width of the image data. + * @param h + * the height of the image data. + * @param scanlineStride + * the scanline stride of the image data. + * @param bitMasks + * the bit masks for all the bands. + */ + public SinglePixelPackedSampleModel(int dataType, int w, int h, int scanlineStride, + int bitMasks[]) { + + super(dataType, w, h, bitMasks.length); + + if (dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_USHORT + && dataType != DataBuffer.TYPE_INT) { + // awt.61=Unsupported data type: {0} + throw new IllegalArgumentException(Messages.getString("awt.61", //$NON-NLS-1$ + dataType)); + } + + this.scanlineStride = scanlineStride; + this.bitMasks = bitMasks.clone(); + this.bitOffsets = new int[this.numBands]; + this.bitSizes = new int[this.numBands]; + + this.maxBitSize = 0; + + for (int i = 0; i < this.numBands; i++) { + int offset = 0; + int size = 0; + int mask = bitMasks[i]; + + if (mask != 0) { + while ((mask & 1) == 0) { + mask >>>= 1; + offset++; + } + + while ((mask & 1) == 1) { + mask >>>= 1; + size++; + } + + if (mask != 0) { + // awt.62=Wrong mask : {0} + throw new IllegalArgumentException(Messages.getString("awt.62", bitMasks[i])); //$NON-NLS-1$ + } + } + + this.bitOffsets[i] = offset; + this.bitSizes[i] = size; + + if (this.maxBitSize < size) { + this.maxBitSize = size; + } + + } + + } + + @Override + public Object getDataElements(int x, int y, Object obj, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + switch (getTransferType()) { + case DataBuffer.TYPE_BYTE: + byte bdata[]; + if (obj == null) { + bdata = new byte[1]; + } else { + bdata = (byte[])obj; + } + + bdata[0] = (byte)data.getElem(y * scanlineStride + x); + obj = bdata; + break; + case DataBuffer.TYPE_USHORT: + short sdata[]; + if (obj == null) { + sdata = new short[1]; + } else { + sdata = (short[])obj; + } + + sdata[0] = (short)data.getElem(y * scanlineStride + x); + obj = sdata; + break; + case DataBuffer.TYPE_INT: + int idata[]; + if (obj == null) { + idata = new int[1]; + } else { + idata = (int[])obj; + } + + idata[0] = data.getElem(y * scanlineStride + x); + obj = idata; + break; + } + return obj; + } + + @Override + public void setDataElements(int x, int y, Object obj, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + switch (getTransferType()) { + case DataBuffer.TYPE_BYTE: + data.setElem(y * scanlineStride + x, ((byte[])obj)[0] & 0xff); + break; + case DataBuffer.TYPE_USHORT: + data.setElem(y * scanlineStride + x, ((short[])obj)[0] & 0xffff); + break; + case DataBuffer.TYPE_INT: + data.setElem(y * scanlineStride + x, ((int[])obj)[0]); + break; + } + } + + /** + * Compares this SinglePixelPackedSampleModel object with the specified + * object. + * + * @param o + * the Object to be compared. + * @return true, if this SinglePixelPackedSampleModel object is equal to the + * specified object, false otherwise. + */ + @Override + public boolean equals(Object o) { + if ((o == null) || !(o instanceof SinglePixelPackedSampleModel)) { + return false; + } + + SinglePixelPackedSampleModel model = (SinglePixelPackedSampleModel)o; + return this.width == model.width && this.height == model.height + && this.numBands == model.numBands && this.dataType == model.dataType + && Arrays.equals(this.bitMasks, model.bitMasks) + && Arrays.equals(this.bitOffsets, model.bitOffsets) + && Arrays.equals(this.bitSizes, model.bitSizes) + && this.scanlineStride == model.scanlineStride; + } + + @Override + public SampleModel createSubsetSampleModel(int bands[]) { + if (bands.length > this.numBands) { + // awt.64=The number of the bands in the subset is greater than the + // number of bands in the sample model + throw new RasterFormatException(Messages.getString("awt.64")); //$NON-NLS-1$ + } + + int masks[] = new int[bands.length]; + for (int i = 0; i < bands.length; i++) { + masks[i] = this.bitMasks[bands[i]]; + } + return new SinglePixelPackedSampleModel(this.dataType, this.width, this.height, + this.scanlineStride, masks); + } + + @Override + public SampleModel createCompatibleSampleModel(int w, int h) { + return new SinglePixelPackedSampleModel(this.dataType, w, h, this.bitMasks); + } + + @Override + public int[] getPixel(int x, int y, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int pixel[]; + if (iArray == null) { + pixel = new int[this.numBands]; + } else { + pixel = iArray; + } + + for (int i = 0; i < this.numBands; i++) { + pixel[i] = getSample(x, y, i, data); + } + + return pixel; + } + + @Override + public void setPixel(int x, int y, int iArray[], DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + for (int i = 0; i < this.numBands; i++) { + setSample(x, y, i, iArray[i], data); + } + } + + @Override + public int getSample(int x, int y, int b, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int sample = data.getElem(y * scanlineStride + x); + return ((sample & this.bitMasks[b]) >>> this.bitOffsets[b]); + } + + @Override + public int[] getPixels(int x, int y, int w, int h, int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || ((long)x + (long)w > this.width) + || ((long)y + (long)h > this.height)) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + int pixels[]; + + if (iArray == null) { + pixels = new int[w * h * this.numBands]; + } else { + pixels = iArray; + } + + int idx = 0; + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < this.numBands; n++) { + pixels[idx++] = getSample(j, i, n, data); + } + } + } + return pixels; + } + + @Override + public void setPixels(int x, int y, int w, int h, int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || ((long)x + (long)w > this.width) + || ((long)y + (long)h > this.height)) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + int idx = 0; + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + for (int n = 0; n < this.numBands; n++) { + setSample(j, i, n, iArray[idx++], data); + } + } + } + } + + @Override + public void setSample(int x, int y, int b, int s, DataBuffer data) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + int tmp = data.getElem(y * scanlineStride + x); + tmp &= ~this.bitMasks[b]; + tmp |= (s << this.bitOffsets[b]) & this.bitMasks[b]; + data.setElem(y * scanlineStride + x, tmp); + } + + @Override + public int[] getSamples(int x, int y, int w, int h, int b, int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || ((long)x + (long)w > this.width) + || ((long)y + (long)h > this.height)) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + int samples[]; + int idx = 0; + + if (iArray == null) { + samples = new int[w * h]; + } else { + samples = iArray; + } + + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + samples[idx++] = getSample(j, i, b, data); + } + } + + return samples; + } + + @Override + public void setSamples(int x, int y, int w, int h, int b, int iArray[], DataBuffer data) { + if ((x < 0) || (y < 0) || ((long)x + (long)w > this.width) + || ((long)y + (long)h > this.height)) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + int idx = 0; + for (int i = y; i < y + h; i++) { + for (int j = x; j < x + w; j++) { + setSample(x + j, y + i, b, iArray[idx++], data); + } + } + } + + @Override + public DataBuffer createDataBuffer() { + DataBuffer data = null; + int size = (this.height - 1) * scanlineStride + width; + + switch (this.dataType) { + case DataBuffer.TYPE_BYTE: + data = new DataBufferByte(size); + break; + case DataBuffer.TYPE_USHORT: + data = new DataBufferUShort(size); + break; + case DataBuffer.TYPE_INT: + data = new DataBufferInt(size); + break; + } + return data; + } + + /** + * Gets the offset of the specified pixel in the data array. + * + * @param x + * the X coordinate of the specified pixel. + * @param y + * the Y coordinate of the specified pixel. + * @return the offset of the specified pixel. + */ + public int getOffset(int x, int y) { + return (y * scanlineStride + x); + } + + @Override + public int getSampleSize(int band) { + return bitSizes[band]; + } + + @Override + public int[] getSampleSize() { + return bitSizes.clone(); + } + + /** + * Gets an array of the bit offsets of the data array elements. + * + * @return an array of the bit offsets. + */ + public int[] getBitOffsets() { + return bitOffsets.clone(); + } + + /** + * Gets an array of the bit masks for all bands. + * + * @return an array of the bit masks for all bands. + */ + public int[] getBitMasks() { + return bitMasks.clone(); + } + + /** + * Returns a hash code of this MultiPixelPackedSampleModel class. + * + * @return the hash code of this MultiPixelPackedSampleModel class. + */ + @Override + public int hashCode() { + int hash = 0; + int tmp = 0; + + hash = width; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= height; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= numBands; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + hash ^= dataType; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + for (int element : bitMasks) { + hash ^= element; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + } + for (int element : bitOffsets) { + hash ^= element; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + } + for (int element : bitSizes) { + hash ^= element; + tmp = hash >>> 24; + hash <<= 8; + hash |= tmp; + } + hash ^= scanlineStride; + return hash; + } + + /** + * Gets the scanline stride. + * + * @return the scanline stride + */ + public int getScanlineStride() { + return this.scanlineStride; + } + + @Override + public int getNumDataElements() { + return 1; + } + +} diff --git a/awt/java/awt/image/TileObserver.java b/awt/java/awt/image/TileObserver.java new file mode 100644 index 000000000..7dd97e29b --- /dev/null +++ b/awt/java/awt/image/TileObserver.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +/** + * An asynchronous update interface for receiving notifications about tile + * information when tiles of a WritableRenderedImage become modifiable or + * unmodifiable. + * + * @since Android 1.0 + */ +public interface TileObserver { + + /** + * This method is called when information about a tile update is available. + * + * @param source + * the source image. + * @param tileX + * the X index of the tile. + * @param tileY + * the Y index of the tile. + * @param willBeWritable + * parameter which indicates whether the tile will be grabbed for + * writing or be released. + */ + public void tileUpdate(WritableRenderedImage source, int tileX, int tileY, + boolean willBeWritable); + +} diff --git a/awt/java/awt/image/VolatileImage.java b/awt/java/awt/image/VolatileImage.java new file mode 100644 index 000000000..f24e866bd --- /dev/null +++ b/awt/java/awt/image/VolatileImage.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.ImageCapabilities; +import java.awt.Transparency; + +/** + * The VolatileImage abstract class represents an image which can lose its + * contents at any point. VolatileImage objects are device specific. This class + * provides methods for checking if operation of this image are compatible for + * the GraphicsConfiguration. + * + * @since Android 1.0 + */ +public abstract class VolatileImage extends Image +// Volatile image implements Transparency since 1.5 + implements Transparency { + + /** + * The Constant IMAGE_INCOMPATIBLE indicates that this VolatileImage is not + * applicable for the GraphicsConfiguration object. + */ + public static final int IMAGE_INCOMPATIBLE = 2; + + /** + * The Constant IMAGE_OK indicates that VolatileImage is ready for using. + */ + public static final int IMAGE_OK = 0; + + /** + * The Constant IMAGE_RESTORED indicates that VolatileImage will be ready to + * use after restoring. + */ + public static final int IMAGE_RESTORED = 1; + + /** + * The transparency value of this image. + */ + protected int transparency = OPAQUE; + + /** + * Instantiates a new VolatileImage object. + */ + public VolatileImage() { + super(); + } + + /** + * Returns true if rendering data is lost during validating. This method + * should be called after rendering operation of image. + * + * @return true, if contents lost during validating, false otherwise. + */ + + public abstract boolean contentsLost(); + + /** + * Creates a Graphics2D used to draw in this VolatileImage. + * + * @return the Graphics2D object. + */ + public abstract Graphics2D createGraphics(); + + /** + * Gets the ImageCapabilities of this VolatileImage. + * + * @return the ImageCapabilities of this VolatileImage. + */ + public abstract ImageCapabilities getCapabilities(); + + /** + * Gets the height of this VolatileImage. + * + * @return the height of this VolatileImage. + */ + public abstract int getHeight(); + + /** + * Gets a BufferedImage representation of current VolatileImage that won't + * be affected by any changes to this VolatileImage. + * + * @return a BufferedImage representation of current VolatileImage. + */ + public abstract BufferedImage getSnapshot(); + + /** + * Gets the width of this VolatileImage. + * + * @return the width of this VolatileImage. + */ + public abstract int getWidth(); + + /** + * Validates the drawing surface of the image if the surface had been lost + * and if the specified GraphicsConfiguration object is applicable to this + * image. + * + * @param gc + * the GraphicsConfiguration object. + * @return one of the image status constants: IMAGE_OK, IMAGE_RESTORED or + * IMAGE_INCOMPATIBLE. + */ + public abstract int validate(GraphicsConfiguration gc); + + @Override + public void flush() { + } + + @Override + public Graphics getGraphics() { + return createGraphics(); + } + + @Override + public ImageProducer getSource() { + return getSnapshot().getSource(); + } + + public int getTransparency() { + return transparency; + } +} diff --git a/awt/java/awt/image/WritableRaster.java b/awt/java/awt/image/WritableRaster.java new file mode 100644 index 000000000..51366ee64 --- /dev/null +++ b/awt/java/awt/image/WritableRaster.java @@ -0,0 +1,592 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.Point; +import java.awt.Rectangle; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The WritableRaster class provides functionality for writing samples and pixel + * capabilities to the Raster. + * + * @since Android 1.0 + */ +public class WritableRaster extends Raster { + + /** + * Instantiates a new WritableRaster object with the specified SampleModel, + * DataBuffer, rectangular region and parent WritableRaster. + * + * @param sampleModel + * the specified SampleModel. + * @param dataBuffer + * the specified DataBuffer. + * @param aRegion + * the rectangular region which defines the new image bounds. + * @param sampleModelTranslate + * this point defines the translation point from the SampleModel + * to the new WritableRaster coordinates. + * @param parent + * the parent of this WritableRaster. + */ + protected WritableRaster(SampleModel sampleModel, DataBuffer dataBuffer, Rectangle aRegion, + Point sampleModelTranslate, WritableRaster parent) { + super(sampleModel, dataBuffer, aRegion, sampleModelTranslate, parent); + } + + /** + * Instantiates a new WritableRaster object with the specified SampleModel + * which defines a layout of this WritableRaster and DataBuffer objects + * which defines the image data. + * + * @param sampleModel + * the specified SampleModel. + * @param dataBuffer + * the specified DataBuffer. + * @param origin + * the point of origin. + */ + protected WritableRaster(SampleModel sampleModel, DataBuffer dataBuffer, Point origin) { + this(sampleModel, dataBuffer, new Rectangle(origin.x, origin.y, sampleModel.width, + sampleModel.height), origin, null); + } + + /** + * Instantiates a new WritableRaster with the specified SampleModel. + * + * @param sampleModel + * the specified SampleModel. + * @param origin + * the origin. + */ + protected WritableRaster(SampleModel sampleModel, Point origin) { + this(sampleModel, sampleModel.createDataBuffer(), new Rectangle(origin.x, origin.y, + sampleModel.width, sampleModel.height), origin, null); + } + + /** + * Sets the data for a single pixel from an input Object which represents an + * array of primitive types: DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, + * DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, or + * DataBuffer.TYPE_DOUBLE. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param inData + * the input data. + */ + public void setDataElements(int x, int y, Object inData) { + sampleModel.setDataElements(x - sampleModelTranslateX, y - sampleModelTranslateY, inData, + dataBuffer); + } + + /** + * Sets the data elements which represent pixel data to the specified + * rectangle area as a primitive array. The following image data types are + * supported: DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, + * DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, or + * DataBuffer.TYPE_DOUBLE. + * + * @param x + * the X coordinate of the rectangle of pixels. + * @param y + * the Y coordinate of the rectangle of pixels. + * @param w + * the width of the rectangle of pixels. + * @param h + * the height of the rectangle of pixels. + * @param inData + * the array of primitive type data to be set to the specified + * area. + */ + public void setDataElements(int x, int y, int w, int h, Object inData) { + sampleModel.setDataElements(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, + inData, dataBuffer); + } + + /** + * Creates the child of this WritableRaster by sharing the specified + * rectangular area in this WritableRaster. The parentX, parentY, width and + * height parameters specify rectangular area to be shared. + * + * @param parentX + * the X coordinate of the upper left corner of the shared + * rectangle with respect to this WritableRaster' coordinates. + * @param parentY + * the Y coordinate of the upper left corner of the shared + * rectangle with respect to this WritableRaster' coordinates. + * @param w + * the width of the child area. + * @param h + * the height of the child area. + * @param childMinX + * the X coordinate of child area mapped to the parentX + * coordinate. + * @param childMinY + * the Y coordinate of child area mapped to the parentY + * coordinate. + * @param bandList + * the array of band indices. + * @return the child WritableRaster. + */ + public WritableRaster createWritableChild(int parentX, int parentY, int w, int h, + int childMinX, int childMinY, int bandList[]) { + if (w <= 0 || h <= 0) { + // awt.244=Width or Height of child Raster is less than or equal to + // zero + throw new RasterFormatException(Messages.getString("awt.244")); //$NON-NLS-1$ + } + + if (parentX < this.minX || parentX + w > this.minX + this.width) { + // awt.245=parentX disposes outside Raster + throw new RasterFormatException(Messages.getString("awt.245")); //$NON-NLS-1$ + } + + if (parentY < this.minY || parentY + h > this.minY + this.height) { + // awt.246=parentY disposes outside Raster + throw new RasterFormatException(Messages.getString("awt.246")); //$NON-NLS-1$ + } + + if ((long)parentX + w > Integer.MAX_VALUE) { + // awt.247=parentX + w results in integer overflow + throw new RasterFormatException(Messages.getString("awt.247")); //$NON-NLS-1$ + } + + if ((long)parentY + h > Integer.MAX_VALUE) { + // awt.248=parentY + h results in integer overflow + throw new RasterFormatException(Messages.getString("awt.248")); //$NON-NLS-1$ + } + + if ((long)childMinX + w > Integer.MAX_VALUE) { + // awt.249=childMinX + w results in integer overflow + throw new RasterFormatException(Messages.getString("awt.249")); //$NON-NLS-1$ + } + + if ((long)childMinY + h > Integer.MAX_VALUE) { + // awt.24A=childMinY + h results in integer overflow + throw new RasterFormatException(Messages.getString("awt.24A")); //$NON-NLS-1$ + } + + SampleModel childModel; + + if (bandList == null) { + childModel = sampleModel; + } else { + childModel = sampleModel.createSubsetSampleModel(bandList); + } + + int childTranslateX = childMinX - parentX; + int childTranslateY = childMinY - parentY; + + return new WritableRaster(childModel, dataBuffer, + new Rectangle(childMinX, childMinY, w, h), new Point(childTranslateX + + sampleModelTranslateX, childTranslateY + sampleModelTranslateY), this); + } + + /** + * Creates the translated child of this WritableRaster. New WritableRaster + * object is a reference to the this WritableRaster and with different + * location. + * + * @param childMinX + * the X coordinate of the new WritableRaster. + * @param childMinY + * the Y coordinate of the new WritableRaster. + * @return the WritableRaster. + */ + public WritableRaster createWritableTranslatedChild(int childMinX, int childMinY) { + return createWritableChild(minX, minY, width, height, childMinX, childMinY, null); + } + + /** + * Gets the parent WritableRaster for this WritableRaster object. + * + * @return the parent WritableRaster for this WritableRaster object. + */ + public WritableRaster getWritableParent() { + return (WritableRaster)parent; + } + + /** + * Sets pixels from the specified source Raster srcRaster to this + * WritableRaster. + * + * @param srcRaster + * the source Raster. + */ + public void setRect(Raster srcRaster) { + setRect(0, 0, srcRaster); + } + + /** + * Sets pixels from the specified source Raster srcRaster to this + * WritableRaster. Each pixel with (x, y) coordinates from the source Raster + * is copied to pixel with (x+dx, y+dy) coordinates in this WritableRaster. + * The pixels with (x+dx, y+dy) coordinates which are out the bounds of this + * raster are ignored. + * + * @param dx + * the distance the pixel's X coordinate in the source Raster is + * translated when writtien to this WritableRaster. + * @param dy + * the distance the pixel's Y coordinate in the source Raster is + * translated when writtien to this WritableRaster. + * @param srcRaster + * the source Raster. + */ + public void setRect(int dx, int dy, Raster srcRaster) { + int w = srcRaster.getWidth(); + int h = srcRaster.getHeight(); + + int srcX = srcRaster.getMinX(); + int srcY = srcRaster.getMinY(); + + int dstX = srcX + dx; + int dstY = srcY + dy; + + if (dstX < this.minX) { + int minOffX = this.minX - dstX; + w -= minOffX; + dstX = this.minX; + srcX += minOffX; + } + + if (dstY < this.minY) { + int minOffY = this.minY - dstY; + h -= minOffY; + dstY = this.minY; + srcY += minOffY; + } + + if (dstX + w > this.minX + this.width) { + int maxOffX = (dstX + w) - (this.minX + this.width); + w -= maxOffX; + } + + if (dstY + h > this.minY + this.height) { + int maxOffY = (dstY + h) - (this.minY + this.height); + h -= maxOffY; + } + + if (w <= 0 || h <= 0) { + return; + } + + switch (sampleModel.getDataType()) { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: + int iPixelsLine[] = null; + for (int i = 0; i < h; i++) { + iPixelsLine = srcRaster.getPixels(srcX, srcY + i, w, 1, iPixelsLine); + setPixels(dstX, dstY + i, w, 1, iPixelsLine); + } + break; + + case DataBuffer.TYPE_FLOAT: + float fPixelsLine[] = null; + for (int i = 0; i < h; i++) { + fPixelsLine = srcRaster.getPixels(srcX, srcY + i, w, 1, fPixelsLine); + setPixels(dstX, dstY + i, w, 1, fPixelsLine); + } + break; + + case DataBuffer.TYPE_DOUBLE: + double dPixelsLine[] = null; + for (int i = 0; i < h; i++) { + dPixelsLine = srcRaster.getPixels(srcX, srcY + i, w, 1, dPixelsLine); + setPixels(dstX, dstY + i, w, 1, dPixelsLine); + } + break; + } + } + + /** + * Sets the data for a rectangle of pixels from an input Raster to this + * WritableRaster. + * + * @param x + * the X coordinate of the point where the data of the input + * Raster is to be written. + * @param y + * the Y coordinate of the point where the data of the input + * Raster is to be written. + * @param inRaster + * the input Raster. + */ + public void setDataElements(int x, int y, Raster inRaster) { + int dstX = x + inRaster.getMinX(); + int dstY = y + inRaster.getMinY(); + + int w = inRaster.getWidth(); + int h = inRaster.getHeight(); + + if (dstX < this.minX || dstX + w > this.minX + this.width || dstY < this.minY + || dstY + h > this.minY + this.height) { + // awt.63=Coordinates are not in bounds + throw new ArrayIndexOutOfBoundsException(Messages.getString("awt.63")); //$NON-NLS-1$ + } + + int srcX = inRaster.getMinX(); + int srcY = inRaster.getMinY(); + Object line = null; + + for (int i = 0; i < h; i++) { + line = inRaster.getDataElements(srcX, srcY + i, w, 1, line); + setDataElements(dstX, dstY + i, w, 1, line); + } + } + + /** + * Sets an integer array of samples for the specified pixel in this + * WritableRaster. + * + * @param x + * the pixel's X coordinate. + * @param y + * the pixel's Y coordinate. + * @param iArray + * the integer array of samples. + */ + public void setPixel(int x, int y, int iArray[]) { + sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, iArray, + dataBuffer); + } + + /** + * Sets a float array of samples for the specified pixel in this + * WritableRaster. + * + * @param x + * the pixel's X coordinate. + * @param y + * the pixel's Y coordinate. + * @param fArray + * the float array of samples. + */ + public void setPixel(int x, int y, float fArray[]) { + sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, fArray, + dataBuffer); + } + + /** + * Sets a double array of samples for the specified pixel in this + * WritableRaster. + * + * @param x + * the pixel's X coordinate. + * @param y + * the pixel's Y coordinate. + * @param dArray + * the double array of samples. + */ + public void setPixel(int x, int y, double dArray[]) { + sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, dArray, + dataBuffer); + } + + /** + * Sets a integer array of samples for the specified rectangular area of + * pixels in this WritableRaster. + * + * @param x + * the X coordinate of rectangular area. + * @param y + * the Y coordinate of rectangular area. + * @param w + * the width of rectangular area. + * @param h + * the height of rectangular area. + * @param iArray + * the integer array of samples. + */ + public void setPixels(int x, int y, int w, int h, int iArray[]) { + sampleModel.setPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, iArray, + dataBuffer); + } + + /** + * Sets a float array of samples for the specified rectangular area of + * pixels in this WritableRaster. + * + * @param x + * the X coordinate of rectangular area. + * @param y + * the Y coordinate of rectangular area. + * @param w + * the width of rectangular area. + * @param h + * the height of rectangular area. + * @param fArray + * the float array of samples. + */ + public void setPixels(int x, int y, int w, int h, float fArray[]) { + sampleModel.setPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, fArray, + dataBuffer); + } + + /** + * Sets a double array of samples for the specified rectangular area of + * pixels in this WritableRaster. + * + * @param x + * the X coordinate of rectangular area. + * @param y + * the Y coordinate of rectangular area. + * @param w + * the width of rectangular area. + * @param h + * the height of rectangular area. + * @param dArray + * the double array of samples. + */ + public void setPixels(int x, int y, int w, int h, double dArray[]) { + sampleModel.setPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, dArray, + dataBuffer); + } + + /** + * Sets the samples for the specified band and the specified rectangular + * area of pixels with an integer array of samples. + * + * @param x + * the X coordinate of the area of pixels. + * @param y + * the Y coordinate of the area of pixels. + * @param w + * the width of the area of pixels. + * @param h + * the height of the area of pixels. + * @param b + * the specified band. + * @param iArray + * the integer array of samples. + */ + public void setSamples(int x, int y, int w, int h, int b, int iArray[]) { + sampleModel.setSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, b, + iArray, dataBuffer); + } + + /** + * Sets the samples for the specified band and the specified rectangular + * area of pixels with a float array of samples. + * + * @param x + * the X coordinate of the area of pixels. + * @param y + * the Y coordinate of the area of pixels. + * @param w + * the width of the area of pixels. + * @param h + * the height of the area of pixels. + * @param b + * the specified band. + * @param fArray + * the float array of samples. + */ + public void setSamples(int x, int y, int w, int h, int b, float fArray[]) { + sampleModel.setSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, b, + fArray, dataBuffer); + } + + /** + * Sets the samples for the specified band and the specified rectangular + * area of pixels with a double array of samples. + * + * @param x + * the X coordinate of the area of pixels. + * @param y + * the Y coordinate of the area of pixels. + * @param w + * the width of the area of pixels. + * @param h + * the height of the area of pixels. + * @param b + * the specified band. + * @param dArray + * the double array of samples. + */ + public void setSamples(int x, int y, int w, int h, int b, double dArray[]) { + sampleModel.setSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, w, h, b, + dArray, dataBuffer); + } + + /** + * Sets the sample for the specified band and the specified pixel with an + * integer sample. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param b + * the specified band. + * @param s + * the sample to be set. + */ + public void setSample(int x, int y, int b, int s) { + sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, b, s, + dataBuffer); + } + + /** + * Sets the sample for the specified band and the specified pixel with a + * float sample. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param b + * the specified band. + * @param s + * the sample to be set. + */ + public void setSample(int x, int y, int b, float s) { + sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, b, s, + dataBuffer); + } + + /** + * Sets the sample for the specified band and the specified pixel with an + * integer sample. + * + * @param x + * the X coordinate of the pixel. + * @param y + * the Y coordinate of the pixel. + * @param b + * the specified band. + * @param s + * the sample to be set. + */ + public void setSample(int x, int y, int b, double s) { + sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, b, s, + dataBuffer); + } + +} diff --git a/awt/java/awt/image/WritableRenderedImage.java b/awt/java/awt/image/WritableRenderedImage.java new file mode 100644 index 000000000..052353b60 --- /dev/null +++ b/awt/java/awt/image/WritableRenderedImage.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image; + +import java.awt.Point; + +/** + * The WriteableRenderedImage interface is interface for objects which contains + * Raster data of one or several tiles. This interface provides notification + * mechanism for obtaining tile's writing status. + * + * @since Android 1.0 + */ +public interface WritableRenderedImage extends RenderedImage { + + /** + * Gets and checks out the writable tile for writing. + * + * @param tileX + * the X index of the tile. + * @param tileY + * the Y index of the tile. + * @return the WritableRaster. + */ + public WritableRaster getWritableTile(int tileX, int tileY); + + /** + * Removes the registered TileObserver. + * + * @param to + * the TileObserver which is registered for this + * WritableRenderedImage. + */ + public void removeTileObserver(TileObserver to); + + /** + * Adds the specified TileObserver to this WritableRenderedImage. + * + * @param to + * the TileObserver object to be added. + */ + public void addTileObserver(TileObserver to); + + /** + * Sets this image to the contents of the specified Raster. + * + * @param r + * the specified Raster. + */ + public void setData(Raster r); + + /** + * Gets the array of points which represent indices of tiles which are check + * out for writing. + * + * @return the array of points. + */ + public Point[] getWritableTileIndices(); + + /** + * Checks if the specified tile is writable or not. + * + * @param tileX + * the X index of tile. + * @param tileY + * the Y index of tile. + * @return true, if the specified tile is writable, false otherwise. + */ + public boolean isTileWritable(int tileX, int tileY); + + /** + * Release the specified writable tile. This method removes the writer from + * the tile. + * + * @param tileX + * the X index of the tile. + * @param tileY + * the Y index of the tile. + */ + public void releaseWritableTile(int tileX, int tileY); + + /** + * Checks if there is a tile which is checked out for writing. + * + * @return true, if any tile is checked out for writing, false if there is + * no such tile. + */ + public boolean hasTileWriters(); + +} diff --git a/awt/java/awt/image/package.html b/awt/java/awt/image/package.html new file mode 100644 index 000000000..b4d6ef0a8 --- /dev/null +++ b/awt/java/awt/image/package.html @@ -0,0 +1,8 @@ + + +

+ This package contains classes and interfaces that allow to modify existing images or to create a new image rather than loading it from a file. +

+ @since Android 1.0 + + diff --git a/awt/java/awt/image/renderable/ContextualRenderedImageFactory.java b/awt/java/awt/image/renderable/ContextualRenderedImageFactory.java new file mode 100644 index 000000000..1881a0c27 --- /dev/null +++ b/awt/java/awt/image/renderable/ContextualRenderedImageFactory.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image.renderable; + +import java.awt.geom.Rectangle2D; +import java.awt.image.RenderedImage; + +/** + * A factory for creating ContextualRenderedImage objects with utilities for + * manipulating the properties in the parameter block. + * + * @since Android 1.0 + */ +public interface ContextualRenderedImageFactory extends RenderedImageFactory { + + /** + * Maps a render context to a parameter block and a renderable image. + * + * @param a0 + * the index. + * @param a1 + * the RenderContext. + * @param a2 + * the ParameterBlock. + * @param a3 + * the RenderableImage. + * @return the render context. + */ + public RenderContext mapRenderContext(int a0, RenderContext a1, ParameterBlock a2, + RenderableImage a3); + + /** + * Gets the value of the property from the parameter block. + * + * @param a0 + * the parameter block to examine to find the property. + * @param a1 + * the name of the property. + * @return the value of the property. + */ + public Object getProperty(ParameterBlock a0, String a1); + + /** + * Creates the rendered image determined by the render context and parameter + * block. + * + * @param a0 + * the RenderContext. + * @param a1 + * the ParameterBlock. + * @return the rendered image. + */ + public RenderedImage create(RenderContext a0, ParameterBlock a1); + + /** + * Gets the bounding rectangle from the parameter block. + * + * @param a0 + * the parameter block to read the bounds from. + * @return the bounding rectangle. + */ + public Rectangle2D getBounds2D(ParameterBlock a0); + + /** + * Gets the names of all of the supported properties. + * + * @return the property names. + */ + public String[] getPropertyNames(); + + /** + * Checks if this image factory is dynamic. + * + * @return true, if this image factory is dynamic. + */ + public boolean isDynamic(); + +} diff --git a/awt/java/awt/image/renderable/ParameterBlock.java b/awt/java/awt/image/renderable/ParameterBlock.java new file mode 100644 index 000000000..7dde73a94 --- /dev/null +++ b/awt/java/awt/image/renderable/ParameterBlock.java @@ -0,0 +1,568 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image.renderable; + +import java.awt.image.RenderedImage; +import java.io.Serializable; +import java.util.Vector; + +/** + * The class ParameterBlock groups an indexed set of parameter data with a set + * of renderable (source) images. The mapping between the indexed parameters and + * their property names is provided by a {@link ContextualRenderedImageFactory}. + * + * @since Android 1.0 + */ +public class ParameterBlock implements Cloneable, Serializable { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -7577115551785240750L; + + /** + * The sources (renderable images). + */ + protected Vector sources = new Vector(); + + /** + * The parameters. + */ + protected Vector parameters = new Vector(); + + /** + * Instantiates a new parameter block. + * + * @param sources + * the vector of source images. + * @param parameters + * the vector of parameters. + */ + public ParameterBlock(Vector sources, Vector parameters) { + setSources(sources); + setParameters(parameters); + } + + /** + * Instantiates a new parameter block with no parameters. + * + * @param sources + * the vector of source images. + */ + public ParameterBlock(Vector sources) { + setSources(sources); + } + + /** + * Instantiates a new parameter block with no image or parameter vectors. + */ + public ParameterBlock() { + } + + /** + * Sets the source image at the specified index. + * + * @param source + * the source image. + * @param index + * the index where the source will be placed. + * @return this parameter block. + */ + public ParameterBlock setSource(Object source, int index) { + if (sources.size() < index + 1) { + sources.setSize(index + 1); + } + sources.setElementAt(source, index); + return this; + } + + /** + * Sets the parameter value object at the specified index. + * + * @param obj + * the parameter value to place at the desired index. + * @param index + * the index where the object is to be placed in the vector of + * parameters. + * @return this parameter block. + */ + public ParameterBlock set(Object obj, int index) { + if (parameters.size() < index + 1) { + parameters.setSize(index + 1); + } + parameters.setElementAt(obj, index); + return this; + } + + /** + * Adds a source to the vector of sources. + * + * @param source + * the source to add. + * @return this parameter block. + */ + public ParameterBlock addSource(Object source) { + sources.addElement(source); + return this; + } + + /** + * Adds the object to the vector of parameter values + * + * @param obj + * the obj to add. + * @return this parameter block. + */ + public ParameterBlock add(Object obj) { + parameters.addElement(obj); + return this; + } + + /** + * Sets the vector of sources, replacing the existing vector of sources, if + * any. + * + * @param sources + * the new sources. + */ + public void setSources(Vector sources) { + this.sources = sources; + } + + /** + * Sets the vector of parameters, replacing the existing vector of + * parameters, if any. + * + * @param parameters + * the new parameters. + */ + public void setParameters(Vector parameters) { + this.parameters = parameters; + } + + /** + * Gets the vector of sources. + * + * @return the sources. + */ + public Vector getSources() { + return sources; + } + + /** + * Gets the vector of parameters. + * + * @return the parameters. + */ + public Vector getParameters() { + return parameters; + } + + /** + * Gets the source at the specified index. + * + * @param index + * the index. + * @return the source object found at the specified index. + */ + public Object getSource(int index) { + return sources.elementAt(index); + } + + /** + * Gets the object parameter found at the specified index. + * + * @param index + * the index. + * @return the parameter object found at the specified index. + */ + public Object getObjectParameter(int index) { + return parameters.elementAt(index); + } + + /** + * Shallow clone (clones using the superclass clone method). + * + * @return the clone of this object. + */ + public Object shallowClone() { + try { + return super.clone(); + } catch (Exception e) { + return null; + } + } + + /** + * Returns a copy of this ParameterBlock instance. + * + * @return the identical copy of this instance. + */ + @SuppressWarnings("unchecked") + @Override + public Object clone() { + ParameterBlock replica; + try { + replica = (ParameterBlock)super.clone(); + } catch (Exception e) { + return null; + } + if (sources != null) { + replica.setSources((Vector)(sources.clone())); + } + if (parameters != null) { + replica.setParameters((Vector)(parameters.clone())); + } + return replica; + } + + /** + * Gets an array of classes corresponding to all of the parameter values + * found in the array of parameters, in order. + * + * @return the parameter classes. + */ + public Class[] getParamClasses() { + int count = parameters.size(); + Class paramClasses[] = new Class[count]; + + for (int i = 0; i < count; i++) { + paramClasses[i] = parameters.elementAt(i).getClass(); + } + return paramClasses; + } + + /** + * Gets the renderable source image found at the specified index in the + * source array. + * + * @param index + * the index. + * @return the renderable source image. + */ + public RenderableImage getRenderableSource(int index) { + return (RenderableImage)sources.elementAt(index); + } + + /** + * Wraps the short value in a Short and places it in the parameter block at + * the specified index. + * + * @param s + * the short value of the parameter. + * @param index + * the index. + * @return this parameter block. + */ + public ParameterBlock set(short s, int index) { + return set(new Short(s), index); + } + + /** + * Wraps the short value in a Short and adds it to the parameter block. + * + * @param s + * the short value of the parameter. + * @return this parameter block. + */ + public ParameterBlock add(short s) { + return add(new Short(s)); + } + + /** + * Wraps the long value in a Long and places it in the parameter block at + * the specified index. + * + * @param l + * the long value of the parameter. + * @param index + * the index. + * @return this parameter block. + */ + public ParameterBlock set(long l, int index) { + return set(new Long(l), index); + } + + /** + * Wraps the long value in a Long and adds it to the parameter block. + * + * @param l + * the long value of the parameter. + * @return this parameter block. + */ + public ParameterBlock add(long l) { + return add(new Long(l)); + } + + /** + * Wraps the integer value in an Integer and places it in the parameter + * block at the specified index. + * + * @param i + * the integer value of the parameter. + * @param index + * the index. + * @return this parameter block. + */ + public ParameterBlock set(int i, int index) { + return set(new Integer(i), index); + } + + /** + * Wraps the integer value in an Integer and adds it to the parameter block. + * + * @param i + * the integer value of the parameter. + * @return this parameter block. + */ + public ParameterBlock add(int i) { + return add(new Integer(i)); + } + + /** + * Wraps the float value in a Float and places it in the parameter block at + * the specified index. + * + * @param f + * the float value of the parameter. + * @param index + * the index. + * @return this parameter block. + */ + public ParameterBlock set(float f, int index) { + return set(new Float(f), index); + } + + /** + * Wraps the float value in a Float and adds it to the parameter block. + * + * @param f + * the float value of the parameter. + * @return this parameter block. + */ + public ParameterBlock add(float f) { + return add(new Float(f)); + } + + /** + * Wraps the double value in a Double and places it in the parameter block + * at the specified index. + * + * @param d + * the double value of the parameter. + * @param index + * the index. + * @return this parameter block. + */ + public ParameterBlock set(double d, int index) { + return set(new Double(d), index); + } + + /** + * Wraps the double value in a Double and adds it to the parameter block. + * + * @param d + * the double value of the parameter. + * @return this parameter block. + */ + public ParameterBlock add(double d) { + return add(new Double(d)); + } + + /** + * Wraps the char value in a Character and places it in the parameter block + * at the specified index. + * + * @param c + * the char value of the parameter. + * @param index + * the index. + * @return this parameter block. + */ + public ParameterBlock set(char c, int index) { + return set(new Character(c), index); + } + + /** + * Wraps the char value in a Character and adds it to the parameter block. + * + * @param c + * the char value of the parameter. + * @return this parameter block. + */ + public ParameterBlock add(char c) { + return add(new Character(c)); + } + + /** + * Wraps the byte value in a Byte and places it in the parameter block at + * the specified index. + * + * @param b + * the byte value of the parameter. + * @param index + * the index. + * @return this parameter block. + */ + public ParameterBlock set(byte b, int index) { + return set(new Byte(b), index); + } + + /** + * Wraps the byte value in a Byte and adds it to the parameter block. + * + * @param b + * the byte value of the parameter. + * @return the parameter block. + */ + public ParameterBlock add(byte b) { + return add(new Byte(b)); + } + + /** + * Gets the RenderedImage at the specified index from the vector of source + * images. + * + * @param index + * the index. + * @return the rendered image. + */ + public RenderedImage getRenderedSource(int index) { + return (RenderedImage)sources.elementAt(index); + } + + /** + * Gets the short-valued parameter found at the desired index in the vector + * of parameter values. + * + * @param index + * the index. + * @return the short parameter. + */ + public short getShortParameter(int index) { + return ((Short)parameters.elementAt(index)).shortValue(); + } + + /** + * Gets the long-valued parameter found at the desired index in the vector + * of parameter values. + * + * @param index + * the index. + * @return the long parameter. + */ + public long getLongParameter(int index) { + return ((Long)parameters.elementAt(index)).longValue(); + } + + /** + * Gets the integer-valued parameter found at the desired index in the + * vector of parameter values. + * + * @param index + * the index. + * @return the integer parameter. + */ + public int getIntParameter(int index) { + return ((Integer)parameters.elementAt(index)).intValue(); + } + + /** + * Gets the float-valued parameter found at the desired index in the vector + * of parameter values. + * + * @param index + * the index. + * @return the float parameter. + */ + public float getFloatParameter(int index) { + return ((Float)parameters.elementAt(index)).floatValue(); + } + + /** + * Gets the double-valued parameter found at the desired index in the vector + * of parameter values. + * + * @param index + * the index. + * @return the double parameter. + */ + public double getDoubleParameter(int index) { + return ((Double)parameters.elementAt(index)).doubleValue(); + } + + /** + * Gets the char-valued parameter found at the desired index in the vector + * of parameter values. + * + * @param index + * the index. + * @return the char parameter. + */ + public char getCharParameter(int index) { + return ((Character)parameters.elementAt(index)).charValue(); + } + + /** + * Gets the byte-valued parameter found at the desired index in the vector + * of parameter values. + * + * @param index + * the index. + * @return the byte parameter. + */ + public byte getByteParameter(int index) { + return ((Byte)parameters.elementAt(index)).byteValue(); + } + + /** + * Clears the vector of sources. + */ + public void removeSources() { + sources.removeAllElements(); + } + + /** + * Clears the vector of parameters. + */ + public void removeParameters() { + parameters.removeAllElements(); + } + + /** + * Gets the number of elements in the vector of sources. + * + * @return the number of elements in the vector of sources. + */ + public int getNumSources() { + return sources.size(); + } + + /** + * Gets the number of elements in the vector of parameters. + * + * @return the number of elements in the vector of parameters. + */ + public int getNumParameters() { + return parameters.size(); + } +} diff --git a/awt/java/awt/image/renderable/RenderContext.java b/awt/java/awt/image/renderable/RenderContext.java new file mode 100644 index 000000000..0db512faf --- /dev/null +++ b/awt/java/awt/image/renderable/RenderContext.java @@ -0,0 +1,214 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.AffineTransform; + +/** + * The Class RenderContext stores data on how an image is to be rendered: the + * affine transform, the area of interest, and the rendering hints. + * + * @since Android 1.0 + */ +public class RenderContext implements Cloneable { + + /** + * The affine transform. + */ + AffineTransform transform; + + /** + * The area of interest. + */ + Shape aoi; + + /** + * The rendering hints. + */ + RenderingHints hints; + + /** + * Instantiates a new render context. + * + * @param usr2dev + * the affine transform. + * @param aoi + * the area of interest. + * @param hints + * the rendering hints. + */ + public RenderContext(AffineTransform usr2dev, Shape aoi, RenderingHints hints) { + this.transform = (AffineTransform)usr2dev.clone(); + this.aoi = aoi; + this.hints = hints; + } + + /** + * Instantiates a new render context with no specified hints. + * + * @param usr2dev + * the affine transform. + * @param aoi + * the area of interest. + */ + public RenderContext(AffineTransform usr2dev, Shape aoi) { + this(usr2dev, aoi, null); + } + + /** + * Instantiates a new render context with no specified area of interest. + * + * @param usr2dev + * the affine transform. + * @param hints + * the rendering hints. + */ + public RenderContext(AffineTransform usr2dev, RenderingHints hints) { + this(usr2dev, null, hints); + } + + /** + * Instantiates a new render context with no rendering hints or area of + * interest. + * + * @param usr2dev + * the affine transform. + */ + public RenderContext(AffineTransform usr2dev) { + this(usr2dev, null, null); + } + + @Override + public Object clone() { + return new RenderContext(transform, aoi, hints); + } + + /** + * Sets the affine transform for this render context. + * + * @param newTransform + * the new affine transform. + */ + public void setTransform(AffineTransform newTransform) { + transform = (AffineTransform)newTransform.clone(); + } + + /** + * Concatenates the current transform with the specified transform (so they + * are applied with the specified transform acting first) and sets the + * resulting transform as the affine transform of this rendering context. + * + * @param modTransform + * the new transform which modifies the current transform. + * @deprecated use + * {@link RenderContext#preConcatenateTransform(AffineTransform)} + * . + */ + @Deprecated + public void preConcetenateTransform(AffineTransform modTransform) { + preConcatenateTransform(modTransform); + } + + /** + * Concatenates the current transform with the specified transform (so they + * are applied with the specified transform acting first) and sets the + * resulting transform as the affine transform of this rendering context. + * + * @param modTransform + * the new transform which modifies the current transform. + */ + public void preConcatenateTransform(AffineTransform modTransform) { + transform.preConcatenate(modTransform); + } + + /** + * Concatenate the specified transform with the current transform. + * + * @param modTransform + * the new transform which modifies the current transform. + * @deprecated use + * {@link RenderContext#concatenateTransform(AffineTransform)}. + */ + @Deprecated + public void concetenateTransform(AffineTransform modTransform) { + concatenateTransform(modTransform); + } + + /** + * Concatenate the specified transform with the current transform. + * + * @param modTransform + * the new transform which modifies the current transform. + */ + public void concatenateTransform(AffineTransform modTransform) { + transform.concatenate(modTransform); + } + + /** + * Gets the transform. + * + * @return the transform. + */ + public AffineTransform getTransform() { + return (AffineTransform)transform.clone(); + } + + /** + * Sets the area of interest. + * + * @param newAoi + * the new area of interest. + */ + public void setAreaOfInterest(Shape newAoi) { + aoi = newAoi; + } + + /** + * Gets the area of interest. + * + * @return the area of interest. + */ + public Shape getAreaOfInterest() { + return aoi; + } + + /** + * Sets the rendering hints. + * + * @param hints + * the new rendering hints. + */ + public void setRenderingHints(RenderingHints hints) { + this.hints = hints; + } + + /** + * Gets the rendering hints. + * + * @return the rendering hints. + */ + public RenderingHints getRenderingHints() { + return hints; + } +} diff --git a/awt/java/awt/image/renderable/RenderableImage.java b/awt/java/awt/image/renderable/RenderableImage.java new file mode 100644 index 000000000..21332f7d1 --- /dev/null +++ b/awt/java/awt/image/renderable/RenderableImage.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.image.RenderedImage; +import java.util.Vector; + +/** + * The Interface RenderableImage is implemented by an object that collects all + * of the image-specific data that defines a single image that could be rendered + * to different rendering targets. + * + * @since Android 1.0 + */ +public interface RenderableImage { + + /** + * The Constant HINTS_OBSERVED indicates that the rendering hints are + * applied rather than ignored. + */ + public static final String HINTS_OBSERVED = "HINTS_OBSERVED"; //$NON-NLS-1$ + + /** + * Gets the property from the RenderableImage's parameter block. + * + * @param name + * the name of the property to get. + * @return the value of the property. + */ + public Object getProperty(String name); + + /** + * Creates the rendered image based on the information contained in the + * parameters and the render context. + * + * @param renderContext + * the render context giving rendering specifications such as + * transformations. + * @return the rendered image. + */ + public RenderedImage createRendering(RenderContext renderContext); + + /** + * Creates the scaled rendered image based on the information contained in + * the parameters and the render context. + * + * @param w + * the desired width after scaling or zero if the scaling should + * be proportional, based on the height. + * @param h + * the desired height after scaling or zero if the scaling should + * be proportional, based on the width. + * @param hints + * the rendering hints to use. + * @return the rendered image. + * @throws IllegalArgumentException + * if both the height and width are zero. + */ + public RenderedImage createScaledRendering(int w, int h, RenderingHints hints); + + /** + * Gets the vector of sources from the parameter block. + * + * @return the sources. + */ + public Vector getSources(); + + /** + * Gets the names of all of the supported properties in the current context. + * + * @return the property names. + */ + public String[] getPropertyNames(); + + /** + * Creates the default rendering (using the identity transform and default + * render context). + * + * @return the rendered image. + */ + public RenderedImage createDefaultRendering(); + + /** + * Checks if this context supports dynamic rendering. + * + * @return true, if this context supports dynamic rendering. + */ + public boolean isDynamic(); + + /** + * Gets the width of the image. + * + * @return the width of the image. + */ + public float getWidth(); + + /** + * Gets the y coordinate of the upper left corner. + * + * @return the y coordinate of the upper left corner. + */ + public float getMinY(); + + /** + * Gets the x coordinate of the upper left corner. + * + * @return the x coordinate of the upper left corner. + */ + public float getMinX(); + + /** + * Gets the height of the image. + * + * @return the height of the image. + */ + public float getHeight(); + +} diff --git a/awt/java/awt/image/renderable/RenderableImageOp.java b/awt/java/awt/image/renderable/RenderableImageOp.java new file mode 100644 index 000000000..dc453727b --- /dev/null +++ b/awt/java/awt/image/renderable/RenderableImageOp.java @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.RenderedImage; +import java.util.Vector; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The Class RenderableImageOp is a basic implementation of RenderableImage, + * with methods to access the parameter data and perform rendering operations. + * + * @since Android 1.0 + */ +public class RenderableImageOp implements RenderableImage { + + /** + * The CRIF. + */ + ContextualRenderedImageFactory CRIF; + + /** + * The param block. + */ + ParameterBlock paramBlock; + + /** + * The height. + */ + float minX, minY, width, height; + + /** + * Instantiates a new renderable image op. + * + * @param CRIF + * the cRIF. + * @param paramBlock + * the param block. + */ + public RenderableImageOp(ContextualRenderedImageFactory CRIF, ParameterBlock paramBlock) { + this.CRIF = CRIF; + this.paramBlock = (ParameterBlock)paramBlock.clone(); + Rectangle2D r = CRIF.getBounds2D(paramBlock); + minX = (float)r.getMinX(); + minY = (float)r.getMinY(); + width = (float)r.getWidth(); + height = (float)r.getHeight(); + } + + public Object getProperty(String name) { + return CRIF.getProperty(paramBlock, name); + } + + /** + * Sets the parameter block. + * + * @param paramBlock + * the param block. + * @return the parameter block. + */ + public ParameterBlock setParameterBlock(ParameterBlock paramBlock) { + ParameterBlock oldParam = this.paramBlock; + this.paramBlock = (ParameterBlock)paramBlock.clone(); + return oldParam; + } + + public RenderedImage createRendering(RenderContext renderContext) { + + Vector sources = getSources(); + ParameterBlock rdParam = (ParameterBlock)paramBlock.clone(); + + if (sources != null) { + Vector rdSources = new Vector(); + int i = 0; + while (i < sources.size()) { + RenderContext newContext = CRIF + .mapRenderContext(i, renderContext, paramBlock, this); + RenderedImage rdim = sources.elementAt(i).createRendering(newContext); + + if (rdim != null) { + rdSources.addElement(rdim); + } + i++; + } + if (rdSources.size() > 0) { + rdParam.setSources(rdSources); + } + } + return CRIF.create(renderContext, rdParam); + } + + public RenderedImage createScaledRendering(int w, int h, RenderingHints hints) { + if (w == 0 && h == 0) { + // awt.60=Width and Height mustn't be equal zero both + throw new IllegalArgumentException(Messages.getString("awt.60")); //$NON-NLS-1$ + } + if (w == 0) { + w = Math.round(h * (getWidth() / getHeight())); + } + + if (h == 0) { + h = Math.round(w * (getHeight() / getWidth())); + } + + double sx = (double)w / getWidth(); + double sy = (double)h / getHeight(); + + AffineTransform at = AffineTransform.getScaleInstance(sx, sy); + RenderContext context = new RenderContext(at, hints); + return createRendering(context); + } + + public Vector getSources() { + if (paramBlock.getNumSources() == 0) { + return null; + } + Vector v = new Vector(); + int i = 0; + while (i < paramBlock.getNumSources()) { + Object o = paramBlock.getSource(i); + if (o instanceof RenderableImage) { + v.addElement((RenderableImage)o); + } + i++; + } + return v; + } + + public String[] getPropertyNames() { + return CRIF.getPropertyNames(); + } + + /** + * Gets the parameter block. + * + * @return the parameter block + */ + public ParameterBlock getParameterBlock() { + return paramBlock; + } + + public RenderedImage createDefaultRendering() { + AffineTransform at = new AffineTransform(); + RenderContext context = new RenderContext(at); + return createRendering(context); + } + + public boolean isDynamic() { + return CRIF.isDynamic(); + } + + public float getWidth() { + return width; + } + + public float getMinY() { + return minY; + } + + public float getMinX() { + return minX; + } + + public float getHeight() { + return height; + } + +} diff --git a/awt/java/awt/image/renderable/RenderableImageProducer.java b/awt/java/awt/image/renderable/RenderableImageProducer.java new file mode 100644 index 000000000..e83ebc76b --- /dev/null +++ b/awt/java/awt/image/renderable/RenderableImageProducer.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image.renderable; + +import java.awt.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageProducer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.util.Vector; + +/** + * The Class RenderableImageProducer provides the implementation for the image + * rendering. + * + * @since Android 1.0 + */ +public class RenderableImageProducer implements ImageProducer, Runnable { + + /** + * The rbl. + */ + RenderableImage rbl; + + /** + * The rc. + */ + RenderContext rc; + + /** + * The consumers. + */ + Vector consumers = new Vector(); + + /** + * Instantiates a new renderable image producer. + * + * @param rdblImage + * the rdbl image. + * @param rc + * the rc. + */ + public RenderableImageProducer(RenderableImage rdblImage, RenderContext rc) { + this.rbl = rdblImage; + this.rc = rc; + } + + /** + * Sets the render context. + * + * @param rc + * the new render context. + */ + public synchronized void setRenderContext(RenderContext rc) { + this.rc = rc; + } + + public synchronized boolean isConsumer(ImageConsumer ic) { + return consumers.contains(ic); + } + + public synchronized void startProduction(ImageConsumer ic) { + addConsumer(ic); + Thread t = new Thread(this, "RenderableImageProducer thread"); //$NON-NLS-1$ + t.start(); + } + + public void requestTopDownLeftRightResend(ImageConsumer ic) { + } + + public synchronized void removeConsumer(ImageConsumer ic) { + if (ic != null) { + consumers.removeElement(ic); + } + } + + public synchronized void addConsumer(ImageConsumer ic) { + if (ic != null && !consumers.contains(ic)) { + consumers.addElement(ic); + } + } + + /** + * Creates the rendered image in a new thread. + */ + public void run() { + if (rbl == null) { + return; + } + + RenderedImage rd; + if (rc != null) { + rd = rbl.createRendering(rc); + } else { + rd = rbl.createDefaultRendering(); + } + + ColorModel cm = rd.getColorModel(); + if (cm == null) { + cm = ColorModel.getRGBdefault(); + } + + Raster r = rd.getData(); + int w = r.getWidth(); + int h = r.getHeight(); + + for (ImageConsumer c : consumers) { + c.setDimensions(w, h); + c.setHints(ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES + | ImageConsumer.SINGLEFRAME | ImageConsumer.SINGLEPASS); + } + + int scanLine[] = new int[w]; + int pixel[] = null; + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + pixel = r.getPixel(x, y, pixel); + scanLine[x] = cm.getDataElement(pixel, 0); + } + + for (ImageConsumer c : consumers) { + c.setPixels(0, y, w, 1, cm, scanLine, 0, w); + } + } + + for (ImageConsumer c : consumers) { + c.imageComplete(ImageConsumer.STATICIMAGEDONE); + } + } + +} diff --git a/awt/java/awt/image/renderable/RenderedImageFactory.java b/awt/java/awt/image/renderable/RenderedImageFactory.java new file mode 100644 index 000000000..881a40ab7 --- /dev/null +++ b/awt/java/awt/image/renderable/RenderedImageFactory.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.image.RenderedImage; + +/** + * A factory for creating RenderedImage objects based on parameters and + * rendering hints. + * + * @since Android 1.0 + */ +public interface RenderedImageFactory { + + /** + * Creates the rendered image. + * + * @param a0 + * the ParameterBlock. + * @param a1 + * the RenderingHints. + * @return the rendered image. + */ + public RenderedImage create(ParameterBlock a0, RenderingHints a1); + +} diff --git a/awt/java/awt/image/renderable/package.html b/awt/java/awt/image/renderable/package.html new file mode 100644 index 000000000..43aaabc83 --- /dev/null +++ b/awt/java/awt/image/renderable/package.html @@ -0,0 +1,8 @@ + + +

+ This package contains classes to create images which are rendering-independent. +

+ @since Android 1.0 + + diff --git a/awt/java/awt/package.html b/awt/java/awt/package.html new file mode 100644 index 000000000..5a6f9f011 --- /dev/null +++ b/awt/java/awt/package.html @@ -0,0 +1,8 @@ + + +

+ This package contains classes and interfaces for creating (graphical) user interfaces (GUI), painting 2D graphics and creating, manipulating and drawing images. +

+ @since Android 1.0 + + diff --git a/awt/java/awt/peer/ButtonPeer.java b/awt/java/awt/peer/ButtonPeer.java new file mode 100644 index 000000000..cc45b4957 --- /dev/null +++ b/awt/java/awt/peer/ButtonPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface ButtonPeer { + +} diff --git a/awt/java/awt/peer/CanvasPeer.java b/awt/java/awt/peer/CanvasPeer.java new file mode 100644 index 000000000..e2763662b --- /dev/null +++ b/awt/java/awt/peer/CanvasPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface CanvasPeer { + +} diff --git a/awt/java/awt/peer/CheckboxMenuItemPeer.java b/awt/java/awt/peer/CheckboxMenuItemPeer.java new file mode 100644 index 000000000..296f42206 --- /dev/null +++ b/awt/java/awt/peer/CheckboxMenuItemPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface CheckboxMenuItemPeer { + +} diff --git a/awt/java/awt/peer/CheckboxPeer.java b/awt/java/awt/peer/CheckboxPeer.java new file mode 100644 index 000000000..e9f8dd192 --- /dev/null +++ b/awt/java/awt/peer/CheckboxPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface CheckboxPeer { + +} diff --git a/awt/java/awt/peer/ChoicePeer.java b/awt/java/awt/peer/ChoicePeer.java new file mode 100644 index 000000000..57b762951 --- /dev/null +++ b/awt/java/awt/peer/ChoicePeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface ChoicePeer { + +} diff --git a/awt/java/awt/peer/ComponentPeer.java b/awt/java/awt/peer/ComponentPeer.java new file mode 100644 index 000000000..bc26791de --- /dev/null +++ b/awt/java/awt/peer/ComponentPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface ComponentPeer { + +} diff --git a/awt/java/awt/peer/DialogPeer.java b/awt/java/awt/peer/DialogPeer.java new file mode 100644 index 000000000..8ae3049bc --- /dev/null +++ b/awt/java/awt/peer/DialogPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface DialogPeer { + +} diff --git a/awt/java/awt/peer/FileDialogPeer.java b/awt/java/awt/peer/FileDialogPeer.java new file mode 100644 index 000000000..0d15e489b --- /dev/null +++ b/awt/java/awt/peer/FileDialogPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface FileDialogPeer { + +} diff --git a/awt/java/awt/peer/FontPeer.java b/awt/java/awt/peer/FontPeer.java new file mode 100644 index 000000000..fd9815f3e --- /dev/null +++ b/awt/java/awt/peer/FontPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface FontPeer { + +} diff --git a/awt/java/awt/peer/FramePeer.java b/awt/java/awt/peer/FramePeer.java new file mode 100644 index 000000000..9cfc40b72 --- /dev/null +++ b/awt/java/awt/peer/FramePeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface FramePeer { + +} diff --git a/awt/java/awt/peer/LabelPeer.java b/awt/java/awt/peer/LabelPeer.java new file mode 100644 index 000000000..052ca9d90 --- /dev/null +++ b/awt/java/awt/peer/LabelPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface LabelPeer { + +} diff --git a/awt/java/awt/peer/LightweightPeer.java b/awt/java/awt/peer/LightweightPeer.java new file mode 100644 index 000000000..1dee90554 --- /dev/null +++ b/awt/java/awt/peer/LightweightPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface LightweightPeer { + +} diff --git a/awt/java/awt/peer/ListPeer.java b/awt/java/awt/peer/ListPeer.java new file mode 100644 index 000000000..0a27885d0 --- /dev/null +++ b/awt/java/awt/peer/ListPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface ListPeer { + +} diff --git a/awt/java/awt/peer/MenuBarPeer.java b/awt/java/awt/peer/MenuBarPeer.java new file mode 100644 index 000000000..3ad2c1603 --- /dev/null +++ b/awt/java/awt/peer/MenuBarPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface MenuBarPeer { + +} diff --git a/awt/java/awt/peer/MenuComponentPeer.java b/awt/java/awt/peer/MenuComponentPeer.java new file mode 100644 index 000000000..3ac3b34a6 --- /dev/null +++ b/awt/java/awt/peer/MenuComponentPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface MenuComponentPeer { + +} diff --git a/awt/java/awt/peer/MenuItemPeer.java b/awt/java/awt/peer/MenuItemPeer.java new file mode 100644 index 000000000..b13389777 --- /dev/null +++ b/awt/java/awt/peer/MenuItemPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface MenuItemPeer { + +} diff --git a/awt/java/awt/peer/MenuPeer.java b/awt/java/awt/peer/MenuPeer.java new file mode 100644 index 000000000..d643ce722 --- /dev/null +++ b/awt/java/awt/peer/MenuPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface MenuPeer { + +} diff --git a/awt/java/awt/peer/MouseInfoPeer.java b/awt/java/awt/peer/MouseInfoPeer.java new file mode 100644 index 000000000..9173a6222 --- /dev/null +++ b/awt/java/awt/peer/MouseInfoPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface MouseInfoPeer { + +} diff --git a/awt/java/awt/peer/PanelPeer.java b/awt/java/awt/peer/PanelPeer.java new file mode 100644 index 000000000..1faa1fe43 --- /dev/null +++ b/awt/java/awt/peer/PanelPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface PanelPeer { + +} diff --git a/awt/java/awt/peer/PopupMenuPeer.java b/awt/java/awt/peer/PopupMenuPeer.java new file mode 100644 index 000000000..cf1ef6111 --- /dev/null +++ b/awt/java/awt/peer/PopupMenuPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface PopupMenuPeer { + +} diff --git a/awt/java/awt/peer/ScrollPanePeer.java b/awt/java/awt/peer/ScrollPanePeer.java new file mode 100644 index 000000000..df3de8383 --- /dev/null +++ b/awt/java/awt/peer/ScrollPanePeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface ScrollPanePeer { + +} diff --git a/awt/java/awt/peer/ScrollbarPeer.java b/awt/java/awt/peer/ScrollbarPeer.java new file mode 100644 index 000000000..eec89611c --- /dev/null +++ b/awt/java/awt/peer/ScrollbarPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface ScrollbarPeer { + +} diff --git a/awt/java/awt/peer/TextAreaPeer.java b/awt/java/awt/peer/TextAreaPeer.java new file mode 100644 index 000000000..636707fd4 --- /dev/null +++ b/awt/java/awt/peer/TextAreaPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface TextAreaPeer { + +} diff --git a/awt/java/awt/peer/TextFieldPeer.java b/awt/java/awt/peer/TextFieldPeer.java new file mode 100644 index 000000000..2b8232a1d --- /dev/null +++ b/awt/java/awt/peer/TextFieldPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface TextFieldPeer { + +} diff --git a/awt/java/awt/peer/WindowPeer.java b/awt/java/awt/peer/WindowPeer.java new file mode 100644 index 000000000..384646f89 --- /dev/null +++ b/awt/java/awt/peer/WindowPeer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package java.awt.peer; + +public interface WindowPeer { + +} diff --git a/awt/java/beans/FeatureDescriptor.java b/awt/java/beans/FeatureDescriptor.java new file mode 100644 index 000000000..2945c65b8 --- /dev/null +++ b/awt/java/beans/FeatureDescriptor.java @@ -0,0 +1,234 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.beans; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +/** + * Common base class for Descriptors. + */ +public class FeatureDescriptor { + + private Map values; + + boolean preferred, hidden, expert; + + String shortDescription; + + String name; + + String displayName; + + /** + *

+ * Constructs an instance. + *

+ */ + public FeatureDescriptor() { + this.values = new HashMap(); + } + + /** + *

+ * Sets the value for the named attribute. + *

+ * + * @param attributeName + * The name of the attribute to set a value with. + * @param value + * The value to set. + */ + public void setValue(String attributeName, Object value) { + if (attributeName == null || value == null) { + throw new NullPointerException(); + } + values.put(attributeName, value); + } + + /** + *

+ * Gets the value associated with the named attribute. + *

+ * + * @param attributeName + * The name of the attribute to get a value for. + * @return The attribute's value. + */ + public Object getValue(String attributeName) { + Object result = null; + if (attributeName != null) { + result = values.get(attributeName); + } + return result; + } + + /** + *

+ * Enumerates the attribute names. + *

+ * + * @return An instance of {@link Enumeration}. + */ + public Enumeration attributeNames() { + // Create a new list, so that the references are copied + return Collections.enumeration(new LinkedList(values.keySet())); + } + + /** + *

+ * Sets the short description. + *

+ * + * @param text + * The description to set. + */ + public void setShortDescription(String text) { + this.shortDescription = text; + } + + /** + *

+ * Sets the name. + *

+ * + * @param name + * The name to set. + */ + public void setName(String name) { + this.name = name; + } + + /** + *

+ * Sets the display name. + *

+ * + * @param displayName + * The display name to set. + */ + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + /** + *

+ * Gets the short description or {@link #getDisplayName()} if not set. + *

+ * + * @return The description. + */ + public String getShortDescription() { + return shortDescription == null ? getDisplayName() : shortDescription; + } + + /** + *

+ * Gets the name. + *

+ * + * @return The name. + */ + public String getName() { + return name; + } + + /** + *

+ * Gets the display name or {@link #getName()} if not set. + *

+ * + * @return The display name. + */ + public String getDisplayName() { + return displayName == null ? getName() : displayName; + } + + /** + *

+ * Sets the preferred indicator. + *

+ * + * @param preferred + * true if preferred, false + * otherwise. + */ + public void setPreferred(boolean preferred) { + this.preferred = preferred; + } + + /** + *

+ * Sets the hidden indicator. + *

+ * + * @param hidden + * true if hidden, false otherwise. + */ + public void setHidden(boolean hidden) { + this.hidden = hidden; + } + + /** + *

+ * Sets the expert indicator. + *

+ * + * @param expert + * true if expert, false otherwise. + */ + public void setExpert(boolean expert) { + this.expert = expert; + } + + /** + *

+ * Indicates if this feature is preferred. + *

+ * + * @return true if preferred, false otherwise. + */ + public boolean isPreferred() { + return preferred; + } + + /** + *

+ * Indicates if this feature is hidden. + *

+ * + * @return true if hidden, false otherwise. + */ + public boolean isHidden() { + return hidden; + } + + /** + *

+ * Indicates if this feature is an expert feature. + *

+ * + * @return true if hidden, false otherwise. + */ + public boolean isExpert() { + return expert; + } +} diff --git a/awt/java/beans/IndexedPropertyDescriptor.java b/awt/java/beans/IndexedPropertyDescriptor.java new file mode 100644 index 000000000..25667d96e --- /dev/null +++ b/awt/java/beans/IndexedPropertyDescriptor.java @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.beans; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import org.apache.harmony.beans.internal.nls.Messages; + +public class IndexedPropertyDescriptor extends PropertyDescriptor { + private Method indexedGetter; + + private Method indexedSetter; + + public IndexedPropertyDescriptor(String propertyName, Class beanClass, + String getterName, String setterName, String indexedGetterName, + String indexedSetterName) throws IntrospectionException { + super(propertyName, beanClass, getterName, setterName); + + // RI behaves like this + if (indexedGetterName == null && indexedSetterName == null && + (getterName != null || setterName != null)) { + throw new IntrospectionException(Messages.getString("beans.50")); + } + setIndexedReadMethod(beanClass, indexedGetterName); + setIndexedWriteMethod(beanClass, indexedSetterName); + } + + public IndexedPropertyDescriptor(String propertyName, Method getter, Method setter, + Method indexedGetter, Method indexedSetter) throws IntrospectionException { + super(propertyName, getter, setter); + + // we need this in order to be compatible with RI + if (indexedGetter == null && indexedSetter == null && + (getter != null || setter != null)) { + throw new IntrospectionException(Messages.getString("beans.50")); + } + setIndexedReadMethod(indexedGetter); + setIndexedWriteMethod(indexedSetter); + } + + public IndexedPropertyDescriptor(String propertyName, Class beanClass) + throws IntrospectionException { + super(propertyName, beanClass, null, null); + String getterName; + String setterName; + String indexedGetterName; + String indexedSetterName; + + // array getter + getterName = createDefaultMethodName(propertyName, "get"); //$NON-NLS-1$ + if (hasMethod(beanClass, getterName)) { + setReadMethod(beanClass, getterName); + } + // array setter + setterName = createDefaultMethodName(propertyName, "set"); //$NON-NLS-1$ + if (hasMethod(beanClass, setterName)) { + setWriteMethod(beanClass, setterName); + } + // indexed getter + indexedGetterName = createDefaultMethodName(propertyName, "get"); //$NON-NLS-1$ + if (hasMethod(beanClass, indexedGetterName)) { + setIndexedReadMethod(beanClass, indexedGetterName); + } + // indexed setter + indexedSetterName = createDefaultMethodName(propertyName, "set"); //$NON-NLS-1$ + if (hasMethod(beanClass, indexedSetterName)) { + setIndexedWriteMethod(beanClass, indexedSetterName); + } + // RI seems to behave a bit differently + if (indexedGetter == null && indexedSetter == null && + getReadMethod() == null && getWriteMethod() == null) { + throw new IntrospectionException( + Messages.getString("beans.01", propertyName)); //$NON-NLS-1$ + } + if (indexedGetter == null && indexedSetter == null) { + // not an indexed property indeed + throw new IntrospectionException(Messages.getString("beans.50")); + } + } + + public void setIndexedReadMethod(Method indexedGetter) throws IntrospectionException { + if (indexedGetter != null) { + int modifiers = indexedGetter.getModifiers(); + Class[] parameterTypes; + Class returnType; + Class indexedPropertyType; + + if (!Modifier.isPublic(modifiers)) { + throw new IntrospectionException(Messages.getString("beans.21")); //$NON-NLS-1$ + } + parameterTypes = indexedGetter.getParameterTypes(); + if (parameterTypes.length != 1) { + throw new IntrospectionException(Messages.getString("beans.22")); //$NON-NLS-1$ + } + if (!parameterTypes[0].equals(int.class)) { + throw new IntrospectionException(Messages.getString("beans.23")); //$NON-NLS-1$ + } + returnType = indexedGetter.getReturnType(); + indexedPropertyType = getIndexedPropertyType(); + if ((indexedPropertyType != null) && !returnType.equals(indexedPropertyType)) { + throw new IntrospectionException(Messages.getString("beans.24")); //$NON-NLS-1$ + } + } + this.indexedGetter = indexedGetter; + } + + public void setIndexedWriteMethod(Method indexedSetter) throws IntrospectionException { + if (indexedSetter != null) { + int modifiers = indexedSetter.getModifiers(); + Class[] parameterTypes; + Class firstParameterType; + Class secondParameterType; + Class propType; + + if (!Modifier.isPublic(modifiers)) { + throw new IntrospectionException(Messages.getString("beans.25")); //$NON-NLS-1$ + } + parameterTypes = indexedSetter.getParameterTypes(); + if (parameterTypes.length != 2) { + throw new IntrospectionException(Messages.getString("beans.26")); //$NON-NLS-1$ + } + firstParameterType = parameterTypes[0]; + if (!firstParameterType.equals(int.class)) { + throw new IntrospectionException(Messages.getString("beans.27")); //$NON-NLS-1$ + } + secondParameterType = parameterTypes[1]; + propType = getIndexedPropertyType(); + if (propType != null && !secondParameterType.equals(propType)) { + throw new IntrospectionException(Messages.getString("beans.28")); //$NON-NLS-1$ + } + } + this.indexedSetter = indexedSetter; + } + + public Method getIndexedWriteMethod() { + return indexedSetter; + } + + public Method getIndexedReadMethod() { + return indexedGetter; + } + + @Override + public boolean equals(Object obj) { + boolean result = super.equals(obj); + + if (result) { + IndexedPropertyDescriptor pd = (IndexedPropertyDescriptor) obj; + + if (indexedGetter != null) { + result = indexedGetter.equals(pd.getIndexedReadMethod()); + } else if (result && indexedGetter == null) { + result = pd.getIndexedReadMethod() == null; + } + + if (result) { + if (indexedSetter != null) { + result = indexedSetter.equals(pd.getIndexedWriteMethod()); + } else if (indexedSetter == null) { + result = pd.getIndexedWriteMethod() == null; + } + } + } + + return result; + } + + public Class getIndexedPropertyType() { + Class result = null; + + if (indexedGetter != null) { + result = indexedGetter.getReturnType(); + } else if (indexedSetter != null) { + Class[] parameterTypes = indexedSetter.getParameterTypes(); + + result = parameterTypes[1]; + } + return result; + } + + private void setIndexedReadMethod(Class beanClass, String indexedGetterName) { + Method[] getters = findMethods(beanClass, indexedGetterName); + boolean result = false; + + for (Method element : getters) { + try { + setIndexedReadMethod(element); + result = true; + } catch (IntrospectionException ie) {} + + if (result) { + break; + } + } + } + + private void setIndexedWriteMethod(Class beanClass, String indexedSetterName) { + Method[] setters = findMethods(beanClass, indexedSetterName); + boolean result = false; + + for (Method element : setters) { + try { + setIndexedWriteMethod(element); + result = true; + } catch (IntrospectionException ie) {} + + if (result) { + break; + } + } + } +} diff --git a/awt/java/beans/IntrospectionException.java b/awt/java/beans/IntrospectionException.java new file mode 100644 index 000000000..c895afe07 --- /dev/null +++ b/awt/java/beans/IntrospectionException.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.beans; + +public class IntrospectionException extends Exception { + + static final long serialVersionUID = -3728150539969542619L; + + public IntrospectionException(String message) { + super(message); + } +} diff --git a/awt/java/beans/PropertyDescriptor.java b/awt/java/beans/PropertyDescriptor.java new file mode 100644 index 000000000..93891525a --- /dev/null +++ b/awt/java/beans/PropertyDescriptor.java @@ -0,0 +1,300 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.beans; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Vector; +import org.apache.harmony.beans.internal.nls.Messages; + +public class PropertyDescriptor extends FeatureDescriptor { + private Method getter; + + private Method setter; + + private Class propertyEditorClass; + + private boolean constrained; + + private boolean bound; + + public PropertyDescriptor(String propertyName, Class beanClass, String getterName, + String setterName) throws IntrospectionException { + super(); + if (beanClass == null) { + throw new IntrospectionException(Messages.getString("beans.03")); //$NON-NLS-1$ + } + if (propertyName == null || propertyName.length() == 0) { + throw new IntrospectionException(Messages.getString("beans.04")); //$NON-NLS-1$ + } + this.setName(propertyName); + this.setDisplayName(propertyName); + if (setterName != null) { + if (hasMethod(beanClass, setterName)) { + setWriteMethod(beanClass, setterName); + } else { + throw new IntrospectionException(Messages.getString("beans.20")); //$NON-NLS-1$ + } + } + if (getterName != null) { + if (hasMethod(beanClass, getterName)) { + setReadMethod(beanClass, getterName); + } else { + throw new IntrospectionException(Messages.getString("beans.1F")); //$NON-NLS-1$ + } + } + } + + public PropertyDescriptor(String propertyName, Method getter, Method setter) + throws IntrospectionException { + super(); + if (propertyName == null || propertyName.length() == 0) { + throw new IntrospectionException(Messages.getString("beans.04")); //$NON-NLS-1$ + } + this.setName(propertyName); + this.setDisplayName(propertyName); + setWriteMethod(setter); + setReadMethod(getter); + } + + public PropertyDescriptor(String propertyName, Class beanClass) + throws IntrospectionException { + String getterName; + String setterName; + if (beanClass == null) { + throw new IntrospectionException(Messages.getString("beans.03")); //$NON-NLS-1$ + } + if (propertyName == null || propertyName.length() == 0) { + throw new IntrospectionException(Messages.getString("beans.04")); //$NON-NLS-1$ + } + this.setName(propertyName); + this.setDisplayName(propertyName); + getterName = createDefaultMethodName(propertyName, "is"); //$NON-NLS-1$ + if (hasMethod(beanClass, getterName)) { + setReadMethod(beanClass, getterName); + } else { + getterName = createDefaultMethodName(propertyName, "get"); //$NON-NLS-1$ + if (hasMethod(beanClass, getterName)) { + setReadMethod(beanClass, getterName); + } + } + setterName = createDefaultMethodName(propertyName, "set"); //$NON-NLS-1$ + if (hasMethod(beanClass, setterName)) { + setWriteMethod(beanClass, setterName); + } + if (getter == null && setter == null) { + throw new IntrospectionException(Messages.getString("beans.01", propertyName)); //$NON-NLS-1$ + } + } + + public void setWriteMethod(Method setter) throws IntrospectionException { + if (setter != null) { + int modifiers = setter.getModifiers(); + if (!Modifier.isPublic(modifiers)) { + throw new IntrospectionException(Messages.getString("beans.05")); //$NON-NLS-1$ + } + Class[] parameterTypes = setter.getParameterTypes(); + if (parameterTypes.length != 1) { + throw new IntrospectionException(Messages.getString("beans.06")); //$NON-NLS-1$ + } + Class parameterType = parameterTypes[0]; + Class propertyType = getPropertyType(); + if (propertyType != null && !propertyType.equals(parameterType)) { + throw new IntrospectionException(Messages.getString("beans.07")); //$NON-NLS-1$ + } + } + this.setter = setter; + } + + public void setReadMethod(Method getter) throws IntrospectionException { + if (getter != null) { + int modifiers = getter.getModifiers(); + if (!Modifier.isPublic(modifiers)) { + throw new IntrospectionException(Messages.getString("beans.0A")); //$NON-NLS-1$ + } + Class[] parameterTypes = getter.getParameterTypes(); + if (parameterTypes.length != 0) { + throw new IntrospectionException(Messages.getString("beans.08")); //$NON-NLS-1$ + } + Class returnType = getter.getReturnType(); + if (returnType.equals(Void.TYPE)) { + throw new IntrospectionException(Messages.getString("beans.33")); //$NON-NLS-1$ + } + Class propertyType = getPropertyType(); + if ((propertyType != null) && !returnType.equals(propertyType)) { + throw new IntrospectionException(Messages.getString("beans.09")); //$NON-NLS-1$ + } + } + this.getter = getter; + } + + public Method getWriteMethod() { + return setter; + } + + public Method getReadMethod() { + return getter; + } + + @Override + public boolean equals(Object object) { + boolean result = (object != null && object instanceof PropertyDescriptor); + if (result) { + PropertyDescriptor pd = (PropertyDescriptor) object; + boolean gettersAreEqual = (this.getter == null) && (pd.getReadMethod() == null) + || (this.getter != null) && (this.getter.equals(pd.getReadMethod())); + boolean settersAreEqual = (this.setter == null) && (pd.getWriteMethod() == null) + || (this.setter != null) && (this.setter.equals(pd.getWriteMethod())); + boolean propertyTypesAreEqual = this.getPropertyType() == pd.getPropertyType(); + boolean propertyEditorClassesAreEqual = this.getPropertyEditorClass() == pd + .getPropertyEditorClass(); + boolean boundPropertyAreEqual = this.isBound() == pd.isBound(); + boolean constrainedPropertyAreEqual = this.isConstrained() == pd.isConstrained(); + result = gettersAreEqual && settersAreEqual && propertyTypesAreEqual + && propertyEditorClassesAreEqual && boundPropertyAreEqual + && constrainedPropertyAreEqual; + } + return result; + } + + public void setPropertyEditorClass(Class propertyEditorClass) { + this.propertyEditorClass = propertyEditorClass; + } + + public Class getPropertyType() { + Class result = null; + if (getter != null) { + result = getter.getReturnType(); + } else if (setter != null) { + Class[] parameterTypes = setter.getParameterTypes(); + result = parameterTypes[0]; + } + return result; + } + + public Class getPropertyEditorClass() { + return propertyEditorClass; + } + + public void setConstrained(boolean constrained) { + this.constrained = constrained; + } + + public void setBound(boolean bound) { + this.bound = bound; + } + + public boolean isConstrained() { + return constrained; + } + + public boolean isBound() { + return bound; + } + + boolean hasMethod(Class beanClass, String methodName) { + Method[] methods = findMethods(beanClass, methodName); + return (methods.length > 0); + } + + String createDefaultMethodName(String propertyName, String prefix) { + String result = null; + if (propertyName != null) { + String bos = propertyName.substring(0, 1).toUpperCase(); + String eos = propertyName.substring(1, propertyName.length()); + result = prefix + bos + eos; + } + return result; + } + + Method[] findMethods(Class aClass, String methodName) { + Method[] allMethods = aClass.getMethods(); + Vector matchedMethods = new Vector(); + Method[] result; + for (Method method : allMethods) { + if (method.getName().equals(methodName)) { + matchedMethods.add(method); + } + } + result = new Method[matchedMethods.size()]; + for (int j = 0; j < matchedMethods.size(); ++j) { + result[j] = matchedMethods.elementAt(j); + } + return result; + } + + void setReadMethod(Class beanClass, String getterName) { + boolean result = false; + Method[] getters = findMethods(beanClass, getterName); + for (Method element : getters) { + try { + setReadMethod(element); + result = true; + } catch (IntrospectionException ie) { + } + if (result) { + break; + } + } + } + + void setWriteMethod(Class beanClass, String setterName) throws IntrospectionException { + boolean result = false; + Method[] setters = findMethods(beanClass, setterName); + for (Method element : setters) { + try { + setWriteMethod(element); + result = true; + } catch (IntrospectionException ie) { + } + if (result) { + break; + } + } + } + + public PropertyEditor createPropertyEditor(Object bean) { + PropertyEditor editor; + if (propertyEditorClass == null) { + return null; + } + if (!PropertyEditor.class.isAssignableFrom(propertyEditorClass)) { + // beans.48=Property editor is not assignable from the + // PropertyEditor interface + throw new ClassCastException(Messages.getString("beans.48")); //$NON-NLS-1$ + } + try { + Constructor constr; + try { + // try to look for the constructor with single Object argument + constr = propertyEditorClass.getConstructor(Object.class); + editor = (PropertyEditor) constr.newInstance(bean); + } catch (NoSuchMethodException e) { + // try no-argument constructor + constr = propertyEditorClass.getConstructor(); + editor = (PropertyEditor) constr.newInstance(); + } + } catch (Exception e) { + // beans.47=Unable to instantiate property editor + RuntimeException re = new RuntimeException(Messages.getString("beans.47"), e); //$NON-NLS-1$ + throw re; + } + return editor; + } +} diff --git a/awt/java/beans/PropertyEditor.java b/awt/java/beans/PropertyEditor.java new file mode 100644 index 000000000..65bedea1e --- /dev/null +++ b/awt/java/beans/PropertyEditor.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.beans; + +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Rectangle; + +public interface PropertyEditor { + + public void paintValue(Graphics gfx, Rectangle box); + + public void setAsText(String text) throws IllegalArgumentException; + + public String[] getTags(); + + public String getJavaInitializationString(); + + public String getAsText(); + + public void setValue(Object value); + + public Object getValue(); + + public void removePropertyChangeListener(PropertyChangeListener listener); + + public void addPropertyChangeListener(PropertyChangeListener listener); + + public Component getCustomEditor(); + + public boolean supportsCustomEditor(); + + public boolean isPaintable(); +} diff --git a/awt/java/beans/PropertyEditorManager.java b/awt/java/beans/PropertyEditorManager.java new file mode 100644 index 000000000..ed5582992 --- /dev/null +++ b/awt/java/beans/PropertyEditorManager.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.beans; + +import java.util.HashMap; +import java.util.Map; + +public class PropertyEditorManager { + + private static String[] path = { "org.apache.harmony.beans.editors" }; //$NON-NLS-1$ + + private static final Map, Class> registeredEditors = new HashMap, Class>(); + + public PropertyEditorManager() { + } + + public static void registerEditor(Class targetType, Class editorClass) { + if (targetType == null) { + throw new NullPointerException(); + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPropertiesAccess(); + } + if (editorClass != null) { + registeredEditors.put(targetType, editorClass); + } else { + registeredEditors.remove(targetType); + } + } + + public static synchronized PropertyEditor findEditor(Class targetType) { + if (targetType == null) { + throw new NullPointerException(); + } + + Class editorClass = null; + PropertyEditor editor = null; + + editorClass = registeredEditors.get(targetType); + + if (editorClass == null) { + String editorClassName = targetType.getName() + "Editor"; //$NON-NLS-1$ + ClassLoader loader = targetType.getClassLoader(); + + if (loader == null) { + loader = Thread.currentThread().getContextClassLoader(); + } + + try { + editorClass = Class.forName(editorClassName, true, loader); + } catch (ClassNotFoundException cnfe) { + String shortEditorClassName = editorClassName + .substring(editorClassName.lastIndexOf(".") + 1); //$NON-NLS-1$ + + if (targetType.isPrimitive()) { + shortEditorClassName = shortEditorClassName.substring(0, 1) + .toUpperCase() + + shortEditorClassName.substring(1); + } + + for (String element : path) { + editorClassName = element + "." + shortEditorClassName; //$NON-NLS-1$ + + try { + editorClass = Class.forName(editorClassName, true, + loader); + break; + } catch (Exception e) { + } + } + } catch (Exception e) { + } + } + + if (editorClass != null) { + try { + editor = (PropertyEditor) editorClass.newInstance(); + } catch (Exception e) { + } + } + + return editor; + } + + public static synchronized void setEditorSearchPath(String[] apath) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPropertiesAccess(); + } + + path = apath; + } + + public static synchronized String[] getEditorSearchPath() { + return path; + } +} diff --git a/awt/java/beans/PropertyEditorSupport.java b/awt/java/beans/PropertyEditorSupport.java new file mode 100644 index 000000000..c3929a181 --- /dev/null +++ b/awt/java/beans/PropertyEditorSupport.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.beans; + +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.harmony.beans.internal.nls.Messages; + +public class PropertyEditorSupport implements PropertyEditor { + + Object source = null; + + List listeners = new ArrayList(); + + Object oldValue = null; + + Object newValue = null; + + public PropertyEditorSupport(Object source) { + if (source == null) { + throw new NullPointerException(Messages.getString("beans.0C")); //$NON-NLS-1$ + } + this.source = source; + } + + public PropertyEditorSupport() { + source = this; + } + + public void paintValue(Graphics gfx, Rectangle box) { + } + + public void setAsText(String text) throws IllegalArgumentException { + if (newValue instanceof String) { + setValue(text); + } else { + throw new IllegalArgumentException(text); + } + } + + public String[] getTags() { + return null; + } + + public String getJavaInitializationString() { + return "???"; //$NON-NLS-1$ + } + + public String getAsText() { + return newValue == null ? "null" : newValue.toString(); //$NON-NLS-1$ + } + + public void setValue(Object value) { + this.oldValue = this.newValue; + this.newValue = value; + firePropertyChange(); + } + + public Object getValue() { + return newValue; + } + + public void setSource(Object source) { + if (source == null) { + throw new NullPointerException(Messages.getString("beans.0C")); //$NON-NLS-1$ + } + this.source = source; + } + + public Object getSource() { + return source; + } + + public synchronized void removePropertyChangeListener( + PropertyChangeListener listener) { + if (listeners != null) { + listeners.remove(listener); + } + } + + public synchronized void addPropertyChangeListener( + PropertyChangeListener listener) { + listeners.add(listener); + } + + public Component getCustomEditor() { + return null; + } + + public boolean supportsCustomEditor() { + return false; + } + + public boolean isPaintable() { + return false; + } + + public void firePropertyChange() { + if (listeners.size() > 0) { + PropertyChangeEvent event = new PropertyChangeEvent(source, null, + oldValue, newValue); + Iterator iterator = listeners.iterator(); + + while (iterator.hasNext()) { + PropertyChangeListener listener = iterator.next(); + listener.propertyChange(event); + } + } + } +} diff --git a/awt/java/beans/PropertyVetoException.java b/awt/java/beans/PropertyVetoException.java new file mode 100644 index 000000000..c7f092af9 --- /dev/null +++ b/awt/java/beans/PropertyVetoException.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.beans; + +/** + * Indicates that a proposed property change is unacceptable. + */ +public class PropertyVetoException extends Exception { + + private static final long serialVersionUID = 129596057694162164L; + + private final PropertyChangeEvent evt; + + /** + *

+ * Constructs an instance with a message and the change event. + *

+ * + * @param message + * A description of the veto. + * @param event + * The event that was vetoed. + */ + public PropertyVetoException(String message, PropertyChangeEvent event) { + super(message); + this.evt = event; + } + + /** + *

+ * Gets the property change event. + *

+ * + * @return An instance of {@link PropertyChangeEvent} + */ + public PropertyChangeEvent getPropertyChangeEvent() { + return evt; + } +} diff --git a/awt/javax/imageio/IIOException.java b/awt/javax/imageio/IIOException.java new file mode 100644 index 000000000..c77716c43 --- /dev/null +++ b/awt/javax/imageio/IIOException.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio; + +import java.io.IOException; + +/** + * The IIOException class indicates errors in reading/writing operations. + * + * @since Android 1.0 + */ +public class IIOException extends IOException { + + /** + * The Constant serialVersionUID. + */ + private static final long serialVersionUID = -3216210718638985251L; + + /** + * Instantiates a new IIOException. + * + * @param message + * the detailed message. + */ + public IIOException(String message) { + super(message); + } + + /** + * Instantiates a new IIOException. + * + * @param message + * the detailed message. + * @param cause + * the cause of this exception. + */ + public IIOException(String message, Throwable cause) { + super(message); + initCause(cause); + } +} diff --git a/awt/javax/imageio/IIOImage.java b/awt/javax/imageio/IIOImage.java new file mode 100644 index 000000000..e9e5130cf --- /dev/null +++ b/awt/javax/imageio/IIOImage.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio; + +import javax.imageio.metadata.IIOMetadata; +import java.awt.image.RenderedImage; +import java.awt.image.Raster; +import java.awt.image.BufferedImage; +import java.util.List; + +/** + * The IIOImage class combines the image, image's thumbnail and image's + * metadata. The image can be presented as RenderedImage or Raster object. + * + * @since Android 1.0 + */ +public class IIOImage { + + /** + * The image of this IIOImage. + */ + protected RenderedImage image; + + /** + * The raster of this IIOImage. + */ + protected Raster raster; + + /** + * The list with thumbnails associated with the image. + */ + protected List thumbnails; + + /** + * The metadata associated with the image. + */ + protected IIOMetadata metadata; + + /** + * Instantiates a new IIOImage with the specified RenderedImage, list of + * thumbnails and metadata. + * + * @param image + * the image specified by RenderedImage. + * @param thumbnails + * the list of BufferedImage objects which represent the + * thumbnails of the image. + * @param metadata + * the metadata of the image. + */ + public IIOImage(RenderedImage image, List thumbnails, + IIOMetadata metadata) { + if (image == null) { + throw new IllegalArgumentException("image should not be NULL"); + } + this.raster = null; + this.image = image; + this.thumbnails = thumbnails; + this.metadata = metadata; + } + + /** + * Instantiates a new IIOImage with the specified Raster, list of thumbnails + * and metadata. + * + * @param raster + * the Raster. + * @param thumbnails + * the list of BufferedImage objects which represent the + * thumbnails of Raster data. + * @param metadata + * the metadata. + */ + public IIOImage(Raster raster, List thumbnails, IIOMetadata metadata) { + if (raster == null) { + throw new IllegalArgumentException("raster should not be NULL"); + } + this.image = null; + this.raster = raster; + this.thumbnails = thumbnails; + this.metadata = metadata; + } + + /** + * Gets the RenderedImage object or returns null if this IIOImage object is + * associated with a Raster. + * + * @return the RenderedImage object or null if this IIOImage object is + * associated with a Raster. + */ + public RenderedImage getRenderedImage() { + return image; + } + + /** + * Sets the RenderedImage to this IIOImage object. + * + * @param image + * the RenderedImage to be set to this IIOImage. + */ + public void setRenderedImage(RenderedImage image) { + if (image == null) { + throw new IllegalArgumentException("image should not be NULL"); + } + raster = null; + this.image = image; + } + + /** + * Returns true if the IIOImage object associated with a Raster, or false if + * it's associated with a RenderedImage. + * + * @return true, if the IIOImage object associated with a Raster, or false + * if it's associated with a RenderedImage. + */ + public boolean hasRaster() { + return raster != null; + } + + /** + * Gets the Raster object or returns null if this IIOImage object is + * associated with a RenderedImage. + * + * @return the Raster or null if this IIOImage object is associated with a + * RenderedImage. + */ + public Raster getRaster() { + return raster; + } + + /** + * Sets the Raster to the IIOImage. + * + * @param raster + * the new Raster to the IIOImage. + */ + public void setRaster(Raster raster) { + if (raster == null) { + throw new IllegalArgumentException("raster should not be NULL"); + } + image = null; + this.raster = raster; + } + + /** + * Gets the number of thumbnails for this IIOImage. + * + * @return the number of thumbnails for this IIOImage. + */ + public int getNumThumbnails() { + return thumbnails != null ? thumbnails.size() : 0; + } + + /** + * Gets the thumbnail with the specified index in the list. + * + * @param index + * the index of the thumbnail in the list. + * @return the thumbnail with the specified index in the list. + */ + public BufferedImage getThumbnail(int index) { + if (thumbnails != null) { + return thumbnails.get(index); + } + throw new IndexOutOfBoundsException("no thumbnails were set"); + } + + /** + * Gets the list of thumbnails. + * + * @return the list of thumbnails. + */ + public List getThumbnails() { + return thumbnails; + } + + /** + * Sets the list of thumbnails images to this IIOImage object. + * + * @param thumbnails + * the list of BufferedImage which represent thumbnails. + */ + public void setThumbnails(List thumbnails) { + this.thumbnails = thumbnails; + } + + /** + * Gets the metadata of this IIOImage. + * + * @return the metadata of this IIOImage. + */ + public IIOMetadata getMetadata() { + return metadata; + } + + /** + * Sets the metadata to this IIOImage object. + * + * @param metadata + * the IIOMetadata, or null. + */ + public void setMetadata(IIOMetadata metadata) { + this.metadata = metadata; + } +} diff --git a/awt/javax/imageio/IIOParam.java b/awt/javax/imageio/IIOParam.java new file mode 100644 index 000000000..2ccc9450f --- /dev/null +++ b/awt/javax/imageio/IIOParam.java @@ -0,0 +1,342 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio; + +import java.awt.*; + +/** + * The IIOParam abstract class is superclass for ImageReadParam and + * ImageWriteParam classes and provides methods and variables which they share. + * + * @since Android 1.0 + */ +public abstract class IIOParam { + + /** + * The source region. + */ + protected Rectangle sourceRegion; + + /** + * The source x subsampling. + */ + protected int sourceXSubsampling = 1; + + /** + * The source y subsampling. + */ + protected int sourceYSubsampling = 1; + + /** + * The subsampling x offset. + */ + protected int subsamplingXOffset; + + /** + * The subsampling y offset. + */ + protected int subsamplingYOffset; + + /** + * The source bands. + */ + protected int[] sourceBands; + + /** + * The destination type. + */ + protected ImageTypeSpecifier destinationType; + + /** + * The destination offset. + */ + protected Point destinationOffset = new Point(0, 0); + + /** + * The default controller. + */ + protected IIOParamController defaultController; + + /** + * The controller. + */ + protected IIOParamController controller; + + /** + * Instantiates a new IIOParam. + */ + protected IIOParam() { + } + + /** + * Sets the source region as a Rectangle object. + * + * @param sourceRegion + * the Rectangle which specifies the source region. + */ + public void setSourceRegion(Rectangle sourceRegion) { + if (sourceRegion != null) { + if (sourceRegion.x < 0) { + throw new IllegalArgumentException("x < 0"); + } + if (sourceRegion.y < 0) { + throw new IllegalArgumentException("y < 0"); + } + if (sourceRegion.width <= 0) { + throw new IllegalArgumentException("width <= 0"); + } + if (sourceRegion.height <= 0) { + throw new IllegalArgumentException("height <= 0"); + } + + if (sourceRegion.width <= subsamplingXOffset) { + throw new IllegalArgumentException("width <= subsamplingXOffset"); + } + + if (sourceRegion.height <= subsamplingYOffset) { + throw new IllegalArgumentException("height <= subsamplingXOffset"); + } + // -- clone it to avoid unexpected modifications + this.sourceRegion = (Rectangle)sourceRegion.clone(); + } else { + this.sourceRegion = null; + } + } + + /** + * Gets the source region. + * + * @return the source region as Rectangle. + */ + public Rectangle getSourceRegion() { + if (sourceRegion == null) { + return null; + } + // -- clone it to avoid unexpected modifications + return (Rectangle)sourceRegion.clone(); + } + + /** + * Sets the source subsampling. The sourceXSubsampling and + * sourceYSubsampling parameters specify the number of rows and columns to + * advance after every source pixel. + * + * @param sourceXSubsampling + * the source X subsampling. + * @param sourceYSubsampling + * the source Y subsampling. + * @param subsamplingXOffset + * the subsampling X offset. + * @param subsamplingYOffset + * the subsampling Y offset. + */ + public void setSourceSubsampling(int sourceXSubsampling, int sourceYSubsampling, + int subsamplingXOffset, int subsamplingYOffset) { + + if (sourceXSubsampling <= 0) { + throw new IllegalArgumentException("sourceXSubsampling <= 0"); + } + if (sourceYSubsampling <= 0) { + throw new IllegalArgumentException("sourceYSubsampling <= 0"); + } + + if (subsamplingXOffset <= 0 || subsamplingXOffset >= sourceXSubsampling) { + throw new IllegalArgumentException("subsamplingXOffset is wrong"); + } + + if (subsamplingYOffset <= 0 || subsamplingYOffset >= sourceYSubsampling) { + throw new IllegalArgumentException("subsamplingYOffset is wrong"); + } + + // -- does region contain pixels + if (sourceRegion != null) { + if (sourceRegion.width <= subsamplingXOffset + || sourceRegion.height <= subsamplingYOffset) { + throw new IllegalArgumentException("there are no pixels in region"); + } + } + + this.sourceXSubsampling = sourceXSubsampling; + this.sourceYSubsampling = sourceYSubsampling; + this.subsamplingXOffset = subsamplingXOffset; + this.subsamplingYOffset = subsamplingYOffset; + } + + /** + * Gets the source X subsampling - the number of source columns to advance + * for each pixel. + * + * @return the source X subsampling. + */ + public int getSourceXSubsampling() { + return sourceXSubsampling; + } + + /** + * Gets the source Y subsampling - the number of source rows to advance for + * each pixel. + * + * @return the source Y subsampling. + */ + public int getSourceYSubsampling() { + return sourceYSubsampling; + } + + /** + * Gets the horizontal offset of the subsampling grid. + * + * @return the horizontal offset of the subsampling grid. + */ + public int getSubsamplingXOffset() { + return subsamplingXOffset; + } + + /** + * Gets the vertical offset of the subsampling grid. + * + * @return the vertical offset of the subsampling grid. + */ + public int getSubsamplingYOffset() { + return subsamplingYOffset; + } + + /** + * Sets the indices of the source bands. + * + * @param sourceBands + * the indices of the source bands. + */ + public void setSourceBands(int[] sourceBands) { + // TODO implement + throw new UnsupportedOperationException("not implemented yet"); + } + + /** + * Gets the array of source bands. + * + * @return the array of source bands. + */ + public int[] getSourceBands() { + // TODO implement + throw new UnsupportedOperationException("not implemented yet"); + } + + /** + * Sets the specified ImageTypeSpecifier for the destination image. + * + * @param destinationType + * the ImageTypeSpecifier. + */ + public void setDestinationType(ImageTypeSpecifier destinationType) { + // TODO implement + throw new UnsupportedOperationException("not implemented yet"); + } + + /** + * Gets the type of the destination image as an ImageTypeSpecifier. . + * + * @return the ImageTypeSpecifier. + */ + public ImageTypeSpecifier getDestinationType() { + // TODO implement + throw new UnsupportedOperationException("not implemented yet"); + } + + /** + * Sets the offset in the destination image where the decoded pixels are + * placed as a result of reading, or specified an area to be written while + * writing operation. + * + * @param destinationOffset + * the destination offset. + */ + public void setDestinationOffset(Point destinationOffset) { + if (destinationOffset == null) { + throw new IllegalArgumentException("destinationOffset == null!"); + } + + this.destinationOffset = (Point)destinationOffset.clone(); + } + + /** + * Gets the offset in the destination image for placing pixels. + * + * @return the offset in the destination image. + */ + public Point getDestinationOffset() { + return (Point)destinationOffset.clone(); + } + + /** + * Sets the IIOParamController to this IIOParam object for providing + * settings to this IIOParam. + * + * @param controller + * the new IIOParamController. + */ + public void setController(IIOParamController controller) { + // TODO implement + throw new UnsupportedOperationException("not implemented yet"); + } + + /** + * Gets the current IIOParamController controller for this IIOParam. + * + * @return the current IIOParamController controller for this IIOParam. + */ + public IIOParamController getController() { + // TODO implement + throw new UnsupportedOperationException("not implemented yet"); + } + + /** + * Gets the default IIOParamController controller for this IIOParam. + * + * @return the default IIOParamController controller for this IIOParam, or + * null. + */ + public IIOParamController getDefaultController() { + // TODO implement + throw new UnsupportedOperationException("not implemented yet"); + } + + /** + * Returns true if IIOParamController is installed for this IIOParam. + * + * @return true, if IIOParamController is installed for this IIOParam, false + * otherwise. + */ + public boolean hasController() { + // TODO implement + throw new UnsupportedOperationException("not implemented yet"); + } + + /** + * Activates the controller. + * + * @return true, if successful, false otherwise. + */ + public boolean activateController() { + // TODO implement + throw new UnsupportedOperationException("not implemented yet"); + } +} diff --git a/awt/javax/imageio/IIOParamController.java b/awt/javax/imageio/IIOParamController.java new file mode 100644 index 000000000..338cb25a7 --- /dev/null +++ b/awt/javax/imageio/IIOParamController.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +package javax.imageio; + +/* + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +/** + * The IIOParamController specifies an activate method that invokes the + * controller. + * + * @since Android 1.0 + */ +public interface IIOParamController { + + /** + * Activates the controller. + * + * @param param + * the IIOParam. + * @return true, if the IIOParam has been modified, false otherwise. + */ + boolean activate(IIOParam param); +} diff --git a/awt/javax/imageio/ImageIO.java b/awt/javax/imageio/ImageIO.java new file mode 100644 index 000000000..e0d7ec990 --- /dev/null +++ b/awt/javax/imageio/ImageIO.java @@ -0,0 +1,800 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageOutputStream; +import javax.imageio.spi.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Iterator; +import java.util.Arrays; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; +import java.net.URL; + +/** + * The ImageIO class provides static methods to perform reading and writing + * operations using registered ImageReader and ImageWriter objects. + * + * @since Android 1.0 + */ +public final class ImageIO { + + /** + * The constant registry. + */ + private static final IIORegistry registry = IIORegistry.getDefaultInstance(); + + /** + * Instantiates a new ImageIO. + */ + private ImageIO() { + } + + /** + * Scans for plug-ins in the class path, loads spi classes, and registers + * them with the IIORegistry. + */ + public static void scanForPlugins() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Sets flag which indicates whether a cache file is used when creating + * ImageInputStreams and ImageOutputStreams or not. + * + * @param useCache + * the use cache flag. + */ + public static void setUseCache(boolean useCache) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets the flag which indicates whether a cache file is used when creating + * ImageInputStreams and ImageOutputStreams or not. This method returns the + * current value which is set by setUseCache method. + * + * @return the use cache flag. + */ + public static boolean getUseCache() { + // TODO implement + return false; + } + + /** + * Sets the cache directory. + * + * @param cacheDirectory + * the File which specifies a cache directory. + */ + public static void setCacheDirectory(File cacheDirectory) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets the directory where cache files are created, returned the file which + * is set by setCacheDirectory method, or null. + * + * @return the File object which is set by setCacheDirectory method, or + * null. + */ + public static File getCacheDirectory() { + // TODO implement + // -- null indicates system-dep default temporary directory + return null; + } + + /** + * Creates an ImageInputStream from the specified Object. The specified + * Object should obtain the input source such as File, or InputStream. + * + * @param input + * the input Object such as File, or InputStream. + * @return the ImageInputStream object, or null. + * @throws IOException + * if an I/O exception has occurred. + */ + public static ImageInputStream createImageInputStream(Object input) throws IOException { + + if (input == null) { + throw new IllegalArgumentException("input source cannot be NULL"); + } + + Iterator it = registry.getServiceProviders(ImageInputStreamSpi.class, + true); + + while (it.hasNext()) { + ImageInputStreamSpi spi = it.next(); + if (spi.getInputClass().isInstance(input)) { + return spi.createInputStreamInstance(input); + } + } + return null; + } + + /** + * Creates an ImageOutputStream using the specified Object. The specified + * Object should obtain the output source such as File, or OutputStream. + * + * @param output + * the output Object such as File, or OutputStream. + * @return the ImageOutputStream object, or null. + * @throws IOException + * if an I/O exception has occurred. + */ + public static ImageOutputStream createImageOutputStream(Object output) throws IOException { + if (output == null) { + throw new IllegalArgumentException("output destination cannot be NULL"); + } + + Iterator it = registry.getServiceProviders( + ImageOutputStreamSpi.class, true); + + while (it.hasNext()) { + ImageOutputStreamSpi spi = it.next(); + if (spi.getOutputClass().isInstance(output)) { + // todo - use getUseCache and getCacheDir here + return spi.createOutputStreamInstance(output); + } + } + return null; + } + + /** + * Gets the array of format names as String which can be decoded by + * registered ImageReader objects. + * + * @return the array of format names. + */ + public static String[] getReaderFormatNames() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets the array of MIME types as String which can be decoded by registered + * ImageReader objects. + * + * @return the array of MIME types. + */ + public static String[] getReaderMIMETypes() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets the Iterator of registered ImageReader which are able to decode an + * input data specified by input Object. + * + * @param input + * the input Object with encoded data such as ImageInputStream + * object. + * @return the Iterator of registered ImageReader. + */ + public static Iterator getImageReaders(Object input) { + if (input == null) { + throw new NullPointerException("input cannot be NULL"); + } + + Iterator it = registry.getServiceProviders(ImageReaderSpi.class, + new CanReadFilter(input), true); + + return new SpiIteratorToReadersIteratorWrapper(it); + } + + /** + * Gets the Iterator of registered ImageReader which are able to decode the + * specified format. + * + * @param formatName + * the format name such as "jpeg", or "gif". + * @return the Iterator of registered ImageReader. + */ + public static Iterator getImageReadersByFormatName(String formatName) { + if (formatName == null) { + throw new NullPointerException("format name cannot be NULL"); + } + + Iterator it = registry.getServiceProviders(ImageReaderSpi.class, + new FormatFilter(formatName), true); + + return new SpiIteratorToReadersIteratorWrapper(it); + } + + /** + * Gets the Iterator which lists the registered ImageReader objects that are + * able to decode files with the specified suffix. + * + * @param fileSuffix + * the file suffix such as "jpg". + * @return the Iterator of registered ImageReaders. + */ + public static Iterator getImageReadersBySuffix(String fileSuffix) { + if (fileSuffix == null) { + throw new NullPointerException("suffix cannot be NULL"); + } + Iterator it = registry.getServiceProviders(ImageReaderSpi.class, + new SuffixFilter(fileSuffix), true); + + return new SpiIteratorToReadersIteratorWrapper(it); + } + + /** + * Gets the Iterator of registered ImageReader objects that are able to + * decode files with the specified MIME type. + * + * @param MIMEType + * the MIME type such as "image/jpeg". + * @return the Iterator of registered ImageReaders. + */ + public static Iterator getImageReadersByMIMEType(String MIMEType) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets an array of Strings giving the names of the formats supported by + * registered ImageWriter objects. + * + * @return the array of format names. + */ + public static String[] getWriterFormatNames() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets an array of Strings giving the MIME types of the formats supported + * by registered ImageWriter objects. + * + * @return the array of MIME types. + */ + public static String[] getWriterMIMETypes() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets the Iterator which lists the registered ImageReader objects that are + * able to encode the specified image format. + * + * @param formatName + * the image format name such as "jpeg". + * @return the Iterator of registered ImageWriter. + */ + public static Iterator getImageWritersByFormatName(String formatName) { + if (formatName == null) { + throw new NullPointerException("format name cannot be NULL"); + } + + Iterator it = registry.getServiceProviders(ImageWriterSpi.class, + new FormatFilter(formatName), true); + + return new SpiIteratorToWritersIteratorWrapper(it); + } + + /** + * Gets the Iterator which lists the registered ImageReader objects that are + * able to encode the specified suffix. + * + * @param fileSuffix + * the file suffix such as "jpg". + * @return the Iterator of registered ImageWriter. + */ + public static Iterator getImageWritersBySuffix(String fileSuffix) { + if (fileSuffix == null) { + throw new NullPointerException("suffix cannot be NULL"); + } + Iterator it = registry.getServiceProviders(ImageWriterSpi.class, + new SuffixFilter(fileSuffix), true); + return new SpiIteratorToWritersIteratorWrapper(it); + } + + /** + * Gets the Iterator which lists the registered ImageReader objects that are + * able to encode the specified MIME type. + * + * @param MIMEType + * the MIME type such as "image/jpeg". + * @return the Iterator of registered ImageWriter. + */ + public static Iterator getImageWritersByMIMEType(String MIMEType) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets an ImageWriter object which corresponds to the specified + * ImageReader, or returns null if the specified ImageReader is not + * registered. + * + * @param reader + * the specified ImageReader. + * @return the ImageWriter, or null. + */ + public static ImageWriter getImageWriter(ImageReader reader) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets an ImageReader object which corresponds to the specified + * ImageWriter, or returns null if the specified ImageWriter is not + * registered. + * + * @param writer + * the registered ImageWriter object. + * @return the ImageReader. + */ + public static ImageReader getImageReader(ImageWriter writer) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets the Iterator of ImageWriter objects which are able to encode images + * with the specified ImageTypeSpecifier and format. + * + * @param type + * the ImageTypeSpecifier, which defines layout. + * @param formatName + * the format name. + * @return the Iterator of ImageWriter objects. + */ + public static Iterator getImageWriters(ImageTypeSpecifier type, String formatName) { + if (type == null) { + throw new NullPointerException("type cannot be NULL"); + } + + if (formatName == null) { + throw new NullPointerException("format name cannot be NULL"); + } + + Iterator it = registry.getServiceProviders(ImageWriterSpi.class, + new FormatAndEncodeFilter(type, formatName), true); + + return new SpiIteratorToWritersIteratorWrapper(it); + } + + /** + * Gets the Iterator of registered ImageTranscoders which are able to + * transcode the metadata of the specified ImageReader object to a suitable + * object for encoding by the specified ImageWriter. + * + * @param reader + * the specified ImageReader. + * @param writer + * the specified ImageWriter. + * @return the Iterator of registered ImageTranscoders. + */ + public static Iterator getImageTranscoders(ImageReader reader, + ImageWriter writer) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Reads image data from the specified File and decodes it using the + * appropriate registered ImageReader object. The File is wrapped in an + * ImageInputStream. + * + * @param input + * the File to be read. + * @return the BufferedImage decoded from the specified File, or null. + * @throws IOException + * if an I/O exception has occurred. + */ + public static BufferedImage read(File input) throws IOException { + if (input == null) { + throw new IllegalArgumentException("input == null!"); + } + + ImageInputStream stream = createImageInputStream(input); + return read(stream); + } + + /** + * Reads image data from the specified InputStream and decodes it using an + * appropriate registered an ImageReader object. + * + * @param input + * the InputStream. + * @return the BufferedImage decoded from the specified InputStream, or + * null. + * @throws IOException + * if an I/O exception has occurred. + */ + public static BufferedImage read(InputStream input) throws IOException { + if (input == null) { + throw new IllegalArgumentException("input == null!"); + } + + ImageInputStream stream = createImageInputStream(input); + return read(stream); + } + + /** + * Reads image data from the specified URL and decodes it using the + * appropriate registered ImageReader object. + * + * @param input + * the URL to be read. + * @return the BufferedImage decoded from the specified URL, or null. + * @throws IOException + * if an I/O exception has occurred. + */ + public static BufferedImage read(URL input) throws IOException { + if (input == null) { + throw new IllegalArgumentException("input == null!"); + } + + InputStream stream = input.openStream(); + BufferedImage res = read(stream); + stream.close(); + + return res; + } + + /** + * Reads image data from the specified ImageInputStream and decodes it using + * appropriate registered an ImageReader object. + * + * @param stream + * the ImageInputStream. + * @return the BufferedImage decoded from the specified ImageInputStream, or + * null. + * @throws IOException + * if an I/O exception has occurred. + */ + public static BufferedImage read(ImageInputStream stream) throws IOException { + if (stream == null) { + throw new IllegalArgumentException("stream == null!"); + } + + Iterator imageReaders = getImageReaders(stream); + if (!imageReaders.hasNext()) { + return null; + } + + ImageReader reader = imageReaders.next(); + reader.setInput(stream, false, true); + BufferedImage res = reader.read(0); + reader.dispose(); + + try { + stream.close(); + } catch (IOException e) { + // Stream could be already closed, proceed silently in this case + } + + return res; + } + + /** + * Writes the specified image in the specified format (using an appropriate + * ImageWriter) to the specified ImageOutputStream. + * + * @param im + * the RenderedImage. + * @param formatName + * the format name. + * @param output + * the ImageOutputStream where Image to be written. + * @return true, if Image is written successfully, false otherwise. + * @throws IOException + * if an I/O exception has occurred. + */ + public static boolean write(RenderedImage im, String formatName, ImageOutputStream output) + throws IOException { + + if (im == null) { + throw new IllegalArgumentException("image cannot be NULL"); + } + if (formatName == null) { + throw new IllegalArgumentException("format name cannot be NULL"); + } + if (output == null) { + throw new IllegalArgumentException("output cannot be NULL"); + } + + Iterator it = getImageWriters(ImageTypeSpecifier.createFromRenderedImage(im), + formatName); + if (it.hasNext()) { + ImageWriter writer = it.next(); + writer.setOutput(output); + writer.write(im); + output.flush(); + writer.dispose(); + return true; + } + return false; + } + + /** + * Writes the specified image in the specified format (using an appropriate + * ImageWriter) to the specified File. + * + * @param im + * the RenderedImage. + * @param formatName + * the format name. + * @param output + * the output File where Image to be written. + * @return true, if Image is written successfully, false otherwise. + * @throws IOException + * if an I/O exception has occurred. + */ + public static boolean write(RenderedImage im, String formatName, File output) + throws IOException { + + if (output == null) { + throw new IllegalArgumentException("output cannot be NULL"); + } + + if (output.exists()) { + output.delete(); + } + + ImageOutputStream ios = createImageOutputStream(output); + boolean rt = write(im, formatName, ios); + ios.close(); + return rt; + } + + /** + * Writes the specified image in the specified format (using an appropriate + * ImageWriter) to the specified OutputStream. + * + * @param im + * the RenderedImage. + * @param formatName + * the format name. + * @param output + * the OutputStream where Image is to be written. + * @return true, if Image is written successfully, false otherwise. + * @throws IOException + * if an I/O exception has occurred. + */ + public static boolean write(RenderedImage im, String formatName, OutputStream output) + throws IOException { + + if (output == null) { + throw new IllegalArgumentException("output cannot be NULL"); + } + + ImageOutputStream ios = createImageOutputStream(output); + boolean rt = write(im, formatName, ios); + ios.close(); + return rt; + } + + /** + * Filter to match spi by format name. + */ + static class FormatFilter implements ServiceRegistry.Filter { + + /** + * The name. + */ + private String name; + + /** + * Instantiates a new format filter. + * + * @param name + * the name. + */ + public FormatFilter(String name) { + this.name = name; + } + + public boolean filter(Object provider) { + ImageReaderWriterSpi spi = (ImageReaderWriterSpi)provider; + return Arrays.asList(spi.getFormatNames()).contains(name); + } + } + + /** + * Filter to match spi by format name and encoding possibility. + */ + static class FormatAndEncodeFilter extends FormatFilter { + + /** + * The type. + */ + private ImageTypeSpecifier type; + + /** + * Instantiates a new format and encode filter. + * + * @param type + * the type. + * @param name + * the name. + */ + public FormatAndEncodeFilter(ImageTypeSpecifier type, String name) { + super(name); + this.type = type; + } + + @Override + public boolean filter(Object provider) { + ImageWriterSpi spi = (ImageWriterSpi)provider; + return super.filter(provider) && spi.canEncodeImage(type); + } + } + + /** + * Filter to match spi by suffix. + */ + static class SuffixFilter implements ServiceRegistry.Filter { + + /** + * The suf. + */ + private String suf; + + /** + * Instantiates a new suffix filter. + * + * @param suf + * the suf. + */ + public SuffixFilter(String suf) { + this.suf = suf; + } + + public boolean filter(Object provider) { + ImageReaderWriterSpi spi = (ImageReaderWriterSpi)provider; + return Arrays.asList(spi.getFileSuffixes()).contains(suf); + } + } + + /** + * Filter to match spi by decoding possibility. + */ + static class CanReadFilter implements ServiceRegistry.Filter { + + /** + * The input. + */ + private Object input; + + /** + * Instantiates a new can read filter. + * + * @param input + * the input. + */ + public CanReadFilter(Object input) { + this.input = input; + } + + public boolean filter(Object provider) { + ImageReaderSpi spi = (ImageReaderSpi)provider; + try { + return spi.canDecodeInput(input); + } catch (IOException e) { + return false; + } + } + } + + /** + * Wraps Spi's iterator to ImageWriter iterator. + */ + static class SpiIteratorToWritersIteratorWrapper implements Iterator { + + /** + * The backend. + */ + private Iterator backend; + + /** + * Instantiates a new spi iterator to writers iterator wrapper. + * + * @param backend + * the backend. + */ + public SpiIteratorToWritersIteratorWrapper(Iterator backend) { + this.backend = backend; + } + + /** + * Next. + * + * @return the image writer. + */ + public ImageWriter next() { + try { + return backend.next().createWriterInstance(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Checks for next. + * + * @return true, if successful. + */ + public boolean hasNext() { + return backend.hasNext(); + } + + /** + * Removes the. + */ + public void remove() { + throw new UnsupportedOperationException( + "Use deregisterServiceprovider instead of Iterator.remove()"); + } + } + + /** + * Wraps spi's iterator to ImageReader iterator. + */ + static class SpiIteratorToReadersIteratorWrapper implements Iterator { + + /** + * The backend. + */ + private Iterator backend; + + /** + * Instantiates a new spi iterator to readers iterator wrapper. + * + * @param backend + * the backend. + */ + public SpiIteratorToReadersIteratorWrapper(Iterator backend) { + this.backend = backend; + } + + /** + * Next. + * + * @return the image reader. + */ + public ImageReader next() { + try { + return backend.next().createReaderInstance(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Checks for next. + * + * @return true, if successful. + */ + public boolean hasNext() { + return backend.hasNext(); + } + + /** + * Removes the. + */ + public void remove() { + throw new UnsupportedOperationException( + "Use deregisterServiceprovider instead of Iterator.remove()"); + } + } +} diff --git a/awt/javax/imageio/ImageReadParam.java b/awt/javax/imageio/ImageReadParam.java new file mode 100644 index 000000000..9cc5c5f1b --- /dev/null +++ b/awt/javax/imageio/ImageReadParam.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +package javax.imageio; + +import java.awt.Dimension; +import java.awt.image.BufferedImage; + +/* + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +/** + * The ImageReadParam class provides information to the ImageReader about how an + * image is to be decoded. + * + * @since Android 1.0 + */ +public class ImageReadParam extends IIOParam { + + /** + * This flag indicates if this ImageReadParam supports setting the source + * rendering size. + */ + protected boolean canSetSourceRenderSize; + + /** + * The destination BufferedImage. + */ + protected BufferedImage destination; + + /** + * The destination bands. + */ + protected int[] destinationBands; + + /** + * The minimum progressive pass. + */ + protected int minProgressivePass; + + /** + * The number of progressive passes. + */ + protected int numProgressivePasses; + + /** + * The source render size. + */ + protected Dimension sourceRenderSize; + + /** + * Returns true if this ImageReaderParam supports rendering a source image + * at an arbitrary size. + * + * @return true, if this ImageReaderParam supports rendering a source image + * at an arbitrary size, false otherwise. + */ + public boolean canSetSourceRenderSize() { + return canSetSourceRenderSize; + } + + /** + * Gets the current destination image as BufferedImage. + * + * @return the BufferedImage which represents the destination. + */ + public BufferedImage getDestination() { + return destination; + } + + /** + * Gets the indices of destination bands. + * + * @return the array of destination bands. + */ + public int[] getDestinationBands() { + return destinationBands; + } + + /** + * Gets the index of the maximum pass to be decoded. This method returns + * Integer.MAX_VALUE, if getSourceNumProgressivePasses() method returns + * value that is equal to Integer.MAX_VALUE. Otherwise this method returns + * getSourceMinProgressivePass() + getSourceNumProgressivePasses() - 1. + * + * @return the index of the maximum pass to be decoded. + */ + public int getSourceMaxProgressivePass() { + if (getSourceNumProgressivePasses() == Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return getSourceMinProgressivePass() + getSourceNumProgressivePasses() - 1; + } + + /** + * Gets the index of the minimum progressive pass that is decoded, default + * is 0. + * + * @return the index of the minimum progressive pass that is decoded, + * default is 0. + */ + public int getSourceMinProgressivePass() { + return minProgressivePass; + } + + /** + * Gets the number of progressive passes. The default value is + * Integer.MAX_VALUE. + * + * @return the number of progressive passes. + */ + public int getSourceNumProgressivePasses() { + return numProgressivePasses; + } + + /** + * Gets the dimension of source image which will be rendered during decoding + * process. + * + * @return the source render size. + */ + public Dimension getSourceRenderSize() { + return sourceRenderSize; + } + + /** + * Sets the specified destination image. This image will be used by read, + * readAll, and readRaster methods, and a reference to it will be returned + * by those methods. + * + * @param destination + * the destination image. + */ + public void setDestination(BufferedImage destination) { + this.destination = destination; + } + + /** + * Sets the indices of the destination bands. + * + * @param destinationBands + * the indices of the destination bands. + */ + public void setDestinationBands(int[] destinationBands) { + this.destinationBands = destinationBands; + } + + @Override + public void setDestinationType(ImageTypeSpecifier destinationType) { + this.destinationType = destinationType; + } + + /** + * Sets the source progressive passes. + * + * @param minPass + * the index of the minimum pass to be decoded. + * @param numPasses + * the number of passes to be decoded. + */ + public void setSourceProgressivePasses(int minPass, int numPasses) { + minProgressivePass = minPass; + numProgressivePasses = numPasses; + } + + /** + * Sets the dimension size of source image if an image can be rendered at an + * arbitrary size. + * + * @param size + * the size of rendered image. + * @throws UnsupportedOperationException + * the unsupported operation exception. + */ + public void setSourceRenderSize(Dimension size) throws UnsupportedOperationException { + if (!canSetSourceRenderSize) { + throw new UnsupportedOperationException("can't set source renderer size"); + } + sourceRenderSize = size; + } +} diff --git a/awt/javax/imageio/ImageReader.java b/awt/javax/imageio/ImageReader.java new file mode 100644 index 000000000..cf282ed2a --- /dev/null +++ b/awt/javax/imageio/ImageReader.java @@ -0,0 +1,1162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio; + +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.event.IIOReadWarningListener; +import javax.imageio.event.IIOReadProgressListener; +import javax.imageio.event.IIOReadUpdateListener; +import java.util.Locale; +import java.util.List; +import java.util.Iterator; +import java.util.Set; +import java.io.IOException; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.*; + +/** + * The ImageReader class is an abstract class for decoding images. ImageReader + * objects are instantiated by the service provider interface, ImageReaderSpi + * class, for the specific format. ImageReaderSpi class should be registered + * with the IIORegistry, which uses them for format recognition and presentation + * of available format readers and writers. + * + * @since Android 1.0 + */ +public abstract class ImageReader { + + /** + * The originating provider. + */ + protected ImageReaderSpi originatingProvider; + + /** + * The input object such as ImageInputStream. + */ + protected Object input; + + /** + * The seek forward only. + */ + protected boolean seekForwardOnly; + + /** + * The ignore metadata flag indicates whether current input source has been + * marked as metadata is allowed to be ignored by setInput. + */ + protected boolean ignoreMetadata; + + /** + * The minimum index. + */ + protected int minIndex; + + /** + * The available locales. + */ + protected Locale[] availableLocales; + + /** + * The locale. + */ + protected Locale locale; + + /** + * The list of warning listeners. + */ + protected List warningListeners; + + /** + * The list of warning locales. + */ + protected List warningLocales; + + /** + * The list of progress listeners. + */ + protected List progressListeners; + + /** + * The list of update listeners. + */ + protected List updateListeners; + + /** + * Instantiates a new ImageReader. + * + * @param originatingProvider + * the ImageReaderSpi which instantiates this ImageReader. + */ + protected ImageReader(ImageReaderSpi originatingProvider) { + this.originatingProvider = originatingProvider; + } + + /** + * Gets the format name of this input source. + * + * @return the format name of this input source. + * @throws IOException + * if an I/O exception has occurred. + */ + public String getFormatName() throws IOException { + return originatingProvider.getFormatNames()[0]; + } + + /** + * Gets the ImageReaderSpi which instantiated this ImageReader. + * + * @return the ImageReaderSpi. + */ + public ImageReaderSpi getOriginatingProvider() { + return originatingProvider; + } + + /** + * Sets the specified Object as the input source of this ImageReader. + * + * @param input + * the input source, it can be an ImageInputStream or other + * supported objects. + * @param seekForwardOnly + * indicates whether the stream must be read sequentially from + * its current starting point. + * @param ignoreMetadata + * parameter which indicates if metadata may be ignored during + * reads or not. + */ + public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) { + if (input != null) { + if (!isSupported(input) && !(input instanceof ImageInputStream)) { + throw new IllegalArgumentException("input " + input + " is not supported"); + } + } + this.minIndex = 0; + this.seekForwardOnly = seekForwardOnly; + this.ignoreMetadata = ignoreMetadata; + this.input = input; + } + + /** + * Checks if is supported. + * + * @param input + * the input. + * @return true, if is supported. + */ + private boolean isSupported(Object input) { + ImageReaderSpi spi = getOriginatingProvider(); + if (null != spi) { + Class[] outTypes = spi.getInputTypes(); + for (Class element : outTypes) { + if (element.isInstance(input)) { + return true; + } + } + } + return false; + } + + /** + * Sets the specified Object as the input source of this ImageReader. + * Metadata is not ignored. + * + * @param input + * the input source, it can be an ImageInputStream or other + * supported objects. + * @param seekForwardOnly + * indicates whether the stream must be read sequentially from + * its current starting point. + */ + public void setInput(Object input, boolean seekForwardOnly) { + setInput(input, seekForwardOnly, false); + } + + /** + * Sets the specified Object as the input source of this ImageReader. + * Metadata is not ignored and forward seeking is not required. + * + * @param input + * the input source, it can be ImageInputStream or other objects. + */ + public void setInput(Object input) { + setInput(input, false, false); + } + + /** + * Gets the input source object of this ImageReader, or returns null. + * + * @return the input source object such as ImageInputStream, or null. + */ + public Object getInput() { + return input; + } + + /** + * Checks if the input source supports only forward reading, or not. + * + * @return true, if the input source supports only forward reading, false + * otherwise. + */ + public boolean isSeekForwardOnly() { + return seekForwardOnly; + } + + /** + * Returns true if the current input source allows to metadata to be ignored + * by passing true as the ignoreMetadata argument to the setInput method. + * + * @return true, if the current input source allows to metadata to be + * ignored by passing true as the ignoreMetadata argument to the + * setInput method. + */ + public boolean isIgnoringMetadata() { + return ignoreMetadata; + } + + /** + * Gets the minimum valid index for reading an image, thumbnail, or image + * metadata. + * + * @return the minimum valid index for reading an image, thumbnail, or image + * metadata. + */ + public int getMinIndex() { + return minIndex; + } + + /** + * Gets the available locales. + * + * @return an array of the available locales. + */ + public Locale[] getAvailableLocales() { + return availableLocales; + } + + /** + * Sets the locale to this ImageReader. + * + * @param locale + * the Locale. + */ + public void setLocale(Locale locale) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Gets the locale of this ImageReader. + * + * @return the locale of this ImageReader. + */ + public Locale getLocale() { + return locale; + } + + /** + * Gets the number of images available in the current input source. + * + * @param allowSearch + * the parameter which indicates what a search is required; if + * false, the reader may return -1 without searching. + * @return the number of images. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract int getNumImages(boolean allowSearch) throws IOException; + + /** + * Gets the width of the specified image in input source. + * + * @param imageIndex + * the image index. + * @return the width in pixels. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract int getWidth(int imageIndex) throws IOException; + + /** + * Gets the height of the specified image in input source. + * + * @param imageIndex + * the image index. + * @return the height in pixels. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract int getHeight(int imageIndex) throws IOException; + + /** + * Checks if the storage format of the specified image places an impediment + * on random pixels access or not. + * + * @param imageIndex + * the image's index. + * @return true, if the storage format of the specified image places an + * impediment on random pixels access, false otherwise. + * @throws IOException + * if an I/O exception has occurred. + */ + public boolean isRandomAccessEasy(int imageIndex) throws IOException { + return false; // def + } + + /** + * Gets the aspect ratio (width devided by height) of the image. + * + * @param imageIndex + * the image index. + * @return the aspect ratio of the image. + * @throws IOException + * if an I/O exception has occurred. + */ + public float getAspectRatio(int imageIndex) throws IOException { + return (float)getWidth(imageIndex) / getHeight(imageIndex); + } + + /** + * Gets an ImageTypeSpecifier which indicates the type of the specified + * image. + * + * @param imageIndex + * the image's index. + * @return the ImageTypeSpecifier. + * @throws IOException + * if an I/O exception has occurred. + */ + public ImageTypeSpecifier getRawImageType(int imageIndex) throws IOException { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Gets an Iterator of ImageTypeSpecifier objects which are associated with + * image types that may be used when decoding specified image. + * + * @param imageIndex + * the image index. + * @return an Iterator of ImageTypeSpecifier objects. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract Iterator getImageTypes(int imageIndex) throws IOException; + + /** + * Gets the default ImageReadParam object. + * + * @return the ImageReadParam object. + */ + public ImageReadParam getDefaultReadParam() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Gets an IIOMetadata object for this input source. + * + * @return the IIOMetadata. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract IIOMetadata getStreamMetadata() throws IOException; + + /** + * Gets an IIOMetadata object for this input source. + * + * @param formatName + * the desired metadata format to be used in the returned + * IIOMetadata object. + * @param nodeNames + * the node names of the document. + * @return the IIOMetadata. + * @throws IOException + * if an I/O exception has occurred. + */ + public IIOMetadata getStreamMetadata(String formatName, Set nodeNames) + throws IOException { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Gets the image metadata of the specified image in input source. + * + * @param imageIndex + * the image index. + * @return the IIOMetadata. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract IIOMetadata getImageMetadata(int imageIndex) throws IOException; + + /** + * Gets the image metadata of the specified image input source. + * + * @param imageIndex + * the image index. + * @param formatName + * the desired metadata format to be used in the returned + * IIOMetadata object. + * @param nodeNames + * the node names which can be contained in the document. + * @return the IIOMetadata. + * @throws IOException + * if an I/O exception has occurred. + */ + public IIOMetadata getImageMetadata(int imageIndex, String formatName, Set nodeNames) + throws IOException { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Reads the specified image and returns it as a BufferedImage using the + * default ImageReadParam. + * + * @param imageIndex + * the image index. + * @return the BufferedImage. + * @throws IOException + * if an I/O exception has occurred. + */ + public BufferedImage read(int imageIndex) throws IOException { + return read(imageIndex, null); + } + + /** + * Reads the specified image and returns it as a BufferedImage using the + * specified ImageReadParam. + * + * @param imageIndex + * the image index. + * @param param + * the ImageReadParam. + * @return the BufferedImage. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract BufferedImage read(int imageIndex, ImageReadParam param) throws IOException; + + /** + * Reads the specified image and returns an IIOImage with this image, + * thumbnails, and metadata for this image, using the specified + * ImageReadParam. + * + * @param imageIndex + * the image index. + * @param param + * the ImageReadParam. + * @return the IIOImage. + * @throws IOException + * if an I/O exception has occurred. + */ + public IIOImage readAll(int imageIndex, ImageReadParam param) throws IOException { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Returns an Iterator of IIOImages from the input source. + * + * @param params + * the Iterator of ImageReadParam objects. + * @return the iterator of IIOImages. + * @throws IOException + * if an I/O exception has occurred. + */ + public Iterator readAll(Iterator params) throws IOException { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Checks whether or not this plug-in supports reading a Raster. + * + * @return true, if this plug-in supports reading a Raster, false otherwise. + */ + public boolean canReadRaster() { + return false; // def + } + + /** + * Reads a new Raster object which contains the raw pixel data from the + * image. + * + * @param imageIndex + * the image index. + * @param param + * the ImageReadParam. + * @return the Raster. + * @throws IOException + * if an I/O exception has occurred. + */ + public Raster readRaster(int imageIndex, ImageReadParam param) throws IOException { + throw new UnsupportedOperationException("Unsupported"); + } + + /** + * Checks if the specified image has tiles or not. + * + * @param imageIndex + * the image's index. + * @return true, if the specified image has tiles, false otherwise. + * @throws IOException + * if an I/O exception has occurred. + */ + public boolean isImageTiled(int imageIndex) throws IOException { + return false; // def + } + + /** + * Gets the tile width in the specified image. + * + * @param imageIndex + * the image's index. + * @return the tile width. + * @throws IOException + * if an I/O exception has occurred. + */ + public int getTileWidth(int imageIndex) throws IOException { + return getWidth(imageIndex); // def + } + + /** + * Gets the tile height in the specified image. + * + * @param imageIndex + * the image's index. + * @return the tile height. + * @throws IOException + * if an I/O exception has occurred. + */ + public int getTileHeight(int imageIndex) throws IOException { + return getHeight(imageIndex); // def + } + + /** + * Gets the X coordinate of the upper left corner of the tile grid in the + * specified image. + * + * @param imageIndex + * the image's index. + * @return the X coordinate of the upper left corner of the tile grid. + * @throws IOException + * if an I/O exception has occurred. + */ + public int getTileGridXOffset(int imageIndex) throws IOException { + return 0; // def + } + + /** + * Gets the Y coordinate of the upper left corner of the tile grid in the + * specified image. + * + * @param imageIndex + * the image's index. + * @return the Y coordinate of the upper left corner of the tile grid. + * @throws IOException + * if an I/O exception has occurred. + */ + public int getTileGridYOffset(int imageIndex) throws IOException { + return 0; // def + } + + /** + * Reads the tile specified by the tileX and tileY parameters of the + * specified image and returns it as a BufferedImage. + * + * @param imageIndex + * the image index. + * @param tileX + * the X index of tile. + * @param tileY + * the Y index of tile. + * @return the BufferedImage. + * @throws IOException + * if an I/O exception has occurred. + */ + public BufferedImage readTile(int imageIndex, int tileX, int tileY) throws IOException { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Reads the tile specified by the tileX and tileY parameters of the + * specified image and returns it as a Raster. + * + * @param imageIndex + * the image index. + * @param tileX + * the X index of tile. + * @param tileY + * the Y index of tile. + * @return the Raster. + * @throws IOException + * if an I/O exception has occurred. + */ + public Raster readTileRaster(int imageIndex, int tileX, int tileY) throws IOException { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Reads the specified image using the specified ImageReadParam and returns + * it as a RenderedImage. + * + * @param imageIndex + * the image index. + * @param param + * the ImageReadParam. + * @return the RenderedImage. + * @throws IOException + * if an I/O exception has occurred. + */ + public RenderedImage readAsRenderedImage(int imageIndex, ImageReadParam param) + throws IOException { + return read(imageIndex, param); + } + + /** + * Returns true if the image format supported by this reader supports + * thumbnail preview images. + * + * @return true, if the image format supported by this reader supports + * thumbnail preview images, false otherwise. + */ + public boolean readerSupportsThumbnails() { + return false; // def + } + + /** + * Checks if the specified image has thumbnails or not. + * + * @param imageIndex + * the image's index. + * @return true, if the specified image has thumbnails, false otherwise. + * @throws IOException + * if an I/O exception has occurred. + */ + public boolean hasThumbnails(int imageIndex) throws IOException { + return getNumThumbnails(imageIndex) > 0; // def + } + + /** + * Gets the number of thumbnails for the specified image. + * + * @param imageIndex + * the image's index. + * @return the number of thumbnails. + * @throws IOException + * if an I/O exception has occurred. + */ + public int getNumThumbnails(int imageIndex) throws IOException { + return 0; // def + } + + /** + * Gets the width of the specified thumbnail for the specified image. + * + * @param imageIndex + * the image's index. + * @param thumbnailIndex + * the thumbnail's index. + * @return the thumbnail width. + * @throws IOException + * if an I/O exception has occurred. + */ + public int getThumbnailWidth(int imageIndex, int thumbnailIndex) throws IOException { + return readThumbnail(imageIndex, thumbnailIndex).getWidth(); // def + } + + /** + * Gets the height of the specified thumbnail for the specified image. + * + * @param imageIndex + * the image's index. + * @param thumbnailIndex + * the thumbnail's index. + * @return the thumbnail height. + * @throws IOException + * if an I/O exception has occurred. + */ + public int getThumbnailHeight(int imageIndex, int thumbnailIndex) throws IOException { + return readThumbnail(imageIndex, thumbnailIndex).getHeight(); // def + } + + /** + * Reads the thumbnail image for the specified image as a BufferedImage. + * + * @param imageIndex + * the image index. + * @param thumbnailIndex + * the thumbnail index. + * @return the BufferedImage. + * @throws IOException + * if an I/O exception has occurred. + */ + public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException { + throw new UnsupportedOperationException("Unsupported"); // def + } + + /** + * Requests an abort operation for current reading operation. + */ + public void abort() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Checks whether or not a request to abort the current read operation has + * been made successfully. + * + * @return true, if the request to abort the current read operation has been + * made successfully, false otherwise. + */ + protected boolean abortRequested() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Clears all previous abort request, and abortRequested returns false after + * calling this method. + */ + protected void clearAbortRequest() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Adds the IIOReadWarningListener. + * + * @param listener + * the IIOReadWarningListener. + */ + public void addIIOReadWarningListener(IIOReadWarningListener listener) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Removes the specified IIOReadWarningListener. + * + * @param listener + * the IIOReadWarningListener to be removed. + */ + public void removeIIOReadWarningListener(IIOReadWarningListener listener) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Removes all registered IIOReadWarningListeners. + */ + public void removeAllIIOReadWarningListeners() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Adds the IIOReadProgressListener. + * + * @param listener + * the IIOReadProgressListener. + */ + public void addIIOReadProgressListener(IIOReadProgressListener listener) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Removes the specified IIOReadProgressListener. + * + * @param listener + * the IIOReadProgressListener to be removed. + */ + public void removeIIOReadProgressListener(IIOReadProgressListener listener) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Removes registered IIOReadProgressListeners. + */ + public void removeAllIIOReadProgressListeners() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Adds the IIOReadUpdateListener. + * + * @param listener + * the IIOReadUpdateListener. + */ + public void addIIOReadUpdateListener(IIOReadUpdateListener listener) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Removes the specified IIOReadUpdateListener. + * + * @param listener + * the IIOReadUpdateListener to be removed. + */ + public void removeIIOReadUpdateListener(IIOReadUpdateListener listener) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Removes registered IIOReadUpdateListeners. + */ + public void removeAllIIOReadUpdateListeners() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the start of an sequence of image reads by calling the + * sequenceStarted method on all registered IIOReadProgressListeners. + * + * @param minIndex + * the minimum index. + */ + protected void processSequenceStarted(int minIndex) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the completion of an sequence of image reads by calling + * sequenceComplete method on all registered IIOReadProgressListeners. + */ + protected void processSequenceComplete() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the start of an image read by calling the imageStarted method + * on all registered IIOReadProgressListeners. + * + * @param imageIndex + * the image index. + */ + protected void processImageStarted(int imageIndex) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the current percentage of image completion by calling the + * imageProgress method on all registered IIOReadProgressListeners. + * + * @param percentageDone + * the percentage done. + */ + protected void processImageProgress(float percentageDone) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes image completion by calling the imageComplete method on all + * registered IIOReadProgressListeners. + */ + protected void processImageComplete() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the start of a thumbnail read by calling the thumbnailStarted + * method on all registered IIOReadProgressListeners. + * + * @param imageIndex + * the image index. + * @param thumbnailIndex + * the thumbnail index. + */ + protected void processThumbnailStarted(int imageIndex, int thumbnailIndex) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the current percentage of thumbnail completion by calling the + * thumbnailProgress method on all registered IIOReadProgressListeners. + * + * @param percentageDone + * the percentage done. + */ + protected void processThumbnailProgress(float percentageDone) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the completion of a thumbnail read by calling the + * thumbnailComplete method on all registered IIOReadProgressListeners. + */ + protected void processThumbnailComplete() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes a read aborted event by calling the readAborted method on all + * registered IIOReadProgressListeners. + */ + protected void processReadAborted() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the beginning of a progressive pass by calling the passStarted + * method on all registered IIOReadUpdateListeners. + * + * @param theImage + * the image to be updated. + * @param pass + * the current pass index. + * @param minPass + * the minimum pass index. + * @param maxPass + * the maximum pass index. + * @param minX + * the X coordinate of of the upper left pixel. + * @param minY + * the Y coordinate of of the upper left pixel. + * @param periodX + * the horizontal separation between pixels. + * @param periodY + * the vertical separation between pixels. + * @param bands + * the number of affected bands. + */ + protected void processPassStarted(BufferedImage theImage, int pass, int minPass, int maxPass, + int minX, int minY, int periodX, int periodY, int[] bands) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the update of a set of samples by calling the imageUpdate + * method on all registered IIOReadUpdateListeners. + * + * @param theImage + * the image to be updated. + * @param minX + * the X coordinate of the upper left pixel. + * @param minY + * the Y coordinate of the upper left pixel. + * @param width + * the width of updated area. + * @param height + * the height of updated area. + * @param periodX + * the horizontal separation between pixels. + * @param periodY + * the vertical separation between pixels. + * @param bands + * the number of affected bands. + */ + protected void processImageUpdate(BufferedImage theImage, int minX, int minY, int width, + int height, int periodX, int periodY, int[] bands) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the end of a progressive pass by calling passComplete method of + * registered IIOReadUpdateListeners. + * + * @param theImage + * the image to be updated. + */ + protected void processPassComplete(BufferedImage theImage) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the beginning of a thumbnail progressive pass by calling the + * thumbnailPassStarted method on all registered IIOReadUpdateListeners. + * + * @param theThumbnail + * the thumbnail to be updated. + * @param pass + * the current pass index. + * @param minPass + * the minimum pass index. + * @param maxPass + * the maximum pass index. + * @param minX + * the X coordinate of the upper left pixel. + * @param minY + * the Y coordinate of the upper left pixel. + * @param periodX + * the horizontal separation between pixels. + * @param periodY + * the vertical separation between pixels. + * @param bands + * the number of affected bands. + */ + protected void processThumbnailPassStarted(BufferedImage theThumbnail, int pass, int minPass, + int maxPass, int minX, int minY, int periodX, int periodY, int[] bands) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the update of a set of samples in a thumbnail image by calling + * the thumbnailUpdate method on all registered IIOReadUpdateListeners. + * + * @param theThumbnail + * the thumbnail to be updated. + * @param minX + * the X coordinate of the upper left pixel. + * @param minY + * the Y coordinate of the upper left pixel. + * @param width + * the total width of the updated area. + * @param height + * the total height of the updated area. + * @param periodX + * the horizontal separation between pixels. + * @param periodY + * the vertical separation between pixels. + * @param bands + * the number of affected bands. + */ + protected void processThumbnailUpdate(BufferedImage theThumbnail, int minX, int minY, + int width, int height, int periodX, int periodY, int[] bands) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes the end of a thumbnail progressive pass by calling the + * thumbnailPassComplete method on all registered IIOReadUpdateListeners. + * + * @param theThumbnail + * the thumbnail to be updated. + */ + protected void processThumbnailPassComplete(BufferedImage theThumbnail) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes a warning message by calling warningOccurred method of + * registered IIOReadWarningListeners. + * + * @param warning + * the warning. + */ + protected void processWarningOccurred(String warning) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Processes a warning by calling the warningOccurred method of on all + * registered IIOReadWarningListeners. + * + * @param baseName + * the base name of ResourceBundles. + * @param keyword + * the keyword to index the warning among ResourceBundles. + */ + protected void processWarningOccurred(String baseName, String keyword) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Resets this ImageReader. + */ + public void reset() { + // def + setInput(null, false); + setLocale(null); + removeAllIIOReadUpdateListeners(); + removeAllIIOReadWarningListeners(); + removeAllIIOReadProgressListeners(); + clearAbortRequest(); + } + + /** + * Disposes of any resources. + */ + public void dispose() { + // do nothing by def + } + + /** + * Gets the region of source image that should be read with the specified + * width, height and ImageReadParam. + * + * @param param + * the ImageReadParam object, or null. + * @param srcWidth + * the source image's width. + * @param srcHeight + * the source image's height. + * @return the Rectangle of source region. + */ + protected static Rectangle getSourceRegion(ImageReadParam param, int srcWidth, int srcHeight) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Computes the specified source region and the specified destination region + * with the specified the width and height of the source image, an optional + * destination image, and an ImageReadParam. + * + * @param param + * the an ImageReadParam object, or null. + * @param srcWidth + * the source image's width. + * @param srcHeight + * the source image's height. + * @param image + * the destination image. + * @param srcRegion + * the source region. + * @param destRegion + * the destination region. + */ + protected static void computeRegions(ImageReadParam param, int srcWidth, int srcHeight, + BufferedImage image, Rectangle srcRegion, Rectangle destRegion) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Checks the validity of the source and destination band and is called when + * the reader knows the number of bands of the source image and the number + * of bands of the destination image. + * + * @param param + * the ImageReadParam for reading the Image. + * @param numSrcBands + * the number of bands in the source. + * @param numDstBands + * the number of bands in the destination. + */ + protected static void checkReadParamBandSettings(ImageReadParam param, int numSrcBands, + int numDstBands) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Gets the destination image where the decoded data is written. + * + * @param param + * the ImageReadParam. + * @param imageTypes + * the iterator of ImageTypeSpecifier objects. + * @param width + * the width of the image being decoded. + * @param height + * the height of the image being decoded. + * @return the BufferedImage where decoded pixels should be written. + * @throws IIOException + * the IIOException is thrown if there is no suitable + * ImageTypeSpecifier. + */ + protected static BufferedImage getDestination(ImageReadParam param, + Iterator imageTypes, int width, int height) throws IIOException { + throw new UnsupportedOperationException("Not implemented yet"); + } +} diff --git a/awt/javax/imageio/ImageTranscoder.java b/awt/javax/imageio/ImageTranscoder.java new file mode 100644 index 000000000..632d890f8 --- /dev/null +++ b/awt/javax/imageio/ImageTranscoder.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio; + +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.ImageTypeSpecifier; + +/** + * The ImageTranscoder interface is to be implemented by classes that perform + * image transcoding operations, that is, take images written in one format and + * write them in another format using read/write operations. Some image data can + * be lost in such processes. The ImageTranscoder interface converts metadata + * objects (IIOMetadata) of ImageReader to appropriate metadata object for + * ImageWriter. + * + * @since Android 1.0 + */ +public interface ImageTranscoder { + + /** + * Converts the specified IIOMetadata object using the specified + * ImageWriteParam for obtaining writer's metadata structure. + * + * @param inData + * the IIOMetadata. + * @param param + * the ImageWriteParam. + * @return the IIOMetadata, or null. + */ + IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param); + + /** + * Converts the specified IIOMetadata object using the specified + * ImageWriteParam for obtaining writer's metadata structure and + * ImageTypeSpecifier object for obtaining the layout and color information + * of the image for this metadata. + * + * @param inData + * the IIOMetadata. + * @param imageType + * the ImageTypeSpecifier. + * @param param + * the ImageWriteParam. + * @return the IIOMetadata, or null. + */ + IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, + ImageWriteParam param); +} diff --git a/awt/javax/imageio/ImageTypeSpecifier.java b/awt/javax/imageio/ImageTypeSpecifier.java new file mode 100644 index 000000000..505b1c484 --- /dev/null +++ b/awt/javax/imageio/ImageTypeSpecifier.java @@ -0,0 +1,347 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio; + +import java.awt.image.ColorModel; +import java.awt.image.SampleModel; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; +import java.awt.color.ColorSpace; + +/** + * The ImageTypeSpecifier class performs conversion operations on the + * SampleModel and the ColorModel of an image. + * + * @since Android 1.0 + */ +public class ImageTypeSpecifier { + + /** + * The ColorModel of this ImageTypeSpecifier. + */ + protected ColorModel colorModel; + + /** + * The SampleModel of this ImageTypeSpecifier. + */ + protected SampleModel sampleModel; + + /** + * Instantiates a new ImageTypeSpecifier with the specified ColorModel and + * SampleModel objects. + * + * @param colorModel + * the ColorModel. + * @param sampleModel + * the SampleModel. + */ + public ImageTypeSpecifier(ColorModel colorModel, SampleModel sampleModel) { + if (colorModel == null) { + throw new IllegalArgumentException("color model should not be NULL"); + } + if (sampleModel == null) { + throw new IllegalArgumentException("sample model should not be NULL"); + } + if (!colorModel.isCompatibleSampleModel(sampleModel)) { + throw new IllegalArgumentException("color and sample models are not compatible"); + } + + this.colorModel = colorModel; + this.sampleModel = sampleModel; + } + + /** + * Instantiates a new ImageTypeSpecifier using the specified RenderedImage. + * + * @param renderedImage + * the RenderedImage. + */ + public ImageTypeSpecifier(RenderedImage renderedImage) { + if (renderedImage == null) { + throw new IllegalArgumentException("image should not be NULL"); + } + this.colorModel = renderedImage.getColorModel(); + this.sampleModel = renderedImage.getSampleModel(); + } + + /** + * Creates an ImageTypeSpecifier with the specified DirectColorModel and a + * packed SampleModel. + * + * @param colorSpace + * the ColorSpace. + * @param redMask + * the red mask. + * @param greenMask + * the green mask. + * @param blueMask + * the blue mask. + * @param alphaMask + * the alpha mask. + * @param transferType + * the transfer type. + * @param isAlphaPremultiplied + * the parameter indicates if the color channel is pre-multiplied + * by alpha. + * @return the ImageTypeSpecifier. + */ + public static ImageTypeSpecifier createPacked(ColorSpace colorSpace, int redMask, + int greenMask, int blueMask, int alphaMask, int transferType, + boolean isAlphaPremultiplied) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Creates an ImageTypeSpecifier with specified ComponentColorModel and a + * PixelInterleavedSampleModel. + * + * @param colorSpace + * the ColorSpace. + * @param bandOffsets + * the band offsets. + * @param dataType + * the data type. + * @param hasAlpha + * the parameter indicates if alpha channel is needed. + * @param isAlphaPremultiplied + * the parameter indicates if the color channel is pre-multiplied + * by alpha. + * @return the ImageTypeSpecifier. + */ + public static ImageTypeSpecifier createInterleaved(ColorSpace colorSpace, int[] bandOffsets, + int dataType, boolean hasAlpha, boolean isAlphaPremultiplied) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Creates a ImageTypeSpecifier for a image with a BandedSampleModel and a + * ComponentColorModel. + * + * @param colorSpace + * the ColorSpace. + * @param bankIndices + * the bank indices. + * @param bandOffsets + * the band offsets. + * @param dataType + * the data type. + * @param hasAlpha + * the parameter indicates a presence of alpha channel. + * @param isAlphaPremultiplied + * the parameter indicates whether or not color channel is alpha + * pre-multiplied. + * @return the image type specifier + */ + public static ImageTypeSpecifier createBanded(ColorSpace colorSpace, int[] bankIndices, + int[] bandOffsets, int dataType, boolean hasAlpha, boolean isAlphaPremultiplied) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Creates a ImageTypeSpecifier for a grayscale image. + * + * @param bits + * the number of bits per gray value. + * @param dataType + * the data type. + * @param isSigned + * a signed flag. + * @return the ImageTypeSpecifier. + */ + public static ImageTypeSpecifier createGrayscale(int bits, int dataType, boolean isSigned) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Creates a ImageTypeSpecifier for a grayscale image. + * + * @param bits + * the number of bits per gray value. + * @param dataType + * the data type. + * @param isSigned + * a signed flag. + * @param isAlphaPremultiplied + * the parameter indicates if color channel is pre-multiplied by + * alpha, or not. + * @return the ImageTypeSpecifier. + */ + public static ImageTypeSpecifier createGrayscale(int bits, int dataType, boolean isSigned, + boolean isAlphaPremultiplied) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Creates a ImageTypeSpecifier with the indexed image format. + * + * @param redLUT + * the red values of indices. + * @param greenLUT + * the green values of indices. + * @param blueLUT + * the blue values of indices. + * @param alphaLUT + * the alpha values of indices. + * @param bits + * the bits number for each index. + * @param dataType + * the data type. + * @return the ImageTypeSpecifier. + */ + public static ImageTypeSpecifier createIndexed(byte[] redLUT, byte[] greenLUT, byte[] blueLUT, + byte[] alphaLUT, int bits, int dataType) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Creates the ImageTypeSpecifier from the specified buffered image type. + * + * @param bufferedImageType + * the buffered image type. + * @return the ImageTypeSpecifier. + */ + public static ImageTypeSpecifier createFromBufferedImageType(int bufferedImageType) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Creates the ImageTypeSpecifier from the specified RenderedImage. + * + * @param image + * the RenderedImage. + * @return the ImageTypeSpecifier. + */ + public static ImageTypeSpecifier createFromRenderedImage(RenderedImage image) { + if (null == image) { + throw new IllegalArgumentException("image should not be NULL"); + } + return new ImageTypeSpecifier(image); + } + + /** + * Gets the BufferedImage type. + * + * @return the BufferedImage type. + */ + public int getBufferedImageType() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets the number of components. + * + * @return the number of components. + */ + public int getNumComponents() { + return colorModel.getNumComponents(); + } + + /** + * Gets the number of bands. + * + * @return the number of bands. + */ + public int getNumBands() { + return sampleModel.getNumBands(); + } + + /** + * Gets the number of bits per the specified band. + * + * @param band + * the index of band. + * @return the number of bits per the specified band. + */ + public int getBitsPerBand(int band) { + if (band < 0 || band >= getNumBands()) { + throw new IllegalArgumentException(); + } + return sampleModel.getSampleSize(band); + } + + /** + * Gets the SampleModel associated with this ImageTypeSpecifier. + * + * @return the SampleModel associated with this ImageTypeSpecifier. + */ + public SampleModel getSampleModel() { + return sampleModel; + } + + /** + * Gets a compatible SampleModel with the specified width and height. + * + * @param width + * the width. + * @param height + * the height. + * @return the SampleModel. + */ + public SampleModel getSampleModel(int width, int height) { + if ((long)width * height > Integer.MAX_VALUE) { + throw new IllegalArgumentException("width * height > Integer.MAX_VALUE"); + } + return sampleModel.createCompatibleSampleModel(width, height); + } + + /** + * Gets the ColorModel associated with this ImageTypeSpecifier. + * + * @return the ColorModel associated with this ImageTypeSpecifier. + */ + public ColorModel getColorModel() { + return colorModel; + } + + /** + * Creates the BufferedImage with the specified width and height and the + * ColorMadel and SampleModel which are specified by this + * ImageTypeSpecifier. + * + * @param width + * the width of the BufferedImage. + * @param height + * the height of the BufferedImage. + * @return the BufferedImage. + */ + public BufferedImage createBufferedImage(int width, int height) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Compares this ImageTypeSpecifier object with the specified object. + * + * @param o + * the Object to be compared. + * @return true, if the object is an ImageTypeSpecifier with the same data + * as this ImageTypeSpecifier, false otherwise. + */ + @Override + public boolean equals(Object o) { + boolean rt = false; + if (o instanceof ImageTypeSpecifier) { + ImageTypeSpecifier ts = (ImageTypeSpecifier)o; + rt = colorModel.equals(ts.colorModel) && sampleModel.equals(ts.sampleModel); + } + return rt; + } +} \ No newline at end of file diff --git a/awt/javax/imageio/ImageWriteParam.java b/awt/javax/imageio/ImageWriteParam.java new file mode 100644 index 000000000..d661889b2 --- /dev/null +++ b/awt/javax/imageio/ImageWriteParam.java @@ -0,0 +1,664 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio; + +import java.util.Locale; +import java.awt.*; + +/** + * The ImageWriteParam class provides information to an ImageWriter about how an + * image is to be encoded. + * + * @since Android 1.0 + */ +public class ImageWriteParam extends IIOParam { + + /** + * The Constant MODE_DISABLED indicates that stream is not tiled, + * progressive, or compressed. + */ + public static final int MODE_DISABLED = 0; + + /** + * The Constant MODE_DEFAULT indicates that the stream will be tiled, + * progressive, or compressed according to the plug-in's default. + */ + public static final int MODE_DEFAULT = 1; + + /** + * The Constant MODE_EXPLICIT indicates that the stream will be tiled, + * progressive, or compressed according to current settings which are + * defined by set methods. + */ + public static final int MODE_EXPLICIT = 2; + + /** + * The Constant MODE_COPY_FROM_METADATA indicates that the stream will be + * tiled, progressive, or compressed according to stream or image metadata. + */ + public static final int MODE_COPY_FROM_METADATA = 3; + + /** + * Whether the ImageWriter can write tiles. + */ + protected boolean canWriteTiles = false; + + /** + * The tiling mode. + */ + protected int tilingMode = MODE_COPY_FROM_METADATA; + + /** + * The preferred tile sizes. + */ + protected Dimension[] preferredTileSizes = null; + + /** + * The tiling set. + */ + protected boolean tilingSet = false; + + /** + * The tile width. + */ + protected int tileWidth = 0; + + /** + * The tile height. + */ + protected int tileHeight = 0; + + /** + * Whether the ImageWriter can offset tiles. + */ + protected boolean canOffsetTiles = false; + + /** + * The tile grid x offset. + */ + protected int tileGridXOffset = 0; + + /** + * The tile grid y offset. + */ + protected int tileGridYOffset = 0; + + /** + * Whether the ImageWriter can write in progressive mode. + */ + protected boolean canWriteProgressive = false; + + /** + * The progressive mode. + */ + protected int progressiveMode = MODE_COPY_FROM_METADATA; + + /** + * Whether the ImageWriter can write in compressed mode. + */ + protected boolean canWriteCompressed = false; + + /** + * The compression mode. + */ + protected int compressionMode = MODE_COPY_FROM_METADATA; + + /** + * The compression types. + */ + protected String[] compressionTypes = null; + + /** + * The compression type. + */ + protected String compressionType = null; + + /** + * The compression quality. + */ + protected float compressionQuality = 1.0f; + + /** + * The locale. + */ + protected Locale locale = null; + + /** + * Instantiates a new ImageWriteParam. + */ + protected ImageWriteParam() { + } + + /** + * Instantiates a new ImageWriteParam with the specified Locale. + * + * @param locale + * the Locale. + */ + public ImageWriteParam(Locale locale) { + this.locale = locale; + + } + + /** + * Gets the mode for writing the stream in a progressive sequence. + * + * @return the current progressive mode. + */ + public int getProgressiveMode() { + if (canWriteProgressive()) { + return progressiveMode; + } + throw new UnsupportedOperationException("progressive mode is not supported"); + } + + /** + * Returns true if images can be written using increasing quality passes by + * progressive. + * + * @return true if images can be written using increasing quality passes by + * progressive, false otherwise. + */ + public boolean canWriteProgressive() { + return canWriteProgressive; + } + + /** + * Sets the progressive mode which defines whether the stream contains a + * progressive sequence of increasing quality during writing. The + * progressive mode should be one of the following values: MODE_DISABLED, + * MODE_DEFAULT, or MODE_COPY_FROM_METADATA. + * + * @param mode + * the new progressive mode. + */ + public void setProgressiveMode(int mode) { + if (canWriteProgressive()) { + if (mode < MODE_DISABLED || mode > MODE_COPY_FROM_METADATA || mode == MODE_EXPLICIT) { + throw new IllegalArgumentException("mode is not supported"); + } + this.progressiveMode = mode; + } + throw new UnsupportedOperationException("progressive mode is not supported"); + } + + /** + * Returns true if the writer can use tiles with non zero grid offsets while + * writing. + * + * @return true, if the writer can use tiles with non zero grid offsets + * while writing, false otherwise. + */ + public boolean canOffsetTiles() { + return canOffsetTiles; + } + + /** + * Returns true if this writer can write images with compression. + * + * @return true, if this writer can write images with compression, false + * otherwise. + */ + public boolean canWriteCompressed() { + return canWriteCompressed; + } + + /** + * Returns true if the writer can write tiles. + * + * @return true, if the writer can write tiles, false otherwise. + */ + public boolean canWriteTiles() { + return canWriteTiles; + } + + /** + * Check write compressed. + */ + private final void checkWriteCompressed() { + if (!canWriteCompressed()) { + throw new UnsupportedOperationException("Compression not supported."); + } + } + + /** + * Check compression mode. + */ + private final void checkCompressionMode() { + if (getCompressionMode() != MODE_EXPLICIT) { + throw new IllegalStateException("Compression mode not MODE_EXPLICIT!"); + } + } + + /** + * Check compression type. + */ + private final void checkCompressionType() { + if (getCompressionTypes() != null && getCompressionType() == null) { + throw new IllegalStateException("No compression type set!"); + } + } + + /** + * Gets the compression mode. + * + * @return the compression mode if it's supported. + */ + public int getCompressionMode() { + checkWriteCompressed(); + return compressionMode; + } + + /** + * Gets the an array of supported compression types. + * + * @return the an array of supported compression types. + */ + public String[] getCompressionTypes() { + checkWriteCompressed(); + if (compressionTypes != null) { + return compressionTypes.clone(); + } + return null; + } + + /** + * Gets the current compression type, or returns null. + * + * @return the current compression type, or returns null if it is not set. + */ + public String getCompressionType() { + checkWriteCompressed(); + checkCompressionMode(); + return compressionType; + } + + /** + * Gets a bit rate which represents an estimate of the number of bits of + * output data for each bit of input image data with the specified quality. + * + * @param quality + * the quality. + * @return an estimate of the bit rate, or -1.0F if there is no estimate. + */ + public float getBitRate(float quality) { + checkWriteCompressed(); + checkCompressionMode(); + checkCompressionType(); + if (quality < 0 || quality > 1) { + throw new IllegalArgumentException("Quality out-of-bounds!"); + } + return -1.0f; + } + + /** + * Gets the compression quality. + * + * @return the compression quality. + */ + public float getCompressionQuality() { + checkWriteCompressed(); + checkCompressionMode(); + checkCompressionType(); + return compressionQuality; + } + + /** + * Gets the array of compression quality descriptions. + * + * @return the string array of compression quality descriptions. + */ + public String[] getCompressionQualityDescriptions() { + checkWriteCompressed(); + checkCompressionMode(); + checkCompressionType(); + return null; + } + + /** + * Gets an array of floats which describes compression quality levels. + * + * @return the array of compression quality values. + */ + public float[] getCompressionQualityValues() { + checkWriteCompressed(); + checkCompressionMode(); + checkCompressionType(); + return null; + } + + /** + * Gets the locale of this ImageWriteParam. + * + * @return the locale of this ImageWriteParam. + */ + public Locale getLocale() { + return locale; + } + + /** + * Gets the current compression type using the current Locale. + * + * @return the current compression type using the current Locale. + */ + public String getLocalizedCompressionTypeName() { + checkWriteCompressed(); + checkCompressionMode(); + + String compressionType = getCompressionType(); + if (compressionType == null) { + throw new IllegalStateException("No compression type set!"); + } + return compressionType; + + } + + /** + * Check tiling. + */ + private final void checkTiling() { + if (!canWriteTiles()) { + throw new UnsupportedOperationException("Tiling not supported!"); + } + } + + /** + * Check tiling mode. + */ + private final void checkTilingMode() { + if (getTilingMode() != MODE_EXPLICIT) { + throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!"); + } + } + + /** + * Check tiling params. + */ + private final void checkTilingParams() { + if (!tilingSet) { + throw new IllegalStateException("Tiling parameters not set!"); + } + } + + /** + * Gets the tiling mode if tiling is supported. + * + * @return the tiling mode if tiling is supported. + */ + public int getTilingMode() { + checkTiling(); + return tilingMode; + } + + /** + * Gets an array of Dimensions giving the sizes of the tiles as they are + * encoded in the output file or stream. + * + * @return the preferred tile sizes. + */ + public Dimension[] getPreferredTileSizes() { + checkTiling(); + if (preferredTileSizes == null) { + return null; + } + + Dimension[] retval = new Dimension[preferredTileSizes.length]; + for (int i = 0; i < preferredTileSizes.length; i++) { + retval[i] = new Dimension(retval[i]); + } + return retval; + } + + /** + * Gets the tile grid X offset for encoding. + * + * @return the tile grid X offset for encoding. + */ + public int getTileGridXOffset() { + checkTiling(); + checkTilingMode(); + checkTilingParams(); + return tileGridXOffset; + } + + /** + * Gets the tile grid Y offset for encoding. + * + * @return the tile grid Y offset for encoding. + */ + public int getTileGridYOffset() { + checkTiling(); + checkTilingMode(); + checkTilingParams(); + return tileGridYOffset; + } + + /** + * Gets the tile height in an image as it is written to the output stream. + * + * @return the tile height in an image as it is written to the output + * stream. + */ + public int getTileHeight() { + checkTiling(); + checkTilingMode(); + checkTilingParams(); + return tileHeight; + } + + /** + * Gets the tile width in an image as it is written to the output stream. + * + * @return the tile width in an image as it is written to the output stream. + */ + public int getTileWidth() { + checkTiling(); + checkTilingMode(); + checkTilingParams(); + return tileWidth; + } + + /** + * Checks if the current compression type has lossless compression or not. + * + * @return true, if the current compression type has lossless compression, + * false otherwise. + */ + public boolean isCompressionLossless() { + checkWriteCompressed(); + checkCompressionMode(); + checkCompressionType(); + return true; + } + + /** + * Removes current compression type. + */ + public void unsetCompression() { + checkWriteCompressed(); + checkCompressionMode(); + compressionType = null; + compressionQuality = 1; + } + + /** + * Sets the compression mode to the specified value. The specified mode can + * be one of the predefined constants: MODE_DEFAULT, MODE_DISABLED, + * MODE_EXPLICIT, or MODE_COPY_FROM_METADATA. + * + * @param mode + * the new compression mode to be set. + */ + public void setCompressionMode(int mode) { + checkWriteCompressed(); + switch (mode) { + case MODE_EXPLICIT: { + compressionMode = mode; + unsetCompression(); + break; + } + case MODE_COPY_FROM_METADATA: + case MODE_DISABLED: + case MODE_DEFAULT: { + compressionMode = mode; + break; + } + default: { + throw new IllegalArgumentException("Illegal value for mode!"); + } + } + } + + /** + * Sets the compression quality. The value should be between 0 and 1. + * + * @param quality + * the new compression quality, float value between 0 and 1. + */ + public void setCompressionQuality(float quality) { + checkWriteCompressed(); + checkCompressionMode(); + checkCompressionType(); + if (quality < 0 || quality > 1) { + throw new IllegalArgumentException("Quality out-of-bounds!"); + } + compressionQuality = quality; + } + + /** + * Sets the compression type. The specified string should be one of the + * values returned by getCompressionTypes method. + * + * @param compressionType + * the new compression type. + */ + public void setCompressionType(String compressionType) { + checkWriteCompressed(); + checkCompressionMode(); + + if (compressionType == null) { // Don't check anything + this.compressionType = null; + } else { + String[] compressionTypes = getCompressionTypes(); + if (compressionTypes == null) { + throw new UnsupportedOperationException("No settable compression types"); + } + + for (int i = 0; i < compressionTypes.length; i++) { + if (compressionTypes[i].equals(compressionType)) { + this.compressionType = compressionType; + return; + } + } + + // Compression type is not in the list. + throw new IllegalArgumentException("Unknown compression type!"); + } + } + + /** + * Sets the instruction that tiling should be performed for the image in the + * output stream with the specified parameters. + * + * @param tileWidth + * the tile's width. + * @param tileHeight + * the tile's height. + * @param tileGridXOffset + * the tile grid's x offset. + * @param tileGridYOffset + * the tile grid's y offset. + */ + public void setTiling(int tileWidth, int tileHeight, int tileGridXOffset, int tileGridYOffset) { + checkTiling(); + checkTilingMode(); + + if (!canOffsetTiles() && (tileGridXOffset != 0 || tileGridYOffset != 0)) { + throw new UnsupportedOperationException("Can't offset tiles!"); + } + + if (tileWidth <= 0 || tileHeight <= 0) { + throw new IllegalArgumentException("tile dimensions are non-positive!"); + } + + Dimension preferredTileSizes[] = getPreferredTileSizes(); + if (preferredTileSizes != null) { + for (int i = 0; i < preferredTileSizes.length; i += 2) { + Dimension minSize = preferredTileSizes[i]; + Dimension maxSize = preferredTileSizes[i + 1]; + if (tileWidth < minSize.width || tileWidth > maxSize.width + || tileHeight < minSize.height || tileHeight > maxSize.height) { + throw new IllegalArgumentException("Illegal tile size!"); + } + } + } + + tilingSet = true; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.tileGridXOffset = tileGridXOffset; + this.tileGridYOffset = tileGridYOffset; + } + + /** + * Clears all tiling settings. + */ + public void unsetTiling() { + checkTiling(); + checkTilingMode(); + + tilingSet = false; + tileWidth = 0; + tileHeight = 0; + tileGridXOffset = 0; + tileGridYOffset = 0; + } + + /** + * Sets the tiling mode. The specified mode should be one of the following + * values: MODE_DISABLED, MODE_DEFAULT, MODE_EXPLICIT, or + * MODE_COPY_FROM_METADATA. + * + * @param mode + * the new tiling mode. + */ + public void setTilingMode(int mode) { + checkTiling(); + + switch (mode) { + case MODE_EXPLICIT: { + tilingMode = mode; + unsetTiling(); + break; + } + case MODE_COPY_FROM_METADATA: + case MODE_DISABLED: + case MODE_DEFAULT: { + tilingMode = mode; + break; + } + default: { + throw new IllegalArgumentException("Illegal value for mode!"); + } + } + } +} diff --git a/awt/javax/imageio/ImageWriter.java b/awt/javax/imageio/ImageWriter.java new file mode 100644 index 000000000..86879e040 --- /dev/null +++ b/awt/javax/imageio/ImageWriter.java @@ -0,0 +1,1001 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import javax.imageio.event.IIOWriteProgressListener; +import javax.imageio.event.IIOWriteWarningListener; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.spi.ImageWriterSpi; + +/** + * The ImageWriter class is an abstract class for encoding images. ImageWriter + * objects are instantiated by the service provider interface, ImageWriterSpi + * class, for the specific format. ImageWriterSpi class should be registered + * with the IIORegistry, which uses them for format recognition and presentation + * of available format readers and writers. + * + * @since Android 1.0 + */ +public abstract class ImageWriter implements ImageTranscoder { + + /** + * The available locales. + */ + protected Locale[] availableLocales; + + /** + * The locale. + */ + protected Locale locale; + + /** + * The originating provider. + */ + protected ImageWriterSpi originatingProvider; + + /** + * The output. + */ + protected Object output; + + /** + * The progress listeners. + */ + protected List progressListeners; + + /** + * The warning listeners. + */ + protected List warningListeners; + + /** + * The warning locales. + */ + protected List warningLocales; + + // Indicates that abort operation is requested + // Abort mechanism should be thread-safe + /** The aborted. */ + private boolean aborted; + + /** + * Instantiates a new ImageWriter. + * + * @param originatingProvider + * the ImageWriterSpi which instantiates this ImageWriter. + */ + protected ImageWriter(ImageWriterSpi originatingProvider) { + this.originatingProvider = originatingProvider; + } + + public abstract IIOMetadata convertStreamMetadata(IIOMetadata iioMetadata, + ImageWriteParam imageWriteParam); + + public abstract IIOMetadata convertImageMetadata(IIOMetadata iioMetadata, + ImageTypeSpecifier imageTypeSpecifier, ImageWriteParam imageWriteParam); + + /** + * Gets the ImageWriterSpi which instantiated this ImageWriter. + * + * @return the ImageWriterSpi. + */ + public ImageWriterSpi getOriginatingProvider() { + return originatingProvider; + } + + /** + * Processes the start of an image read by calling their imageStarted method + * of registered IIOWriteProgressListeners. + * + * @param imageIndex + * the image index. + */ + protected void processImageStarted(int imageIndex) { + if (null != progressListeners) { + for (IIOWriteProgressListener listener : progressListeners) { + listener.imageStarted(this, imageIndex); + } + } + } + + /** + * Processes the current percentage of image completion by calling + * imageProgress method of registered IIOWriteProgressListener. + * + * @param percentageDone + * the percentage done. + */ + protected void processImageProgress(float percentageDone) { + if (null != progressListeners) { + for (IIOWriteProgressListener listener : progressListeners) { + listener.imageProgress(this, percentageDone); + } + } + } + + /** + * Processes image completion by calling imageComplete method of registered + * IIOWriteProgressListeners. + */ + protected void processImageComplete() { + if (null != progressListeners) { + for (IIOWriteProgressListener listener : progressListeners) { + listener.imageComplete(this); + } + } + } + + /** + * Processes a warning message by calling warningOccurred method of + * registered IIOWriteWarningListeners. + * + * @param imageIndex + * the image index. + * @param warning + * the warning. + */ + protected void processWarningOccurred(int imageIndex, String warning) { + if (null == warning) { + throw new NullPointerException("warning message should not be NULL"); + } + if (null != warningListeners) { + for (IIOWriteWarningListener listener : warningListeners) { + listener.warningOccurred(this, imageIndex, warning); + } + } + } + + /** + * Processes a warning message by calling warningOccurred method of + * registered IIOWriteWarningListeners with string from ResourceBundle. + * + * @param imageIndex + * the image index. + * @param bundle + * the name of ResourceBundle. + * @param key + * the keyword. + */ + protected void processWarningOccurred(int imageIndex, String bundle, String key) { + if (warningListeners != null) { // Don't check the parameters + return; + } + + if (bundle == null) { + throw new IllegalArgumentException("baseName == null!"); + } + if (key == null) { + throw new IllegalArgumentException("keyword == null!"); + } + + // Get the context class loader and try to locate the bundle with it + // first + ClassLoader contextClassloader = AccessController + .doPrivileged(new PrivilegedAction() { + public ClassLoader run() { + return Thread.currentThread().getContextClassLoader(); + } + }); + + // Iterate through both listeners and locales + int n = warningListeners.size(); + for (int i = 0; i < n; i++) { + IIOWriteWarningListener listener = warningListeners.get(i); + Locale locale = warningLocales.get(i); + + // Now try to get the resource bundle + ResourceBundle rb; + try { + rb = ResourceBundle.getBundle(bundle, locale, contextClassloader); + } catch (MissingResourceException e) { + try { + rb = ResourceBundle.getBundle(bundle, locale); + } catch (MissingResourceException e1) { + throw new IllegalArgumentException("Bundle not found!"); + } + } + + try { + String warning = rb.getString(key); + listener.warningOccurred(this, imageIndex, warning); + } catch (MissingResourceException e) { + throw new IllegalArgumentException("Resource is missing!"); + } catch (ClassCastException e) { + throw new IllegalArgumentException("Resource is not a String!"); + } + } + } + + /** + * Sets the specified Object to the output of this ImageWriter. + * + * @param output + * the Object which represents destination, it can be + * ImageOutputStream or other objects. + */ + public void setOutput(Object output) { + if (output != null) { + ImageWriterSpi spi = getOriginatingProvider(); + if (null != spi) { + Class[] outTypes = spi.getOutputTypes(); + boolean supported = false; + for (Class element : outTypes) { + if (element.isInstance(output)) { + supported = true; + break; + } + } + if (!supported) { + throw new IllegalArgumentException("output " + output + " is not supported"); + } + } + } + this.output = output; + } + + /** + * Writes a completed image stream that contains the specified image, + * default metadata, and thumbnails to the output. + * + * @param image + * the specified image to be written. + * @throws IOException + * if an I/O exception has occurred during writing. + */ + public void write(IIOImage image) throws IOException { + write(null, image, null); + } + + /** + * Writes a completed image stream that contains the specified rendered + * image, default metadata, and thumbnails to the output. + * + * @param image + * the specified RenderedImage to be written. + * @throws IOException + * if an I/O exception has occurred during writing. + */ + public void write(RenderedImage image) throws IOException { + write(null, new IIOImage(image, null, null), null); + } + + /** + * Writes a completed image stream that contains the specified image, + * metadata and thumbnails to the output. + * + * @param streamMetadata + * the stream metadata, or null. + * @param image + * the specified image to be written, if canWriteRaster() method + * returns false, then Image must contain only RenderedImage. + * @param param + * the ImageWriteParam, or null. + * @throws IOException + * if an error occurs during writing. + */ + public abstract void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) + throws IOException; + + /** + * Disposes of any resources. + */ + public void dispose() { + // def impl. does nothing according to the spec. + } + + /** + * Requests an abort operation for current writing operation. + */ + public synchronized void abort() { + aborted = true; + } + + /** + * Checks whether or not a request to abort the current write operation has + * been made successfully. + * + * @return true, if the request to abort the current write operation has + * been made successfully, false otherwise. + */ + protected synchronized boolean abortRequested() { + return aborted; + } + + /** + * Clears all previous abort request, and abortRequested returns false after + * calling this method. + */ + protected synchronized void clearAbortRequest() { + aborted = false; + } + + /** + * Adds the IIOWriteProgressListener listener. + * + * @param listener + * the IIOWriteProgressListener listener. + */ + public void addIIOWriteProgressListener(IIOWriteProgressListener listener) { + if (listener == null) { + return; + } + + if (progressListeners == null) { + progressListeners = new ArrayList(); + } + + progressListeners.add(listener); + } + + /** + * Adds the IIOWriteWarningListener. + * + * @param listener + * the IIOWriteWarningListener listener. + */ + public void addIIOWriteWarningListener(IIOWriteWarningListener listener) { + if (listener == null) { + return; + } + + if (warningListeners == null) { + warningListeners = new ArrayList(); + warningLocales = new ArrayList(); + } + + warningListeners.add(listener); + warningLocales.add(getLocale()); + } + + /** + * Gets the output object that was set by setOutput method. + * + * @return the output object such as ImageOutputStream, or null if it is not + * set. + */ + public Object getOutput() { + return output; + } + + /** + * Check output return false. + * + * @return true, if successful. + */ + private final boolean checkOutputReturnFalse() { + if (getOutput() == null) { + throw new IllegalStateException("getOutput() == null!"); + } + return false; + } + + /** + * Unsupported operation. + */ + private final void unsupportedOperation() { + if (getOutput() == null) { + throw new IllegalStateException("getOutput() == null!"); + } + throw new UnsupportedOperationException("Unsupported write variant!"); + } + + /** + * Returns true if a new empty image can be inserted at the specified index. + * + * @param imageIndex + * the specified index of image. + * @return true if a new empty image can be inserted at the specified index, + * false otherwise. + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public boolean canInsertEmpty(int imageIndex) throws IOException { + return checkOutputReturnFalse(); + } + + /** + * Returns true if a new image can be inserted at the specified index. + * + * @param imageIndex + * the specified index of image. + * @return true if a new image can be inserted at the specified index, false + * otherwise. + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public boolean canInsertImage(int imageIndex) throws IOException { + return checkOutputReturnFalse(); + } + + /** + * Returns true if the image with the specified index can be removed. + * + * @param imageIndex + * the specified index of image. + * @return true if the image with the specified index can be removed, false + * otherwise. + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public boolean canRemoveImage(int imageIndex) throws IOException { + return checkOutputReturnFalse(); + } + + /** + * Returns true if metadata of the image with the specified index can be + * replaced. + * + * @param imageIndex + * the specified image index. + * @return true if metadata of the image with the specified index can be + * replaced, false otherwise. + * @throws IOException + * if an I/O exception has occurred. + */ + public boolean canReplaceImageMetadata(int imageIndex) throws IOException { + return checkOutputReturnFalse(); + } + + /** + * Returns true if pixels of the image with the specified index can be + * replaced by the replacePixels methods. + * + * @param imageIndex + * the image's index. + * @return true if pixels of the image with the specified index can be + * replaced by the replacePixels methods, false otherwise. + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public boolean canReplacePixels(int imageIndex) throws IOException { + return checkOutputReturnFalse(); + } + + /** + * Returns true if the stream metadata presented in the output can be + * removed. + * + * @return true if the stream metadata presented in the output can be + * removed, false otherwise. + * @throws IOException + * if an I/O exception has occurred. + */ + public boolean canReplaceStreamMetadata() throws IOException { + return checkOutputReturnFalse(); + } + + /** + * Returns true if the writing of a complete image stream which contains a + * single image is supported with undefined pixel values and associated + * metadata and thumbnails to the output. + * + * @return true if the writing of a complete image stream which contains a + * single image is supported, false otherwise. + * @throws IOException + * if an I/O exception has occurred. + */ + public boolean canWriteEmpty() throws IOException { + return checkOutputReturnFalse(); + } + + /** + * Returns true if the methods which taken an IIOImageParameter can deal + * with a Raster source image. + * + * @return true if the methods which taken an IIOImageParameter can deal + * with a Raster source image, false otherwise. + */ + public boolean canWriteRasters() { + return false; + } + + /** + * Returns true if the writer can add an image to stream that already + * contains header information. + * + * @return if the writer can add an image to stream that already contains + * header information, false otherwise. + */ + public boolean canWriteSequence() { + return false; + } + + /** + * Ends the insertion of a new image. + * + * @throws IOException + * if an I/O exception has occurred. + */ + public void endInsertEmpty() throws IOException { + unsupportedOperation(); + } + + /** + * Ends the replace pixels operation. + * + * @throws IOException + * if an I/O exception has occurred. + */ + public void endReplacePixels() throws IOException { + unsupportedOperation(); + } + + /** + * Ends an empty write operation. + * + * @throws IOException + * if an I/O exception has occurred. + */ + public void endWriteEmpty() throws IOException { + unsupportedOperation(); + } + + /** + * Ends the sequence of write operations. + * + * @throws IOException + * if an I/O exception has occurred. + */ + public void endWriteSequence() throws IOException { + unsupportedOperation(); + } + + /** + * Gets an array of available locales. + * + * @return an of array available locales. + */ + public Locale[] getAvailableLocales() { + if (availableLocales == null) { + return null; + } + + return availableLocales.clone(); + } + + /** + * Gets an IIOMetadata object that contains default values for encoding an + * image with the specified type. + * + * @param imageType + * the ImageTypeSpecifier. + * @param param + * the ImageWriteParam. + * @return the IIOMetadata object. + */ + public abstract IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, + ImageWriteParam param); + + /** + * Gets an IIOMetadata object that contains default values for encoding a + * stream of images. + * + * @param param + * the ImageWriteParam. + * @return the IIOMetadata object. + */ + public abstract IIOMetadata getDefaultStreamMetadata(ImageWriteParam param); + + /** + * Gets the current locale of this ImageWriter. + * + * @return the current locale of this ImageWriter. + */ + public Locale getLocale() { + return locale; + } + + /** + * Gets the default write param. Gets a new ImageWriteParam object for this + * ImageWriter with the current Locale. + * + * @return a new ImageWriteParam object for this ImageWriter. + */ + public ImageWriteParam getDefaultWriteParam() { + return new ImageWriteParam(getLocale()); + } + + /** + * Gets the number of thumbnails supported by the format being written with + * supported image type, image write parameters, stream, and image metadata + * objects. + * + * @param imageType + * the ImageTypeSpecifier. + * @param param + * the image's parameters. + * @param streamMetadata + * the stream metadata. + * @param imageMetadata + * the image metadata. + * @return the number of thumbnails supported. + */ + public int getNumThumbnailsSupported(ImageTypeSpecifier imageType, ImageWriteParam param, + IIOMetadata streamMetadata, IIOMetadata imageMetadata) { + return 0; + } + + /** + * Gets the preferred thumbnail sizes. Gets an array of Dimensions with the + * sizes for thumbnail images as they are encoded in the output file or + * stream. + * + * @param imageType + * the ImageTypeSpecifier. + * @param param + * the ImageWriteParam. + * @param streamMetadata + * the stream metadata. + * @param imageMetadata + * the image metadata. + * @return the preferred thumbnail sizes. + */ + public Dimension[] getPreferredThumbnailSizes(ImageTypeSpecifier imageType, + ImageWriteParam param, IIOMetadata streamMetadata, IIOMetadata imageMetadata) { + return null; + } + + /** + * Prepares insertion of an empty image by requesting the insertion of a new + * image into an existing image stream. + * + * @param imageIndex + * the image index. + * @param imageType + * the image type. + * @param width + * the width of the image. + * @param height + * the height of the image. + * @param imageMetadata + * the image metadata, or null. + * @param thumbnails + * the array thumbnails for this image, or null. + * @param param + * the ImageWriteParam, or null. + * @throws IOException + * if an I/O exception has occurred. + */ + public void prepareInsertEmpty(int imageIndex, ImageTypeSpecifier imageType, int width, + int height, IIOMetadata imageMetadata, List thumbnails, + ImageWriteParam param) throws IOException { + unsupportedOperation(); + } + + /** + * Prepares the writer to call the replacePixels method for the specified + * region. + * + * @param imageIndex + * the image's index. + * @param region + * the specified region. + * @throws IOException + * if an I/O exception has occurred. + */ + public void prepareReplacePixels(int imageIndex, Rectangle region) throws IOException { + unsupportedOperation(); + } + + /** + * Prepares the writer for writing an empty image by beginning the process + * of writing a complete image stream that contains a single image with + * undefined pixel values, metadata and thumbnails, to the output. + * + * @param streamMetadata + * the stream metadata. + * @param imageType + * the image type. + * @param width + * the width of the image. + * @param height + * the height of the image. + * @param imageMetadata + * the image's metadata, or null. + * @param thumbnails + * the image's thumbnails, or null. + * @param param + * the image's parameters, or null. + * @throws IOException + * if an I/O exception has occurred. + */ + public void prepareWriteEmpty(IIOMetadata streamMetadata, ImageTypeSpecifier imageType, + int width, int height, IIOMetadata imageMetadata, + List thumbnails, ImageWriteParam param) throws IOException { + unsupportedOperation(); + } + + /** + * Prepares a stream to accept calls of writeToSequence method using the + * metadata object. + * + * @param streamMetadata + * the stream metadata. + * @throws IOException + * if an I/O exception has occurred. + */ + public void prepareWriteSequence(IIOMetadata streamMetadata) throws IOException { + unsupportedOperation(); + } + + /** + * Processes the completion of a thumbnail read by calling their + * thumbnailComplete method of registered IIOWriteProgressListeners. + */ + protected void processThumbnailComplete() { + if (progressListeners != null) { + for (IIOWriteProgressListener listener : progressListeners) { + listener.thumbnailComplete(this); + } + } + } + + /** + * Processes the current percentage of thumbnail completion by calling their + * thumbnailProgress method of registered IIOWriteProgressListeners. + * + * @param percentageDone + * the percentage done. + */ + protected void processThumbnailProgress(float percentageDone) { + if (progressListeners != null) { + for (IIOWriteProgressListener listener : progressListeners) { + listener.thumbnailProgress(this, percentageDone); + } + } + } + + /** + * Processes the start of a thumbnail read by calling thumbnailStarted + * method of registered IIOWriteProgressListeners. + * + * @param imageIndex + * the image index. + * @param thumbnailIndex + * the thumbnail index. + */ + protected void processThumbnailStarted(int imageIndex, int thumbnailIndex) { + if (progressListeners != null) { + for (IIOWriteProgressListener listener : progressListeners) { + listener.thumbnailStarted(this, imageIndex, thumbnailIndex); + } + } + } + + /** + * Processes that the writing has been aborted by calling writeAborted + * method of registered IIOWriteProgressListeners. + */ + protected void processWriteAborted() { + if (progressListeners != null) { + for (IIOWriteProgressListener listener : progressListeners) { + listener.writeAborted(this); + } + } + } + + /** + * Removes the all IIOWriteProgressListener listeners. + */ + public void removeAllIIOWriteProgressListeners() { + progressListeners = null; + } + + /** + * Removes the all IIOWriteWarningListener listeners. + */ + public void removeAllIIOWriteWarningListeners() { + warningListeners = null; + warningLocales = null; + } + + /** + * Removes the specified IIOWriteProgressListener listener. + * + * @param listener + * the registered IIOWriteProgressListener to be removed. + */ + public void removeIIOWriteProgressListener(IIOWriteProgressListener listener) { + if (progressListeners != null && listener != null) { + if (progressListeners.remove(listener) && progressListeners.isEmpty()) { + progressListeners = null; + } + } + } + + /** + * Removes the specified IIOWriteWarningListener listener. + * + * @param listener + * the registered IIOWriteWarningListener listener to be removed. + */ + public void removeIIOWriteWarningListener(IIOWriteWarningListener listener) { + if (warningListeners == null || listener == null) { + return; + } + + int idx = warningListeners.indexOf(listener); + if (idx > -1) { + warningListeners.remove(idx); + warningLocales.remove(idx); + + if (warningListeners.isEmpty()) { + warningListeners = null; + warningLocales = null; + } + } + } + + /** + * Removes the image with the specified index from the stream. + * + * @param imageIndex + * the image's index. + * @throws IOException + * if an I/O exception has occurred. + */ + public void removeImage(int imageIndex) throws IOException { + unsupportedOperation(); + } + + /** + * Replaces image metadata of the image with specified index. + * + * @param imageIndex + * the image's index. + * @param imageMetadata + * the image metadata. + * @throws IOException + * if an I/O exception has occurred. + */ + public void replaceImageMetadata(int imageIndex, IIOMetadata imageMetadata) throws IOException { + unsupportedOperation(); + } + + /** + * Replaces a part of an image presented in the output with the specified + * RenderedImage. + * + * @param image + * the RenderedImage. + * @param param + * the ImageWriteParam. + * @throws IOException + * if an I/O exception has occurred. + */ + public void replacePixels(RenderedImage image, ImageWriteParam param) throws IOException { + unsupportedOperation(); + } + + /** + * Replaces a part of an image presented in the output with the specified + * Raster. + * + * @param raster + * the Raster. + * @param param + * the ImageWriteParam. + * @throws IOException + * if an I/O exception has occurred. + */ + public void replacePixels(Raster raster, ImageWriteParam param) throws IOException { + unsupportedOperation(); + } + + /** + * Replaces the stream metadata of the output with new IIOMetadata. + * + * @param streamMetadata + * the new stream metadata. + * @throws IOException + * if an I/O exception has occurred. + */ + public void replaceStreamMetadata(IIOMetadata streamMetadata) throws IOException { + unsupportedOperation(); + } + + /** + * Sets the locale of this ImageWriter. + * + * @param locale + * the new locale. + */ + public void setLocale(Locale locale) { + if (locale == null) { + this.locale = null; + return; + } + + Locale[] locales = getAvailableLocales(); + boolean validLocale = false; + if (locales != null) { + for (int i = 0; i < locales.length; i++) { + if (locale.equals(locales[i])) { + validLocale = true; + break; + } + } + } + + if (validLocale) { + this.locale = locale; + } else { + throw new IllegalArgumentException("Invalid locale!"); + } + } + + /** + * Resets this ImageWriter. + */ + public void reset() { + setOutput(null); + setLocale(null); + removeAllIIOWriteWarningListeners(); + removeAllIIOWriteProgressListeners(); + clearAbortRequest(); + } + + /** + * Inserts image into existing output stream. + * + * @param imageIndex + * the image index where an image will be written. + * @param image + * the specified image to be written. + * @param param + * the ImageWriteParam, or null. + * @throws IOException + * if an I/O exception has occurred. + */ + public void writeInsert(int imageIndex, IIOImage image, ImageWriteParam param) + throws IOException { + unsupportedOperation(); + } + + /** + * Writes the specified image to the sequence. + * + * @param image + * the image to be written. + * @param param + * the ImageWriteParam, or null. + * @throws IOException + * if an I/O exception has occurred during writing. + */ + public void writeToSequence(IIOImage image, ImageWriteParam param) throws IOException { + unsupportedOperation(); + } +} diff --git a/awt/javax/imageio/event/IIOReadProgressListener.java b/awt/javax/imageio/event/IIOReadProgressListener.java new file mode 100644 index 000000000..294489639 --- /dev/null +++ b/awt/javax/imageio/event/IIOReadProgressListener.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +package javax.imageio.event; + +import java.util.EventListener; +import javax.imageio.ImageReader; + +/** + * The IIOReadProgressListener interface notifies callers about the progress of + * the image and thumbnail reading methods. + * + * @since Android 1.0 + */ +public interface IIOReadProgressListener extends EventListener { + + /** + * Notifies this listener that the image reading has been completed. + * + * @param source + * the ImageReader object which calls this method. + */ + void imageComplete(ImageReader source); + + /** + * Notifies this listener about the degree of completion of the read call. + * + * @param source + * the ImageReader object which calls this method. + * @param percentageDone + * the percentage of decoding done. + */ + void imageProgress(ImageReader source, float percentageDone); + + /** + * Notifies this listener that an image read operation has been started. + * + * @param source + * the ImageReader object which calls this method. + * @param imageIndex + * the index of the image in an input file or stream to be read. + */ + void imageStarted(ImageReader source, int imageIndex); + + /** + * Notifies this listener that a read operation has been aborted. + * + * @param source + * the ImageReader object which calls this method. + */ + void readAborted(ImageReader source); + + /** + * Notifies this listener that a sequence of read operations has been + * completed. + * + * @param source + * the ImageReader object which calls this method. + */ + void sequenceComplete(ImageReader source); + + /** + * Notifies this listener that a sequence of read operation has been + * started. + * + * @param source + * the ImageReader object which calls this method. + * @param minIndex + * the index of the first image to be read. + */ + void sequenceStarted(ImageReader source, int minIndex); + + /** + * Notifies that a thumbnail read operation has been completed. + * + * @param source + * the ImageReader object which calls this method. + */ + void thumbnailComplete(ImageReader source); + + /** + * Notifies this listener about the degree of completion of the read call. + * + * @param source + * the ImageReader object which calls this method. + * @param percentageDone + * the percentage of decoding done. + */ + void thumbnailProgress(ImageReader source, float percentageDone); + + /** + * Notifies this listener that a thumbnail reading operation has been + * started. + * + * @param source + * the ImageReader object which calls this method. + * @param imageIndex + * the index of the image in an input file or stream to be read. + * @param thumbnailIndex + * the index of the thumbnail to be read. + */ + void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex); +} diff --git a/awt/javax/imageio/event/IIOReadUpdateListener.java b/awt/javax/imageio/event/IIOReadUpdateListener.java new file mode 100644 index 000000000..49bdbcb41 --- /dev/null +++ b/awt/javax/imageio/event/IIOReadUpdateListener.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +package javax.imageio.event; + +import java.awt.image.BufferedImage; +import java.util.EventListener; +import javax.imageio.ImageReader; + +/* + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +/** + * The IIOReadUpdateListener interface provides functionality to receive + * notification of pixel updates during image and thumbnail reading operations. + * + * @since Android 1.0 + */ +public interface IIOReadUpdateListener extends EventListener { + + /** + * Notifies this listener that the specified area of the image has been + * updated. + * + * @param source + * the ImageReader object which calls this method. + * @param theImage + * the image to be updated. + * @param minX + * the minimum X coordinate of the pixels in the updated area. + * @param minY + * the minimum Y coordinate of the pixels in the updated area. + * @param width + * the width of updated area. + * @param height + * the height of updated area. + * @param periodX + * the horizontal spacing period between updated pixels, if it + * equals 1, there is no space between pixels. + * @param periodY + * the vertical spacing period between updated pixels, if it + * equals 1, there is no space between pixels. + * @param bands + * the array of integer values indicating the bands being + * updated. + */ + void imageUpdate(ImageReader source, BufferedImage theImage, int minX, int minY, int width, + int height, int periodX, int periodY, int[] bands); + + /** + * Notifies this listener that the current read operation has completed a + * progressive pass. + * + * @param source + * the ImageReader object which calls this method. + * @param theImage + * the image to be updated. + */ + void passComplete(ImageReader source, BufferedImage theImage); + + /** + * Notifies this listener that the current read operation has begun a + * progressive pass. + * + * @param source + * the ImageReader object which calls this method. + * @param theImage + * the image to be updated. + * @param pass + * the number of the pass. + * @param minPass + * the index of the first pass that will be decoded. + * @param maxPass + * the index of the last pass that will be decoded. + * @param minX + * the minimum X coordinate of the pixels in the updated area. + * @param minY + * the minimum Y coordinate of the pixels in the updated area. + * @param periodX + * the horizontal spacing period between updated pixels, if it + * equals 1, there is no space between pixels. + * @param periodY + * the vertical spacing period between updated pixels, if it + * equals 1, there is no space between pixels. + * @param bands + * the array of integer values indicating the bands being + * updated. + */ + void passStarted(ImageReader source, BufferedImage theImage, int pass, int minPass, + int maxPass, int minX, int minY, int periodX, int periodY, int[] bands); + + /** + * Notifies this listener that the current thumbnail read operation has + * completed a progressive pass. + * + * @param source + * the ImageReader object which calls this method. + * @param theImage + * the thumbnail to be updated. + */ + void thumbnailPassComplete(ImageReader source, BufferedImage theImage); + + /** + * Notifies this listener that the current thumbnail read operation has + * begun a progressive pass. + * + * @param source + * the ImageReader object which calls this method. + * @param theThumbnail + * the thumbnail to be updated. + * @param pass + * the number of the pass. + * @param minPass + * the index of the first pass that will be decoded. + * @param maxPass + * the index of the last pass that will be decoded. + * @param minX + * the minimum X coordinate of the pixels in the updated area. + * @param minY + * the minimum Y coordinate of the pixels in the updated area. + * @param periodX + * the horizontal spacing period between updated pixels, if it + * equals 1, there is no space between pixels. + * @param periodY + * the vertical spacing period between updated pixels, if it + * equals 1, there is no space between pixels. + * @param bands + * the array of integer values indicating the bands being + * updated. + */ + void thumbnailPassStarted(ImageReader source, BufferedImage theThumbnail, int pass, + int minPass, int maxPass, int minX, int minY, int periodX, int periodY, int[] bands); + + /** + * Notifies this listener that a specified area of a thumbnail image has + * been updated. + * + * @param source + * the ImageReader object which calls this method. + * @param theThumbnail + * the thumbnail to be updated. + * @param minX + * the minimum X coordinate of the pixels in the updated area. + * @param minY + * the minimum Y coordinate of the pixels in the updated area. + * @param width + * the width of updated area. + * @param height + * the height of updated area. + * @param periodX + * the horizontal spacing period between updated pixels, if it + * equals 1, there is no space between pixels. + * @param periodY + * the vertical spacing period between updated pixels, if it + * equals 1, there is no space between pixels. + * @param bands + * the array of integer values indicating the bands being + * updated. + */ + void thumbnailUpdate(ImageReader source, BufferedImage theThumbnail, int minX, int minY, + int width, int height, int periodX, int periodY, int[] bands); +} diff --git a/awt/javax/imageio/event/IIOReadWarningListener.java b/awt/javax/imageio/event/IIOReadWarningListener.java new file mode 100644 index 000000000..318a5df46 --- /dev/null +++ b/awt/javax/imageio/event/IIOReadWarningListener.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +package javax.imageio.event; + +import java.util.EventListener; +import javax.imageio.ImageReader; + +/* + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +/** + * The IIOReadWarningListener provides methods to receive notification of + * warning messages generated by image and thumbnail reading methods. + * + * @since Android 1.0 + */ +public interface IIOReadWarningListener extends EventListener { + + /** + * Notifies this listener about a warning (non-fatal error) during decoding. + * + * @param source + * the ImageReader object which calls this method. + * @param warning + * the string describing the warning. + */ + public void warningOccurred(ImageReader source, String warning); +} diff --git a/awt/javax/imageio/event/IIOWriteProgressListener.java b/awt/javax/imageio/event/IIOWriteProgressListener.java new file mode 100644 index 000000000..4a2c595ab --- /dev/null +++ b/awt/javax/imageio/event/IIOWriteProgressListener.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.event; + +import javax.imageio.ImageWriter; +import java.util.EventListener; + +/** + * The IIOWriteProgressListener interface provides methods to receive + * notification about the progress of the image and thumbnail writing methods. + * + * @since Android 1.0 + */ +public interface IIOWriteProgressListener extends EventListener { + + /** + * Notifies this listener that an image write operation has been started. + * + * @param source + * the ImageWriter object which calls this method. + * @param imageIndex + * the index of the image being written. + */ + void imageStarted(ImageWriter source, int imageIndex); + + /** + * Notifies this listener about the degree of completion of the write call. + * + * @param source + * the ImageWriter object which calls this method. + * @param percentageDone + * the percentage of encoding done. + */ + void imageProgress(ImageWriter source, float percentageDone); + + /** + * Notifies this listener that the image writing has been completed. + * + * @param source + * the ImageWriter object which calls this method. + */ + void imageComplete(ImageWriter source); + + /** + * Notifies this listener that a thumbnail write operation has been started. + * + * @param source + * the ImageWriter object which calls this method. + * @param imageIndex + * the index of the image being written. + * @param thumbnailIndex + * the index of the thumbnail being written. + */ + void thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex); + + /** + * Notifies this listener about the degree of completion of the write call. + * + * @param source + * the ImageWriter object which calls this method. + * @param percentageDone + * the percentage of encoding done. + */ + void thumbnailProgress(ImageWriter source, float percentageDone); + + /** + * Notifies this listener that a thumbnail write operation has been + * completed. + * + * @param source + * the ImageWriter object which calls this method. + */ + void thumbnailComplete(ImageWriter source); + + /** + * Notifies this listener that writing operation has been aborted. + * + * @param source + * the ImageWriter object which calls this method. + */ + void writeAborted(ImageWriter source); +} diff --git a/awt/javax/imageio/event/IIOWriteWarningListener.java b/awt/javax/imageio/event/IIOWriteWarningListener.java new file mode 100644 index 000000000..8ee41cddd --- /dev/null +++ b/awt/javax/imageio/event/IIOWriteWarningListener.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.event; + +import javax.imageio.ImageWriter; +import java.util.EventListener; + +/** + * The IIOWriteWarningListener provides methods to receive notification of + * warnings generated by image and thumbnail writing methods. + * + * @since Android 1.0 + */ +public interface IIOWriteWarningListener extends EventListener { + + /** + * Notifies this listener about a warning (non-fatal error) during encoding. + * + * @param source + * the ImageWriter object which calls this method. + * @param imageIndex + * the index of the image generating the warning. + * @param warning + * the string describing the warning. + */ + void warningOccurred(ImageWriter source, int imageIndex, String warning); +} diff --git a/awt/javax/imageio/event/package.html b/awt/javax/imageio/event/package.html new file mode 100644 index 000000000..c2fe39fae --- /dev/null +++ b/awt/javax/imageio/event/package.html @@ -0,0 +1,8 @@ + + +

+ This package provides interfaces to handle events which can be fired during the reading or writing of images. +

+ @since Android 1.0 + + diff --git a/awt/javax/imageio/metadata/IIOInvalidTreeException.java b/awt/javax/imageio/metadata/IIOInvalidTreeException.java new file mode 100644 index 000000000..ba906577b --- /dev/null +++ b/awt/javax/imageio/metadata/IIOInvalidTreeException.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.metadata; + +import org.w3c.dom.Node; +import javax.imageio.IIOException; + +/** + * The IIOInvalidTreeException provides notification about fails of + * IIOMetadataNodes tree parsing by IIOMetadata object. + * + * @since Android 1.0 + */ +public class IIOInvalidTreeException extends IIOException { + + /** + * The offending node. + */ + protected Node offendingNode = null; + + /** + * Instantiates an IIOInvalidTreeException with the specified detailed + * message and specified offending Node. + * + * @param message + * the detailed message. + * @param offendingNode + * the offending node. + */ + public IIOInvalidTreeException(String message, Node offendingNode) { + super(message); + this.offendingNode = offendingNode; + } + + /** + * Instantiates a new IIOInvalidTreeException with the specified detailed + * message and specified offending Node. + * + * @param message + * the detailed message. + * @param cause + * the cause of this exception. + * @param offendingNode + * the offending node. + */ + public IIOInvalidTreeException(String message, Throwable cause, Node offendingNode) { + super(message, cause); + this.offendingNode = offendingNode; + } + + /** + * Gets the offending node. + * + * @return the offending node. + */ + public Node getOffendingNode() { + return offendingNode; + } +} diff --git a/awt/javax/imageio/metadata/IIOMetadata.java b/awt/javax/imageio/metadata/IIOMetadata.java new file mode 100644 index 000000000..96cebf98d --- /dev/null +++ b/awt/javax/imageio/metadata/IIOMetadata.java @@ -0,0 +1,391 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.metadata; + +import java.util.ArrayList; + +import org.apache.harmony.x.imageio.metadata.IIOMetadataUtils; +import org.w3c.dom.Node; + +/** + * The class IIOMetadata represents the metadata (bundled with an image) as a + * Dom-type tree. + * + * @since Android 1.0 + */ +public abstract class IIOMetadata { + + /** + * Whether the standard metadata format is supported. + */ + protected boolean standardFormatSupported; + + /** + * The native metadata format name. + */ + protected String nativeMetadataFormatName; + + /** + * The native metadata format class name. + */ + protected String nativeMetadataFormatClassName; + + /** + * The extra metadata format names. + */ + protected String[] extraMetadataFormatNames; + + /** + * The extra metadata format class names. + */ + protected String[] extraMetadataFormatClassNames; + + /** + * The default controller. + */ + protected IIOMetadataController defaultController; + + /** + * The controller. + */ + protected IIOMetadataController controller; + + /** + * Instantiates a new IIOMetadata with no data set. + */ + protected IIOMetadata() { + } + + /** + * Instantiates a new IIOMetadata with the specified data parameters. + * + * @param standardMetadataFormatSupported + * whether the standard metadata format is supported. + * @param nativeMetadataFormatName + * the native metadata format name. + * @param nativeMetadataFormatClassName + * the native metadata format class name. + * @param extraMetadataFormatNames + * the extra metadata format names. + * @param extraMetadataFormatClassNames + * the extra metadata format class names. + */ + protected IIOMetadata(boolean standardMetadataFormatSupported, String nativeMetadataFormatName, + String nativeMetadataFormatClassName, String[] extraMetadataFormatNames, + String[] extraMetadataFormatClassNames) { + standardFormatSupported = standardMetadataFormatSupported; + this.nativeMetadataFormatName = nativeMetadataFormatName; + this.nativeMetadataFormatClassName = nativeMetadataFormatClassName; + if (extraMetadataFormatNames == null) { + if (extraMetadataFormatClassNames != null) { + throw new IllegalArgumentException( + "extraMetadataFormatNames == null && extraMetadataFormatClassNames != null!"); + } + } else { + if (extraMetadataFormatClassNames == null) { + throw new IllegalArgumentException( + "extraMetadataFormatNames != null && extraMetadataFormatClassNames == null!"); + } + if (extraMetadataFormatNames.length == 0) { + throw new IllegalArgumentException("extraMetadataFormatNames.length == 0!"); + } + if (extraMetadataFormatClassNames.length != extraMetadataFormatNames.length) { + throw new IllegalArgumentException( + "extraMetadataFormatClassNames.length != extraMetadataFormatNames.length!"); + } + this.extraMetadataFormatNames = extraMetadataFormatNames.clone(); + this.extraMetadataFormatClassNames = extraMetadataFormatClassNames.clone(); + } + } + + /** + * Gets the metadata as tree-type document. + * + * @param formatName + * the format name. + * @return the node in tree format. + */ + public abstract Node getAsTree(String formatName); + + /** + * Checks if the metadata is read only. + * + * @return true, if the metadata is read only. + */ + public abstract boolean isReadOnly(); + + /** + * Merges the specified tree with this metadata tree. + * + * @param formatName + * the format of the specified tree. + * @param root + * the root node of the metadata tree. + * @throws IIOInvalidTreeException + * if the specified tree is incompatible with the this metadata + * tree. + */ + public abstract void mergeTree(String formatName, Node root) throws IIOInvalidTreeException; + + /** + * Resets the controller. + */ + public abstract void reset(); + + /** + * Gets the controller associated with this metadata document. + * + * @return the controller. + */ + public IIOMetadataController getController() { + return controller; + } + + /** + * Checks whether this metadata has a controller. + * + * @return true, if this metadata has a controller. + */ + public boolean hasController() { + return getController() != null; + } + + /** + * Activate the controller. + * + * @return true, if successful. + */ + public boolean activateController() { + if (!hasController()) { + throw new IllegalStateException("hasController() == false!"); + } + return getController().activate(this); + } + + /** + * Gets the default controller. + * + * @return the default controller. + */ + public IIOMetadataController getDefaultController() { + return defaultController; + } + + /** + * Gets the extra metadata format names. + * + * @return the extra metadata format names. + */ + public String[] getExtraMetadataFormatNames() { + return extraMetadataFormatNames == null ? null : extraMetadataFormatNames.clone(); + } + + /** + * Gets the metadata format. + * + * @param formatName + * the format name. + * @return the metadata format. + */ + public IIOMetadataFormat getMetadataFormat(String formatName) { + return IIOMetadataUtils.instantiateMetadataFormat(formatName, standardFormatSupported, + nativeMetadataFormatName, nativeMetadataFormatClassName, extraMetadataFormatNames, + extraMetadataFormatClassNames); + } + + /** + * Gets the native metadata format name. + * + * @return the native metadata format name. + */ + public String getNativeMetadataFormatName() { + return nativeMetadataFormatName; + } + + /** + * Checks if the standard metadata format is supported. + * + * @return true, if the standard metadata format is supported. + */ + public boolean isStandardMetadataFormatSupported() { + return standardFormatSupported; + } + + /** + * Gets the metadata format names. + * + * @return the metadata format names. + */ + public String[] getMetadataFormatNames() { + ArrayList res = new ArrayList(); + + String nativeMetadataFormatName = getNativeMetadataFormatName(); + boolean standardFormatSupported = isStandardMetadataFormatSupported(); + String extraMetadataFormatNames[] = getExtraMetadataFormatNames(); + + if (standardFormatSupported) { + res.add(IIOMetadataFormatImpl.standardMetadataFormatName); + } + if (nativeMetadataFormatName != null) { + res.add(nativeMetadataFormatName); + } + if (extraMetadataFormatNames != null) { + for (String extraMetadataFormatName : extraMetadataFormatNames) { + res.add(extraMetadataFormatName); + } + } + + return res.size() > 0 ? res.toArray(new String[0]) : null; + } + + /** + * Gets the standard chroma node. + * + * @return the standard chroma node. + */ + protected IIOMetadataNode getStandardChromaNode() { + return null; + } + + /** + * Gets the standard compression node. + * + * @return the standard compression node. + */ + protected IIOMetadataNode getStandardCompressionNode() { + return null; + } + + /** + * Gets the standard data node. + * + * @return the standard data node. + */ + protected IIOMetadataNode getStandardDataNode() { + return null; + } + + /** + * Gets the standard dimension node. + * + * @return the standard dimension node. + */ + protected IIOMetadataNode getStandardDimensionNode() { + return null; + } + + /** + * Gets the standard document node. + * + * @return the standard document node. + */ + protected IIOMetadataNode getStandardDocumentNode() { + return null; + } + + /** + * Gets the standard text node. + * + * @return the standard text node. + */ + protected IIOMetadataNode getStandardTextNode() { + return null; + } + + /** + * Gets the standard tile node. + * + * @return the standard tile node. + */ + protected IIOMetadataNode getStandardTileNode() { + return null; + } + + /** + * Gets the standard transparency node. + * + * @return the standard transparency node. + */ + protected IIOMetadataNode getStandardTransparencyNode() { + return null; + } + + /** + * Gets the metadata as a tree in standard format. + * + * @return the metadata as a tree in standard format. + */ + protected final IIOMetadataNode getStandardTree() { + // Create root node + IIOMetadataNode root = new IIOMetadataNode(IIOMetadataFormatImpl.standardMetadataFormatName); + + Node node; + if ((node = getStandardChromaNode()) != null) { + root.appendChild(node); + } + if ((node = getStandardCompressionNode()) != null) { + root.appendChild(node); + } + if ((node = getStandardDataNode()) != null) { + root.appendChild(node); + } + if ((node = getStandardDimensionNode()) != null) { + root.appendChild(node); + } + if ((node = getStandardDocumentNode()) != null) { + root.appendChild(node); + } + if ((node = getStandardTextNode()) != null) { + root.appendChild(node); + } + if ((node = getStandardTileNode()) != null) { + root.appendChild(node); + } + if ((node = getStandardTransparencyNode()) != null) { + root.appendChild(node); + } + + return root; + } + + /** + * Sets the controller. + * + * @param controller + * the new controller. + */ + public void setController(IIOMetadataController controller) { + this.controller = controller; + } + + /** + * Sets the from tree. + * + * @param formatName + * the name of the metatdata format of the from tree. + * @param root + * the root node of the from tree. + * @throws IIOInvalidTreeException + * if the tree or its format is not compatible with this + * metadata. + */ + public void setFromTree(String formatName, Node root) throws IIOInvalidTreeException { + reset(); + mergeTree(formatName, root); + } +} diff --git a/awt/javax/imageio/metadata/IIOMetadataController.java b/awt/javax/imageio/metadata/IIOMetadataController.java new file mode 100644 index 000000000..140594830 --- /dev/null +++ b/awt/javax/imageio/metadata/IIOMetadataController.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +package javax.imageio.metadata; + +/* + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +/** + * The IIOMetadataController interface provides a method for implementing + * objects to activate the controller without defining how the controller + * obtains values. + * + * @since Android 1.0 + */ +public interface IIOMetadataController { + + /** + * Activates a controller. + * + * @param metadata + * the metadata to be modified. + * @return true, if the IIOMetadata has been modified, false otherwise. + */ + public boolean activate(IIOMetadata metadata); +} diff --git a/awt/javax/imageio/metadata/IIOMetadataFormat.java b/awt/javax/imageio/metadata/IIOMetadataFormat.java new file mode 100644 index 000000000..0e7e6979c --- /dev/null +++ b/awt/javax/imageio/metadata/IIOMetadataFormat.java @@ -0,0 +1,404 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.metadata; + +import javax.imageio.ImageTypeSpecifier; +import java.util.Locale; + +/** + * The Interface IIOMetadataFormat is implemented by classes that describe the + * rules and allowed elements for a metadata document tree. + * + * @since Android 1.0 + */ +public interface IIOMetadataFormat { + + /** + * The CHILD_POLICY_EMPTY. + */ + int CHILD_POLICY_EMPTY = 0; + + /** + * The CHILD_POLICY_ALL. + */ + int CHILD_POLICY_ALL = 1; + + /** + * The CHILD_POLICY_SOME. + */ + int CHILD_POLICY_SOME = 2; + + /** + * The CHILD_POLICY_CHOICE. + */ + int CHILD_POLICY_CHOICE = 3; + + /** + * The CHILD_POLICY_SEQUENCE. + */ + int CHILD_POLICY_SEQUENCE = 4; + + /** + * The CHILD_POLICY_REPEAT. + */ + int CHILD_POLICY_REPEAT = 5; + + /** + * The maximum value for the child policy. + */ + int CHILD_POLICY_MAX = CHILD_POLICY_REPEAT; + + /** + * The DATATYPE_STRING. + */ + int DATATYPE_STRING = 0; + + /** + * The DATATYPE_BOOLEAN. + */ + int DATATYPE_BOOLEAN = 1; + + /** + * The DATATYPE_INTEGER. + */ + int DATATYPE_INTEGER = 2; + + /** + * The DATATYPE_FLOAT. + */ + int DATATYPE_FLOAT = 3; + + /** + * The DATATYPE_DOUBLE. + */ + int DATATYPE_DOUBLE = 4; + + /** + * The VALUE_NONE. + */ + int VALUE_NONE = 0; + + /** + * The VALUE_ARBITRARY. + */ + int VALUE_ARBITRARY = 1; + + /** + * The VALUE_RANGE. + */ + int VALUE_RANGE = 2; + + /** + * The VALUE_RANGE_MIN_INCLUSIVE_MASK. + */ + int VALUE_RANGE_MIN_INCLUSIVE_MASK = 4; + + /** + * The VALUE_RANGE_MAX_INCLUSIVE_MASK. + */ + int VALUE_RANGE_MAX_INCLUSIVE_MASK = 8; + + /** + * The VALUE_ENUMERATION. + */ + int VALUE_ENUMERATION = 16; + + /** + * The VALUE_LIST. + */ + int VALUE_LIST = 32; + + /** + * The VALUE_RANGE_MIN_INCLUSIVE. + */ + int VALUE_RANGE_MIN_INCLUSIVE = VALUE_RANGE | VALUE_RANGE_MIN_INCLUSIVE_MASK; + + /** + * The VALUE_RANGE_MAX_INCLUSIVE. + */ + int VALUE_RANGE_MAX_INCLUSIVE = VALUE_RANGE | VALUE_RANGE_MAX_INCLUSIVE_MASK; + + /** + * The VALUE_RANGE_MIN_MAX_INCLUSIVE. + */ + int VALUE_RANGE_MIN_MAX_INCLUSIVE = VALUE_RANGE | VALUE_RANGE_MIN_INCLUSIVE_MASK + | VALUE_RANGE_MAX_INCLUSIVE_MASK; + + /** + * Tells whether the specified element is allowed for the specified image + * type. + * + * @param elementName + * the element name. + * @param imageType + * the image type. + * @return true, if the specified element is allowed for the specified image + * type. + */ + boolean canNodeAppear(String elementName, ImageTypeSpecifier imageType); + + /** + * Gets data type of the specified attribute of the specified element. + * + * @param elementName + * the element name. + * @param attrName + * the attribute name. + * @return the attribute's data type. + */ + int getAttributeDataType(String elementName, String attrName); + + /** + * Gets the default value of the specified attribute of the specified + * element. + * + * @param elementName + * the element name. + * @param attrName + * the attribute name. + * @return the attribute's default value. + */ + String getAttributeDefaultValue(String elementName, String attrName); + + /** + * Gets the user-friendly description of the attribute. + * + * @param elementName + * the element name. + * @param attrName + * the attribute name. + * @param locale + * the locale giving the desired language for the description. + * @return the attribute description. + */ + String getAttributeDescription(String elementName, String attrName, Locale locale); + + /** + * Gets the attribute enumerations. + * + * @param elementName + * the element name. + * @param attrName + * the attribute name. + * @return the attribute enumerations. + */ + String[] getAttributeEnumerations(String elementName, String attrName); + + /** + * Gets the maximum length of the attribute list. + * + * @param elementName + * the element name. + * @param attrName + * the attribute name. + * @return the maximum length of the attribute list. + */ + int getAttributeListMaxLength(String elementName, String attrName); + + /** + * Gets the minimum length of the attribute list. + * + * @param elementName + * the element name. + * @param attrName + * the attribute name. + * @return the minimum length of the attribute list. + */ + int getAttributeListMinLength(String elementName, String attrName); + + /** + * Gets the maximum value allowed for the attribute. + * + * @param elementName + * the element name. + * @param attrName + * the attribute name. + * @return the maximum value allowed for the attribute. + */ + String getAttributeMaxValue(String elementName, String attrName); + + /** + * Gets the minimum value allowed for the attribute. + * + * @param elementName + * the element name. + * @param attrName + * the attribute name. + * @return the minimum value allowed for the attribute. + */ + String getAttributeMinValue(String elementName, String attrName); + + /** + * Gets the attribute names allowed for the specified element. + * + * @param elementName + * the element name. + * @return the attribute names. + */ + String[] getAttributeNames(String elementName); + + /** + * Gets the attribute value type. + * + * @param elementName + * the element name. + * @param attrName + * the attribute name. + * @return the attribute value type. + */ + int getAttributeValueType(String elementName, String attrName); + + /** + * Checks whether the specified attribute is required for the specified + * element. + * + * @param elementName + * the element name. + * @param attrName + * the attribute name. + * @return true, if the specified attribute is required for the specified + * element. + */ + boolean isAttributeRequired(String elementName, String attrName); + + /** + * Gets the names of the possible child elements for the given element. + * + * @param elementName + * the element name. + * @return the child names. + */ + String[] getChildNames(String elementName); + + /** + * Gets the constant describing the element's child policy. + * + * @param elementName + * the element name. + * @return the child policy. + */ + int getChildPolicy(String elementName); + + /** + * Gets the user-friendly description of the element. + * + * @param elementName + * the element name. + * @param locale + * the locale giving the desired language for the description. + * @return the element description. + */ + String getElementDescription(String elementName, Locale locale); + + /** + * Gets the maximum number of children allowed for the element. + * + * @param elementName + * the element name. + * @return the maximum number of children allowed for the element. + */ + int getElementMaxChildren(String elementName); + + /** + * Gets the minimum number of children allowed for the element. + * + * @param elementName + * the element name. + * @return the minimum number of children allowed for the element. + */ + int getElementMinChildren(String elementName); + + /** + * Gets the maximum object array length allowed for the element. + * + * @param elementName + * the element name. + * @return the maximum object array length allowed for the element. + */ + int getObjectArrayMaxLength(String elementName); + + /** + * Gets the minimum object array length allowed for the element. + * + * @param elementName + * the element name. + * @return the minimum object array length allowed for the element. + */ + int getObjectArrayMinLength(String elementName); + + /** + * Gets the object class corresponding to the specified element. + * + * @param elementName + * the element name. + * @return the object class corresponding to the specified element. + */ + Class getObjectClass(String elementName); + + /** + * Gets the object default value for the element. + * + * @param elementName + * the element name. + * @return the object default value for the element. + */ + Object getObjectDefaultValue(String elementName); + + /** + * Gets the object enumerations. + * + * @param elementName + * the element name. + * @return the object enumerations. + */ + Object[] getObjectEnumerations(String elementName); + + /** + * Gets the maximum value allowed for the element's object. + * + * @param elementName + * the element name. + * @return the maximum value allowed for the element's object. + */ + Comparable getObjectMaxValue(String elementName); + + /** + * Gets the minimum value allowed for the element's object. + * + * @param elementName + * the element name. + * @return the minimum value allowed for the element's object. + */ + Comparable getObjectMinValue(String elementName); + + /** + * Gets the constant that indicates the type of the element's value. + * + * @param elementName + * the element name. + * @return the constant that indicates the type of the element's value. + */ + int getObjectValueType(String elementName); + + /** + * Gets the name of the root element. + * + * @return the name of the root element. + */ + String getRootName(); +} diff --git a/awt/javax/imageio/metadata/IIOMetadataFormatImpl.java b/awt/javax/imageio/metadata/IIOMetadataFormatImpl.java new file mode 100644 index 000000000..1a6e56853 --- /dev/null +++ b/awt/javax/imageio/metadata/IIOMetadataFormatImpl.java @@ -0,0 +1,1056 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.metadata; + +import javax.imageio.ImageTypeSpecifier; +import java.util.*; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * The IIOMetadataFormatImpl class provides an implementation of the + * IIOMetadataFormat interface. + * + * @since Android 1.0 + */ +public abstract class IIOMetadataFormatImpl implements IIOMetadataFormat { + + /** + * The Constant standardMetadataFormatName. + */ + @SuppressWarnings( { + "ConstantDeclaredInAbstractClass" + }) + public static final String standardMetadataFormatName = "javax_imageio_1.0"; + + /** + * The standard format. + */ + @SuppressWarnings( { + "StaticNonFinalField" + }) + private static IIOMetadataFormatImpl standardFormat; + + /** + * The root name. + */ + private String rootName; + + /** + * The element hash. + */ + private HashMap elementHash = new HashMap(); + + /** + * The resource base name. + */ + private String resourceBaseName = getClass().getName() + "Resources"; + + /** + * Instantiates an IIOMetadataFormatImpl with the specified root name and + * child policy (not CHILD_POLICY_REPEAT). + * + * @param rootName + * the name of root element. + * @param childPolicy + * the child policy defined by one of the CHILD_POLICY_* + * constants (except CHILD_POLICY_REPEAT). + */ + public IIOMetadataFormatImpl(String rootName, int childPolicy) { + if (rootName == null) { + throw new IllegalArgumentException("rootName is null"); + } + if (childPolicy < CHILD_POLICY_EMPTY || childPolicy > CHILD_POLICY_MAX + || childPolicy == CHILD_POLICY_REPEAT) { + throw new IllegalArgumentException("childPolicy is not one of the predefined constants"); + } + + this.rootName = rootName; + Element root = new Element(); + root.name = rootName; + root.childPolicy = childPolicy; + elementHash.put(rootName, root); + } + + /** + * Instantiates an IIOMetadataFormatImpl with the specified root name and + * CHILD_POLICY_REPEAT child policy. + * + * @param rootName + * the name of root element. + * @param minChildren + * the minimum number of children. + * @param maxChildren + * the maximum number of children + */ + public IIOMetadataFormatImpl(String rootName, int minChildren, int maxChildren) { + if (rootName == null) { + throw new IllegalArgumentException("rootName is null"); + } + if (minChildren < 0) { + throw new IllegalArgumentException("minChildren < 0!"); + } + if (minChildren > maxChildren) { + throw new IllegalArgumentException("minChildren > maxChildren!"); + } + + this.rootName = rootName; + Element root = new Element(); + root.name = rootName; + root.minChildren = minChildren; + root.maxChildren = maxChildren; + root.childPolicy = CHILD_POLICY_REPEAT; + elementHash.put(rootName, root); + } + + @SuppressWarnings( { + "AbstractMethodOverridesAbstractMethod" + }) + public abstract boolean canNodeAppear(String elementName, ImageTypeSpecifier imageType); + + /** + * Adds a new attribute to an existing element. + * + * @param elementName + * the name of the element to which the new attribute will be + * added. + * @param attrName + * the attribute name. + * @param dataType + * the data type of the new attribute. + * @param required + * the flag which indicates whether this attribute must be + * present. + * @param listMinLength + * the minimum legal number of list items. + * @param listMaxLength + * the the maximum legal number of list items. + */ + protected void addAttribute(String elementName, String attrName, int dataType, + boolean required, int listMinLength, int listMaxLength) { + if (attrName == null) { + throw new IllegalArgumentException("attrName == null!"); + } + if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) { + throw new IllegalArgumentException("Invalid value for dataType!"); + } + if (listMinLength < 0 || listMinLength > listMaxLength) { + throw new IllegalArgumentException("Invalid list bounds!"); + } + + Element element = findElement(elementName); + Attlist attr = new Attlist(); + attr.name = attrName; + attr.dataType = dataType; + attr.required = required; + attr.listMinLength = listMinLength; + attr.listMaxLength = listMaxLength; + attr.valueType = VALUE_LIST; + + element.attributes.put(attrName, attr); + } + + /** + * Adds a new attribute to an existing element. + * + * @param elementName + * the name of the element to which the new attribute will be + * added. + * @param attrName + * the attribute name. + * @param dataType + * the data type of the new attribute. + * @param required + * the flag which indicates whether this attribute must be + * present. + * @param defaultValue + * the default value of the attribute. + */ + protected void addAttribute(String elementName, String attrName, int dataType, + boolean required, String defaultValue) { + if (attrName == null) { + throw new IllegalArgumentException("attrName == null!"); + } + if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) { + throw new IllegalArgumentException("Invalid value for dataType!"); + } + + Element element = findElement(elementName); + Attlist attr = new Attlist(); + attr.name = attrName; + attr.dataType = dataType; + attr.required = required; + attr.defaultValue = defaultValue; + attr.valueType = VALUE_ARBITRARY; + + element.attributes.put(attrName, attr); + } + + /** + * Adds a new attribute to an existing element. + * + * @param elementName + * the name of the element to which the new attribute will be + * added. + * @param attrName + * the attribute name. + * @param dataType + * the data type of the new attribute. + * @param required + * the flag which indicates whether this attribute must be + * present. + * @param defaultValue + * the default value of the attribute. + * @param enumeratedValues + * the legal values for the attribute as a list of strings. + */ + protected void addAttribute(String elementName, String attrName, int dataType, + boolean required, String defaultValue, List enumeratedValues) { + if (attrName == null) { + throw new IllegalArgumentException("attrName == null!"); + } + if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) { + throw new IllegalArgumentException("Invalid value for dataType!"); + } + if (enumeratedValues == null || enumeratedValues.isEmpty()) { + throw new IllegalArgumentException("enumeratedValues is empty or null"); + } + + try { + for (String enumeratedValue : enumeratedValues) { + if (enumeratedValue == null) { + throw new IllegalArgumentException("enumeratedValues contains a null!"); + } + } + } catch (ClassCastException e) { + throw new IllegalArgumentException("enumeratedValues contains a non-String value!"); + } + + Element element = findElement(elementName); + Attlist attr = new Attlist(); + attr.name = attrName; + attr.dataType = dataType; + attr.required = required; + attr.defaultValue = defaultValue; + attr.enumeratedValues = enumeratedValues; + attr.valueType = VALUE_ENUMERATION; + + element.attributes.put(attrName, attr); + } + + /** + * Adds a new attribute to an existing element. + * + * @param elementName + * the name of the element to which the new attribute will be + * added. + * @param attrName + * the attribute name. + * @param dataType + * the data type of the new attribute. + * @param required + * the flag which indicates whether this attribute must be + * present. + * @param defaultValue + * the default value of attribute. + * @param minValue + * the minimum legal value of an attribute. + * @param maxValue + * the maximum legal value of an attribute. + * @param minInclusive + * the flag which indicates whether the minValue is inclusive. + * @param maxInclusive + * the flag which indicates whether the maxValue is inclusive. + */ + protected void addAttribute(String elementName, String attrName, int dataType, + boolean required, String defaultValue, String minValue, String maxValue, + boolean minInclusive, boolean maxInclusive) { + if (attrName == null) { + throw new IllegalArgumentException("attrName == null!"); + } + if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) { + throw new IllegalArgumentException("Invalid value for dataType!"); + } + + Element element = findElement(elementName); + Attlist attr = new Attlist(); + attr.name = attrName; + attr.dataType = dataType; + attr.required = required; + attr.defaultValue = defaultValue; + attr.minValue = minValue; + attr.maxValue = maxValue; + attr.minInclusive = minInclusive; + attr.maxInclusive = maxInclusive; + + attr.valueType = VALUE_RANGE; + attr.valueType |= minInclusive ? VALUE_RANGE_MIN_INCLUSIVE_MASK : 0; + attr.valueType |= maxInclusive ? VALUE_RANGE_MAX_INCLUSIVE_MASK : 0; + + element.attributes.put(attrName, attr); + } + + /** + * Adds a new attribute with boolean data type to an existing element. + * + * @param elementName + * the name of the element to which the new attribute will be + * added. + * @param attrName + * the attribute name. + * @param hasDefaultValue + * the flag which indicates whether this attribute must have a + * default value. + * @param defaultValue + * the default value. + */ + protected void addBooleanAttribute(String elementName, String attrName, + boolean hasDefaultValue, boolean defaultValue) { + String defaultVal = hasDefaultValue ? (defaultValue ? "TRUE" : "FALSE") : null; + ArrayList values = new ArrayList(2); + values.add("TRUE"); + values.add("FALSE"); + + addAttribute(elementName, attrName, DATATYPE_BOOLEAN, true, defaultVal, values); + } + + /** + * Adds an existing element to the list of child elements of the specified + * parent element. + * + * @param elementName + * the name of the element to be added. + * @param parentName + * the parent element name. + */ + protected void addChildElement(String elementName, String parentName) { + Element parent = findElement(parentName); + Element element = findElement(elementName); + parent.children.add(element.name); + } + + /** + * Adds a new element type to this IIOMetadataFormat with a child policy (if + * policy is not CHILD_POLICY_REPEAT). + * + * @param elementName + * the name of the element to be added. + * @param parentName + * the parent element name. + * @param childPolicy + * one of the CHILD_POLICY_* constants defined by + * IIOMetadataFormat. + */ + protected void addElement(String elementName, String parentName, int childPolicy) { + if (childPolicy < CHILD_POLICY_EMPTY || childPolicy > CHILD_POLICY_MAX + || childPolicy == CHILD_POLICY_REPEAT) { + throw new IllegalArgumentException("childPolicy is not one of the predefined constants"); + } + + Element parent = findElement(parentName); + Element element = new Element(); + element.name = elementName; + element.childPolicy = childPolicy; + elementHash.put(elementName, element); + parent.children.add(elementName); + } + + /** + * Adds a new element type to this IIOMetadataFormat with + * CHILD_POLICY_REPEAT and the specified minimum and maximum number of child + * elements. + * + * @param elementName + * the element name to be added. + * @param parentName + * the parent element name. + * @param minChildren + * the minimum number of child elements. + * @param maxChildren + * the maximum number of child elements. + */ + protected void addElement(String elementName, String parentName, int minChildren, + int maxChildren) { + if (minChildren < 0) { + throw new IllegalArgumentException("minChildren < 0!"); + } + if (minChildren > maxChildren) { + throw new IllegalArgumentException("minChildren > maxChildren!"); + } + + Element parent = findElement(parentName); + Element element = new Element(); + element.name = elementName; + element.childPolicy = CHILD_POLICY_REPEAT; + element.minChildren = minChildren; + element.maxChildren = maxChildren; + elementHash.put(elementName, element); + parent.children.add(elementName); + } + + /** + * Adds an Object reference with the specified class type to be stored as + * element's value. + * + * @param elementName + * the element name. + * @param classType + * the class indicates the legal types for the object's value. + * @param arrayMinLength + * the minimum legal length for the array. + * @param arrayMaxLength + * the maximum legal length for the array. + */ + protected void addObjectValue(String elementName, Class classType, int arrayMinLength, + int arrayMaxLength) { + Element element = findElement(elementName); + + ObjectValue objVal = new ObjectValue(); + objVal.classType = classType; + objVal.arrayMaxLength = arrayMaxLength; + objVal.arrayMinLength = arrayMinLength; + objVal.valueType = VALUE_LIST; + + element.objectValue = objVal; + } + + /** + * Adds an Object reference with the specified class type to be stored as an + * element's value. + * + * @param elementName + * the element name. + * @param classType + * the class indicates the legal types for the object's value. + * @param required + * a flag indicated that this object value must be present. + * @param defaultValue + * the default value, or null. + */ + protected void addObjectValue(String elementName, Class classType, boolean required, + T defaultValue) { + // note: reqired is an unused parameter + Element element = findElement(elementName); + + ObjectValue objVal = new ObjectValue(); + objVal.classType = classType; + objVal.defaultValue = defaultValue; + objVal.valueType = VALUE_ARBITRARY; + + element.objectValue = objVal; + } + + /** + * Adds an Object reference with the specified class type to be stored as + * the element's value. + * + * @param elementName + * the element name. + * @param classType + * the class indicates the legal types for the object value. + * @param required + * a flag indicated that this object value must be present. + * @param defaultValue + * the default value, or null. + * @param enumeratedValues + * the list of legal values for the object. + */ + protected void addObjectValue(String elementName, Class classType, boolean required, + T defaultValue, List enumeratedValues) { + // note: reqired is an unused parameter + if (enumeratedValues == null || enumeratedValues.isEmpty()) { + throw new IllegalArgumentException("enumeratedValues is empty or null"); + } + + try { + for (T enumeratedValue : enumeratedValues) { + if (enumeratedValue == null) { + throw new IllegalArgumentException("enumeratedValues contains a null!"); + } + } + } catch (ClassCastException e) { + throw new IllegalArgumentException( + "enumeratedValues contains a value not of class classType!"); + } + + Element element = findElement(elementName); + + ObjectValue objVal = new ObjectValue(); + objVal.classType = classType; + objVal.defaultValue = defaultValue; + objVal.enumeratedValues = enumeratedValues; + objVal.valueType = VALUE_ENUMERATION; + + element.objectValue = objVal; + } + + /** + * Adds an Object reference with the specified class type to be stored as + * the element's value. + * + * @param elementName + * the element name. + * @param classType + * the class indicates the legal types for the object value. + * @param defaultValue + * the default value, or null. + * @param minValue + * the minimum legal value for the object value. + * @param maxValue + * the maximum legal value for the object value. + * @param minInclusive + * the flag which indicates whether the minValue is inclusive. + * @param maxInclusive + * the flag which indicates whether the maxValue is inclusive. + */ + protected > void addObjectValue(String elementName, + Class classType, T defaultValue, Comparable minValue, + Comparable maxValue, boolean minInclusive, boolean maxInclusive) { + Element element = findElement(elementName); + + ObjectValue objVal = new ObjectValue(); + objVal.classType = classType; + objVal.defaultValue = defaultValue; + objVal.minValue = minValue; + objVal.maxValue = maxValue; + objVal.minInclusive = minInclusive; + objVal.maxInclusive = maxInclusive; + + objVal.valueType = VALUE_RANGE; + objVal.valueType |= minInclusive ? VALUE_RANGE_MIN_INCLUSIVE_MASK : 0; + objVal.valueType |= maxInclusive ? VALUE_RANGE_MAX_INCLUSIVE_MASK : 0; + + element.objectValue = objVal; + } + + public int getAttributeDataType(String elementName, String attrName) { + Attlist attr = findAttribute(elementName, attrName); + return attr.dataType; + } + + public String getAttributeDefaultValue(String elementName, String attrName) { + Attlist attr = findAttribute(elementName, attrName); + return attr.defaultValue; + } + + public String getAttributeDescription(String elementName, String attrName, Locale locale) { + findAttribute(elementName, attrName); + return getResourceString(elementName + "/" + attrName, locale); + } + + public String[] getAttributeEnumerations(String elementName, String attrName) { + Attlist attr = findAttribute(elementName, attrName); + if (attr.valueType != VALUE_ENUMERATION) { + throw new IllegalArgumentException("Attribute is not an enumeration!"); + } + + return attr.enumeratedValues.toArray(new String[attr.enumeratedValues.size()]); + } + + public int getAttributeListMaxLength(String elementName, String attrName) { + Attlist attr = findAttribute(elementName, attrName); + if (attr.valueType != VALUE_LIST) { + throw new IllegalArgumentException("Attribute is not a list!"); + } + return attr.listMaxLength; + } + + public int getAttributeListMinLength(String elementName, String attrName) { + Attlist attr = findAttribute(elementName, attrName); + if (attr.valueType != VALUE_LIST) { + throw new IllegalArgumentException("Attribute is not a list!"); + } + return attr.listMinLength; + } + + public String getAttributeMaxValue(String elementName, String attrName) { + Attlist attr = findAttribute(elementName, attrName); + if ((attr.valueType & VALUE_RANGE) == 0) { + throw new IllegalArgumentException("Attribute is not a range!"); + } + return attr.maxValue; + } + + public String getAttributeMinValue(String elementName, String attrName) { + Attlist attr = findAttribute(elementName, attrName); + if ((attr.valueType & VALUE_RANGE) == 0) { + throw new IllegalArgumentException("Attribute is not a range!"); + } + return attr.minValue; + } + + public String[] getAttributeNames(String elementName) { + Element element = findElement(elementName); + return element.attributes.keySet().toArray(new String[element.attributes.size()]); + } + + public int getAttributeValueType(String elementName, String attrName) { + Attlist attr = findAttribute(elementName, attrName); + return attr.valueType; + } + + public String[] getChildNames(String elementName) { + Element element = findElement(elementName); + if (element.childPolicy == CHILD_POLICY_EMPTY) { // Element cannot have + // children + return null; + } + return element.children.toArray(new String[element.children.size()]); + } + + public int getChildPolicy(String elementName) { + Element element = findElement(elementName); + return element.childPolicy; + } + + public String getElementDescription(String elementName, Locale locale) { + findElement(elementName); // Check if there is such element + return getResourceString(elementName, locale); + } + + public int getElementMaxChildren(String elementName) { + Element element = findElement(elementName); + if (element.childPolicy != CHILD_POLICY_REPEAT) { + throw new IllegalArgumentException("Child policy is not CHILD_POLICY_REPEAT!"); + } + return element.maxChildren; + } + + public int getElementMinChildren(String elementName) { + Element element = findElement(elementName); + if (element.childPolicy != CHILD_POLICY_REPEAT) { + throw new IllegalArgumentException("Child policy is not CHILD_POLICY_REPEAT!"); + } + return element.minChildren; + } + + public int getObjectArrayMaxLength(String elementName) { + Element element = findElement(elementName); + ObjectValue v = element.objectValue; + if (v == null || v.valueType != VALUE_LIST) { + throw new IllegalArgumentException("Not a list!"); + } + return v.arrayMaxLength; + } + + public int getObjectArrayMinLength(String elementName) { + Element element = findElement(elementName); + ObjectValue v = element.objectValue; + if (v == null || v.valueType != VALUE_LIST) { + throw new IllegalArgumentException("Not a list!"); + } + return v.arrayMinLength; + } + + public Class getObjectClass(String elementName) { + ObjectValue v = findObjectValue(elementName); + return v.classType; + } + + public Object getObjectDefaultValue(String elementName) { + ObjectValue v = findObjectValue(elementName); + return v.defaultValue; + } + + public Object[] getObjectEnumerations(String elementName) { + Element element = findElement(elementName); + ObjectValue v = element.objectValue; + if (v == null || v.valueType != VALUE_ENUMERATION) { + throw new IllegalArgumentException("Not an enumeration!"); + } + return v.enumeratedValues.toArray(); + } + + public Comparable getObjectMaxValue(String elementName) { + Element element = findElement(elementName); + ObjectValue v = element.objectValue; + if (v == null || (v.valueType & VALUE_RANGE) == 0) { + throw new IllegalArgumentException("Not a range!"); + } + return v.maxValue; + } + + public Comparable getObjectMinValue(String elementName) { + Element element = findElement(elementName); + ObjectValue v = element.objectValue; + if (v == null || (v.valueType & VALUE_RANGE) == 0) { + throw new IllegalArgumentException("Not a range!"); + } + return v.minValue; + } + + public int getObjectValueType(String elementName) { + Element element = findElement(elementName); + if (element.objectValue == null) { + return VALUE_NONE; + } + return element.objectValue.valueType; + } + + /** + * Gets the resource base name for locating ResourceBundles. + * + * @return the current resource base name. + */ + protected String getResourceBaseName() { + return resourceBaseName; + } + + public String getRootName() { + return rootName; + } + + /** + * Gets the standard format instance. + * + * @return the IIOMetadataFormat instance. + */ + public static IIOMetadataFormat getStandardFormatInstance() { + if (standardFormat == null) { + standardFormat = new IIOStandardMetadataFormat(); + } + + return standardFormat; + } + + public boolean isAttributeRequired(String elementName, String attrName) { + return findAttribute(elementName, attrName).required; + } + + /** + * Removes the specified attribute from the specified element. + * + * @param elementName + * the specified element name. + * @param attrName + * the specified attribute name. + */ + protected void removeAttribute(String elementName, String attrName) { + Element element = findElement(elementName); + element.attributes.remove(attrName); + } + + /** + * Removes the specified element from this format. + * + * @param elementName + * the specified element name. + */ + protected void removeElement(String elementName) { + Element element; + if ((element = elementHash.get(elementName)) != null) { + elementHash.remove(elementName); + for (Element e : elementHash.values()) { + e.children.remove(element.name); + } + } + } + + /** + * Removes the object value from the specified element. + * + * @param elementName + * the element name. + */ + protected void removeObjectValue(String elementName) { + Element element = findElement(elementName); + element.objectValue = null; + } + + /** + * Sets a new base name for ResourceBundles containing descriptions of + * elements and attributes for this format. + * + * @param resourceBaseName + * the new resource base name. + */ + protected void setResourceBaseName(String resourceBaseName) { + if (resourceBaseName == null) { + throw new IllegalArgumentException("resourceBaseName == null!"); + } + this.resourceBaseName = resourceBaseName; + } + + /** + * The Class Element. + */ + @SuppressWarnings( { + "ClassWithoutConstructor" + }) + private class Element { + + /** + * The name. + */ + String name; + + /** + * The children. + */ + ArrayList children = new ArrayList(); + + /** + * The attributes. + */ + HashMap attributes = new HashMap(); + + /** + * The min children. + */ + int minChildren; + + /** + * The max children. + */ + int maxChildren; + + /** + * The child policy. + */ + int childPolicy; + + /** + * The object value. + */ + ObjectValue objectValue; + } + + /** + * The Class Attlist. + */ + @SuppressWarnings( { + "ClassWithoutConstructor" + }) + private class Attlist { + + /** + * The name. + */ + String name; + + /** + * The data type. + */ + int dataType; + + /** + * The required. + */ + boolean required; + + /** + * The list min length. + */ + int listMinLength; + + /** + * The list max length. + */ + int listMaxLength; + + /** + * The default value. + */ + String defaultValue; + + /** + * The enumerated values. + */ + List enumeratedValues; + + /** + * The min value. + */ + String minValue; + + /** + * The max value. + */ + String maxValue; + + /** + * The min inclusive. + */ + boolean minInclusive; + + /** + * The max inclusive. + */ + boolean maxInclusive; + + /** + * The value type. + */ + int valueType; + } + + /** + * The Class ObjectValue. + */ + @SuppressWarnings( { + "ClassWithoutConstructor" + }) + private class ObjectValue { + + /** + * The class type. + */ + Class classType; + + /** + * The array min length. + */ + int arrayMinLength; + + /** + * The array max length. + */ + int arrayMaxLength; + + /** + * The default value. + */ + T defaultValue; + + /** + * The enumerated values. + */ + List enumeratedValues; + + /** + * The min value. + */ + Comparable minValue; + + /** + * The max value. + */ + Comparable maxValue; + + /** + * The min inclusive. + */ + boolean minInclusive; + + /** + * The max inclusive. + */ + boolean maxInclusive; + + /** + * The value type. + */ + int valueType; + } + + /** + * Find element. + * + * @param name + * the name. + * @return the element. + */ + private Element findElement(String name) { + Element element; + if ((element = elementHash.get(name)) == null) { + throw new IllegalArgumentException("element name is null or no such element: " + name); + } + + return element; + } + + /** + * Find attribute. + * + * @param elementName + * the element name. + * @param attributeName + * the attribute name. + * @return the attlist. + */ + private Attlist findAttribute(String elementName, String attributeName) { + Element element = findElement(elementName); + Attlist attribute; + if ((attribute = element.attributes.get(attributeName)) == null) { + throw new IllegalArgumentException("attribute name is null or no such attribute: " + + attributeName); + } + + return attribute; + } + + /** + * Find object value. + * + * @param elementName + * the element name. + * @return the object value. + */ + private ObjectValue findObjectValue(String elementName) { + Element element = findElement(elementName); + ObjectValue v = element.objectValue; + if (v == null) { + throw new IllegalArgumentException("No object within element"); + } + return v; + } + + /** + * Gets the resource string. + * + * @param key + * the key. + * @param locale + * the locale. + * @return the resource string. + */ + private String getResourceString(String key, Locale locale) { + if (locale == null) { + locale = Locale.getDefault(); + } + + // Get the context class loader and try to locate the bundle with it + // first + ClassLoader contextClassloader = AccessController + .doPrivileged(new PrivilegedAction() { + public ClassLoader run() { + return Thread.currentThread().getContextClassLoader(); + } + }); + + // Now try to get the resource bundle + ResourceBundle rb; + try { + rb = ResourceBundle.getBundle(resourceBaseName, locale, contextClassloader); + } catch (MissingResourceException e) { + try { + rb = ResourceBundle.getBundle(resourceBaseName, locale); + } catch (MissingResourceException e1) { + return null; + } + } + + try { + return rb.getString(key); + } catch (MissingResourceException e) { + return null; + } catch (ClassCastException e) { + return null; // Not a string resource + } + } +} diff --git a/awt/javax/imageio/metadata/IIOMetadataNode.java b/awt/javax/imageio/metadata/IIOMetadataNode.java new file mode 100644 index 000000000..adc6d67b6 --- /dev/null +++ b/awt/javax/imageio/metadata/IIOMetadataNode.java @@ -0,0 +1,1070 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.metadata; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +//???AWT +//import org.w3c.dom.TypeInfo; +//import org.w3c.dom.UserDataHandler; + +/** + * The Class IIOMetadataNode represents a node of the (DOM-style) metadata tree. + * + * @since Android 1.0 + */ +public class IIOMetadataNode implements Element, NodeList { + + /** + * The node name. + */ + private String nodeName; + + /** + * The node value. + */ + private String nodeValue; + + /** + * The attributes. + */ + private IIOMetadataNodeList attrs = new IIOMetadataNodeList(new ArrayList()); + + /** + * The parent node. + */ + private IIOMetadataNode parent; + + /** + * The first child node. + */ + private IIOMetadataNode firstChild; + + /** + * The last child node. + */ + private IIOMetadataNode lastChild; + + /** + * The previous sibling. + */ + private IIOMetadataNode previousSibling; + + /** + * The next sibling. + */ + private IIOMetadataNode nextSibling; + + /** + * The number of children. + */ + private int nChildren; + + /** + * The user object associated with this node. + */ + private Object userObject; + + /** + * The text content of this node. + */ + private String textContent; + + /** + * Instantiates a new empty node. + */ + public IIOMetadataNode() { + } + + /** + * Instantiates a new empty node with the specified name. + * + * @param nodeName + * the node name. + */ + public IIOMetadataNode(String nodeName) { + this.nodeName = nodeName; + } + + /** + * Instantiates a new IIOMetadataNode with the specified name and value. + * + * @param nodeName + * the node name. + * @param nodeValue + * the node value. + */ + private IIOMetadataNode(String nodeName, String nodeValue) { + this.nodeName = nodeName; + this.nodeValue = nodeValue; + } + + public String getTagName() { + return nodeName; + } + + public String getAttribute(String name) { + Attr attrNode = (Attr)attrs.getNamedItem(name); + return (attrNode == null) ? "" : attrNode.getValue(); + } + + public void setAttribute(String name, String value) throws DOMException { + Attr attr = (Attr)attrs.getNamedItem(name); + if (attr != null) { + attr.setValue(value); + } else { + attrs.list.add(new IIOMetadataAttr(name, value, this)); + } + } + + public void removeAttribute(String name) throws DOMException { + IIOMetadataAttr attr = (IIOMetadataAttr)attrs.getNamedItem(name); + if (attr != null) { + attr.setOwnerElement(null); + attrs.list.remove(attr); + } + } + + public Attr getAttributeNode(String name) { + return (Attr)attrs.getNamedItem(name); + } + + public Attr setAttributeNode(Attr newAttr) throws DOMException { + // Check if this attribute is already in use. + Element owner = newAttr.getOwnerElement(); + if (owner != null) { + if (owner == this) { // Replacing an attribute node by itself has no + // effect + return null; + } else { + throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, + "Attribute is already in use"); + } + } + + String name = newAttr.getName(); + Attr oldAttr = getAttributeNode(name); + if (oldAttr != null) { + removeAttributeNode(oldAttr); + } + + IIOMetadataAttr iioAttr; + if (newAttr instanceof IIOMetadataAttr) { + iioAttr = (IIOMetadataAttr)newAttr; + iioAttr.setOwnerElement(this); + } else { + iioAttr = new IIOMetadataAttr(name, newAttr.getValue(), this); + } + + attrs.list.add(iioAttr); + + return oldAttr; + } + + public Attr removeAttributeNode(Attr oldAttr) throws DOMException { + if (!attrs.list.remove(oldAttr)) { // Not found + throw new DOMException(DOMException.NOT_FOUND_ERR, "No such attribute!"); + } + + ((IIOMetadataAttr)oldAttr).setOwnerElement(null); + + return oldAttr; + } + + public NodeList getElementsByTagName(String name) { + ArrayList nodes = new ArrayList(); + + // Non-recursive tree walk + Node pos = this; + + while (pos != null) { + if (pos.getNodeName().equals(name)) { + nodes.add((IIOMetadataNode)pos); + } + + Node nextNode = pos.getFirstChild(); + + while (nextNode == null) { + if (pos == this) { + break; + } + + nextNode = pos.getNextSibling(); + + if (nextNode == null) { + pos = pos.getParentNode(); + + if (pos == null || pos == this) { + nextNode = null; + break; + } + } + } + pos = nextNode; + } + + return new IIOMetadataNodeList(nodes); + } + + public String getAttributeNS(String namespaceURI, String localName) throws DOMException { + return getAttribute(localName); + } + + public void setAttributeNS(String namespaceURI, String qualifiedName, String value) + throws DOMException { + setAttribute(qualifiedName, value); + } + + public void removeAttributeNS(String namespaceURI, String localName) throws DOMException { + removeAttribute(localName); + } + + public Attr getAttributeNodeNS(String namespaceURI, String localName) throws DOMException { + return getAttributeNode(localName); + } + + public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { + return setAttributeNode(newAttr); + } + + public NodeList getElementsByTagNameNS(String namespaceURI, String localName) + throws DOMException { + return getElementsByTagName(localName); + } + + public boolean hasAttribute(String name) { + return attrs.getNamedItem(name) != null; + } + + public boolean hasAttributeNS(String namespaceURI, String localName) throws DOMException { + return hasAttribute(localName); + } + + // ???AWT + /* + * public TypeInfo getSchemaTypeInfo() { throw new + * DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); } + */ + + /** + * Description copied from interface: org.w3c.dom.Element (DOM Level + * 3) + *

+ * If the parameter isId is true, this method declares the specified + * attribute to be a user-determined ID attribute . This affects the value + * of Attr.isId and the behavior of Document.getElementById, but does not + * change any schema that may be in use, in particular this does not affect + * the Attr.schemaTypeInfo of the specified Attr node. Use the value false + * for the parameter isId to undeclare an attribute for being a + * user-determined ID attribute. To specify an attribute by local name and + * namespace URI, use the setIdAttributeNS method. + *

+ * + * @param name + * the name of the attribute. + * @param isId + * the flag which determines whether this attribute is of type + * ID. + * @throws DOMException + * if a DOM error occurred while setting the attribute type. + *

+ * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + *
+ * NOT_FOUND_ERR: Raised if the specified node is not an + * attribute of this element. + *

+ */ + public void setIdAttribute(String name, boolean isId) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + /** + * Description copied from interface: org.w3c.dom.Element (DOM Level + * 3) + *

+ * If the parameter isId is true, this method declares the specified + * attribute to be a user-determined ID attribute . This affects the value + * of Attr.isId and the behavior of Document.getElementById, but does not + * change any schema that may be in use, in particular this does not affect + * the Attr.schemaTypeInfo of the specified Attr node. Use the value false + * for the parameter isId to undeclare an attribute for being a + * user-determined ID attribute. + *

+ * + * @param namespaceURI + * the namespace URI of the attribute. + * @param localName + * the local name of the attribute. + * @param isId + * the flag which determines whether this attribute is of type + * ID. + * @throws DOMException + * if a DOM error occurred while setting the attribute type. + *

+ * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + *
+ * NOT_FOUND_ERR: Raised if the specified node is not an + * attribute of this element. + *

+ */ + public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) + throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + /** + * Description copied from interface: org.w3c.dom.Element (DOM Level + * 3) + *

+ * If the parameter isId is true, this method declares the specified + * attribute to be a user-determined ID attribute . This affects the value + * of Attr.isId and the behavior of Document.getElementById, but does not + * change any schema that may be in use, in particular this does not affect + * the Attr.schemaTypeInfo of the specified Attr node. Use the value false + * for the parameter isId to undeclare an attribute for being a + * user-determined ID attribute. + *

+ * + * @param idAttr + * the attribute node. + * @param isId + * the flag which determines whether this attribute is of type + * ID. + * @throws DOMException + * if a DOM error occurred while setting the attribute type. + *

+ * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + *
+ * NOT_FOUND_ERR: Raised if the specified node is not an + * attribute of this element. + *

+ */ + public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public String getNodeName() { + return nodeName; + } + + public String getNodeValue() throws DOMException { + return nodeValue; + } + + public void setNodeValue(String nodeValue) throws DOMException { + this.nodeValue = nodeValue; + } + + public short getNodeType() { + return ELEMENT_NODE; + } + + public Node getParentNode() { + return parent; + } + + public NodeList getChildNodes() { + return this; + } + + public Node getFirstChild() { + return firstChild; + } + + public Node getLastChild() { + return lastChild; + } + + public Node getPreviousSibling() { + return previousSibling; + } + + public Node getNextSibling() { + return nextSibling; + } + + public NamedNodeMap getAttributes() { + return attrs; + } + + public Document getOwnerDocument() { + return null; + } + + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + if (newChild == null) { + throw new IllegalArgumentException("newChild == null!"); + } + + IIOMetadataNode newIIOChild = (IIOMetadataNode)newChild; + IIOMetadataNode refIIOChild = (IIOMetadataNode)refChild; + + newIIOChild.parent = this; + + if (refIIOChild == null) { + newIIOChild.nextSibling = null; + newIIOChild.previousSibling = lastChild; + + // Fix this node + lastChild = newIIOChild; + if (firstChild == null) { + firstChild = newIIOChild; + } + } else { + newIIOChild.nextSibling = refIIOChild; + newIIOChild.previousSibling = refIIOChild.previousSibling; + + // Fix this node + if (firstChild == refIIOChild) { + firstChild = newIIOChild; + } + + // Fix next node + if (refIIOChild != null) { + refIIOChild.previousSibling = newIIOChild; + } + } + + // Fix prev node + if (newIIOChild.previousSibling != null) { + newIIOChild.previousSibling.nextSibling = newIIOChild; + } + + nChildren++; + + return newIIOChild; + } + + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + if (newChild == null) { + throw new IllegalArgumentException("newChild == null!"); + } + + IIOMetadataNode newIIOChild = (IIOMetadataNode)newChild; + IIOMetadataNode oldIIOChild = (IIOMetadataNode)oldChild; + + IIOMetadataNode next = oldIIOChild.nextSibling; + IIOMetadataNode previous = oldIIOChild.previousSibling; + + // Fix new node + newIIOChild.parent = this; + newIIOChild.nextSibling = next; + newIIOChild.previousSibling = previous; + + // Fix this node + if (lastChild == oldIIOChild) { + lastChild = newIIOChild; + } + if (firstChild == oldIIOChild) { + firstChild = newIIOChild; + } + + // Fix siblings + if (next != null) { + next.previousSibling = newIIOChild; + } + if (previous != null) { + previous.nextSibling = newIIOChild; + } + + // Fix old child + oldIIOChild.parent = null; + oldIIOChild.nextSibling = next; + oldIIOChild.previousSibling = previous; + + return oldIIOChild; + } + + public Node removeChild(Node oldChild) throws DOMException { + if (oldChild == null) { + throw new IllegalArgumentException("oldChild == null!"); + } + + IIOMetadataNode oldIIOChild = (IIOMetadataNode)oldChild; + + // Fix next and previous + IIOMetadataNode previous = oldIIOChild.previousSibling; + IIOMetadataNode next = oldIIOChild.nextSibling; + + if (previous != null) { + previous.nextSibling = next; + } + if (next != null) { + next.previousSibling = previous; + } + + // Fix this node + if (lastChild == oldIIOChild) { + lastChild = previous; + } + if (firstChild == oldIIOChild) { + firstChild = next; + } + nChildren--; + + // Fix old child + oldIIOChild.parent = null; + oldIIOChild.previousSibling = null; + oldIIOChild.nextSibling = null; + + return oldIIOChild; + } + + public Node appendChild(Node newChild) throws DOMException { + return insertBefore(newChild, null); + } + + public boolean hasChildNodes() { + return nChildren != 0; + } + + public Node cloneNode(boolean deep) { + IIOMetadataNode cloned = new IIOMetadataNode(nodeName); + cloned.setUserObject(getUserObject()); + + if (deep) { // Clone recursively + IIOMetadataNode c = firstChild; + while (c != null) { + cloned.insertBefore(c.cloneNode(true), null); + c = c.nextSibling; + } + } + + return cloned; // To change body of implemented methods use File | + // Settings | File Templates. + } + + public void normalize() { + // Do nothing + } + + public boolean isSupported(String feature, String version) { + return false; + } + + public String getNamespaceURI() { + return null; + } + + public String getPrefix() { + return null; + } + + public void setPrefix(String prefix) throws DOMException { + // Do nothing + } + + public String getLocalName() { + return nodeName; + } + + public boolean hasAttributes() { + return attrs.list.size() > 0; + } + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

+ * The absolute base URI of this node or null if the implementation wasn't + * able to obtain an absolute URI. This value is computed as described in. + * However, when the Document supports the feature "HTML" [DOM Level 2 + * HTML], the base URI is computed using first the value of the href + * attribute of the HTML BASE element if any, and the value of the + * documentURI attribute from the Document interface otherwise. + *

+ * + * @return the string representation of the absolute base URI. + */ + public String getBaseURI() { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

+ * Compares the reference node, i.e. the node on which this method is being + * called, with a node, i.e. the one passed as a parameter, with regard to + * their position in the document and according to the document order. + *

+ * + * @param other + * the node to compare against the reference node. + * @return Returns how the node is positioned relatively to the reference + * node. + * @throws DOMException + * NOT_SUPPORTED_ERR: when the compared nodes are from different + * DOM implementations that do not coordinate to return + * consistent implementation-specific results. + */ + public short compareDocumentPosition(Node other) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

+ * This attribute returns the text content of this node and its descendants. + * When it is defined to be null, setting it has no effect. On setting, any + * possible children this node may have are removed and, if it the new + * string is not empty or null, replaced by a single Text node containing + * the string this attribute is set to. On getting, no serialization is + * performed, the returned string does not contain any markup. No whitespace + * normalization is performed and the returned string does not contain the + * white spaces in element content (see the attribute + * Text.isElementContentWhitespace). Similarly, on setting, no parsing is + * performed either, the input string is taken as pure textual content. The + * string returned is made of the text content of this node depending on its + * type, as defined below: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Node typeContent
ELEMENT_NODE, ATTRIBUTE_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE, + * DOCUMENT_FRAGMENT_NODEconcatenation of the textContent attribute value of every child node, + * excluding COMMENT_NODE and PROCESSING_INSTRUCTION_NODE nodes. This is the + * empty string if the node has no children.
TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE, + * PROCESSING_INSTRUCTION_NODEnodeValue
DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODEnull
+ *

+ * + * @return the text content depending on the type of this node. + * @throws DOMException + * DOMSTRING_SIZE_ERR: Raised when it would return more + * characters than fit in a DOMString variable on the + * implementation platform. + */ + public String getTextContent() throws DOMException { + return textContent; + } + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

+ * This attribute returns the text content of this node and its descendants. + * When it is defined to be null, setting it has no effect. On setting, any + * possible children this node may have are removed and, if it the new + * string is not empty or null, replaced by a single Text node containing + * the string this attribute is set to. On getting, no serialization is + * performed, the returned string does not contain any markup. No whitespace + * normalization is performed and the returned string does not contain the + * white spaces in element content (see the attribute + * Text.isElementContentWhitespace). Similarly, on setting, no parsing is + * performed either, the input string is taken as pure textual content. The + * string returned is made of the text content of this node depending on its + * type, as defined below: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Node typeContent
ELEMENT_NODE, ATTRIBUTE_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE, + * DOCUMENT_FRAGMENT_NODEconcatenation of the textContent attribute value of every child node, + * excluding COMMENT_NODE and PROCESSING_INSTRUCTION_NODE nodes. This is the + * empty string if the node has no children.
TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE, + * PROCESSING_INSTRUCTION_NODEnodeValue
DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODEnull
+ *

+ * + * @param textContent + * the text content for this node. + * @throws DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised when the node is + * readonly. + */ + public void setTextContent(String textContent) throws DOMException { + this.textContent = textContent; + } + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

+ * Returns whether this node is the same node as the given one. This method + * provides a way to determine whether two Node references returned by the + * implementation reference the same object. When two Node references are + * references to the same object, even if through a proxy, the references + * may be used completely interchangeably, such that all attributes have the + * same values and calling the same DOM method on either reference always + * has exactly the same effect. + *

+ * + * @param other + * the node to test against. + * @return true, if the nodes are the same, false otherwise. + */ + public boolean isSameNode(Node other) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

+ * Look up the prefix associated to the given namespace URI, starting from + * this node. The default namespace declarations are ignored by this method. + * See for details on the algorithm used by this method. + *

+ * + * @param namespaceURI + * the namespace URI to look for. + * @return the associated namespace prefix if found or null if none is + * found. If more than one prefix are associated to the namespace + * prefix, the returned namespace prefix is implementation + * dependent. + */ + public String lookupPrefix(String namespaceURI) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

+ * This method checks if the specified namespaceURI is the default namespace + * or not. + *

+ * + * @param namespaceURI + * the namespace URI to look for. + * @return true, if the specified namespaceURI is the default namespace, + * false otherwise. + */ + public boolean isDefaultNamespace(String namespaceURI) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

+ * Look up the namespace URI associated to the given prefix, starting from + * this node. See for details on the algorithm used by this method. + *

+ * + * @param prefix + * the prefix to look for. If this parameter is null, the method + * will return the default namespace URI if any. + * @return the associated namespace URI or null if none is found. + */ + public String lookupNamespaceURI(String prefix) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

+ * Tests whether two nodes are equal. This method tests for equality of + * nodes, not sameness (i.e., whether the two nodes are references to the + * same object) which can be tested with Node.isSameNode(). All nodes that + * are the same will also be equal, though the reverse may not be true. Two + * nodes are equal if and only if the following conditions are satisfied: + *

+ *

  • The two nodes are of the same type.
  • + *
  • The following string attributes are equal: nodeName, localName, + * namespaceURI, prefix, nodeValue . This is: they are both null, or they + * have the same length and are character for character identical.
  • + *
  • The attributes NamedNodeMaps are equal. This is: they are both null, + * or they have the same length and for each node that exists in one map + * there is a node that exists in the other map and is equal, although not + * necessarily at the same index.
  • + *
  • The childNodes NodeLists are equal. This is: they are both null, or + * they have the same length and contain equal nodes at the same index. Note + * that normalization can affect equality; to avoid this, nodes should be + * normalized before being compared.
  • + *

    + * For two DocumentType nodes to be equal, the following conditions must + * also be satisfied: + *

    + *

  • The following string attributes are equal: publicId, systemId, + * internalSubset.
  • + *
  • The entities NamedNodeMaps are equal.
  • + *
  • The notations NamedNodeMaps are equal.
  • + *

    + * On the other hand, the following do not affect equality: the + * ownerDocument, baseURI, and parentNode attributes, the specified + * attribute for Attr nodes, the schemaTypeInfo attribute for Attr and + * Element nodes, the Text.isElementContentWhitespace attribute for Text + * nodes, as well as any user data or event listeners registered on the + * nodes.

    + *

    + * Note: As a general rule, anything not mentioned in the description above + * is not significant in consideration of equality checking. Note that + * future versions of this specification may take into account more + * attributes and implementations conform to this specification are expected + * to be updated accordingly. + *

    + * + * @param arg + * the node to compare equality with. + * @return true, if the nodes are equal, false otherwise. + */ + public boolean isEqualNode(Node arg) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

    + * This method returns a specialized object which implements the specialized + * APIs of the specified feature and version, as specified in. The + * specialized object may also be obtained by using binding-specific casting + * methods but is not necessarily expected to, as discussed in. This method + * also allow the implementation to provide specialized objects which do not + * support the Node interface. + *

    + * + * @param feature + * the name of the feature requested. Note that any plus sign "+" + * prepended to the name of the feature will be ignored since it + * is not significant in the context of this method. + * @param version + * this is the version number of the feature to test. + * @return the object which implements the specialized APIs of the specified + * feature and version, if any, or null if there is no object which + * implements interfaces associated with that feature. If the + * DOMObject returned by this method implements the Node interface, + * it must delegate to the primary core Node and not return results + * inconsistent with the primary core Node such as attributes, + * childNodes, etc. + */ + public Object getFeature(String feature, String version) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + // ???AWT + /* + * public Object setUserData(String key, Object data, UserDataHandler + * handler) { throw new DOMException(DOMException.NOT_SUPPORTED_ERR, + * "Method not supported"); } + */ + + /** + * Description copied from interface: org.w3c.dom.Node (DOM Level 3) + *

    + * Retrieves the object associated to a key on a this node. The object must + * first have been set to this node by calling setUserData with the same + * key. + *

    + * + * @param key + * the key the object is associated to. + * @return the DOMUserData associated to the given key on this node, or null + * if there was none. + */ + public Object getUserData(String key) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public Node item(int index) { + if (index < 0 || index >= nChildren) { + return null; + } + + Node n; + for (n = getFirstChild(); index > 0; index--) { + n = n.getNextSibling(); + } + + return n; + } + + public int getLength() { + return nChildren; + } + + /** + * Gets the user object associated with this node. + * + * @return the user object associated with this node. + */ + public Object getUserObject() { + return userObject; + } + + /** + * Sets the user object associated with this node. + * + * @param userObject + * the new user object associated with this node. + */ + public void setUserObject(Object userObject) { + this.userObject = userObject; + } + + /** + * The Class IIOMetadataAttr. + */ + private class IIOMetadataAttr extends IIOMetadataNode implements Attr { + + /** + * The owner element. + */ + private Element ownerElement; + + /** + * Instantiates a new iIO metadata attr. + * + * @param name + * the name. + * @param value + * the value. + * @param owner + * the owner. + */ + public IIOMetadataAttr(String name, String value, Element owner) { + super(name, value); + this.ownerElement = owner; + } + + public String getName() { + return getNodeName(); + } + + public boolean getSpecified() { + return true; + } + + public String getValue() { + return nodeValue; + } + + public void setValue(String value) throws DOMException { + nodeValue = value; + } + + public Element getOwnerElement() { + return ownerElement; + } + + /** + * Sets the owner element. + * + * @param ownerElement + * the new owner element. + */ + public void setOwnerElement(Element ownerElement) { + this.ownerElement = ownerElement; + } + + /** + * @return + */ + public boolean isId() { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + @Override + public short getNodeType() { + return ATTRIBUTE_NODE; + } + } + + /** + * The Class IIOMetadataNodeList. + */ + private class IIOMetadataNodeList implements NodeList, NamedNodeMap { + + /** + * The list. + */ + private List list; + + /** + * Instantiates a new iIO metadata node list. + * + * @param list + * the list. + */ + IIOMetadataNodeList(List list) { + this.list = list; + } + + public Node item(int index) { + try { + return list.get(index); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + public int getLength() { + return list.size(); + } + + public Node getNamedItem(String name) { + for (IIOMetadataNode node : list) { + if (name.equals(node.getNodeName())) { + return node; + } + } + return null; + } + + public Node setNamedItem(Node arg) throws DOMException { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, + "This NamedNodeMap is read-only!"); + } + + public Node removeNamedItem(String name) throws DOMException { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, + "This NamedNodeMap is read-only!"); + } + + public Node getNamedItemNS(String namespaceURI, String localName) throws DOMException { + return getNamedItem(localName); + } + + public Node setNamedItemNS(Node arg) throws DOMException { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, + "This NamedNodeMap is read-only!"); + } + + public Node removeNamedItemNS(String namespaceURI, String localName) throws DOMException { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, + "This NamedNodeMap is read-only!"); + } + } +} diff --git a/awt/javax/imageio/metadata/IIOStandardMetadataFormat.java b/awt/javax/imageio/metadata/IIOStandardMetadataFormat.java new file mode 100644 index 000000000..706cb2f7a --- /dev/null +++ b/awt/javax/imageio/metadata/IIOStandardMetadataFormat.java @@ -0,0 +1,297 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.metadata; + +import javax.imageio.ImageTypeSpecifier; +import java.util.ArrayList; + +/** + * The class IIOStandardMetadataFormat describes the rules of the standard + * metadata format. + * + * @since Android 1.0 + */ +class IIOStandardMetadataFormat extends IIOMetadataFormatImpl { + + /** + * Instantiates a new IIOStandardMetadataFormat. + */ + public IIOStandardMetadataFormat() { + super(standardMetadataFormatName, CHILD_POLICY_SOME); + buildDTD(); + } + + @Override + public boolean canNodeAppear(String elementName, ImageTypeSpecifier imageType) { + return true; + } + + /** + * Builds the DTD that describes the standard metadata format. + */ + private void buildDTD() { + // CHROMA + addElement("Chroma", standardMetadataFormatName, CHILD_POLICY_SOME); + + addElement("ColorSpaceType", "Chroma", CHILD_POLICY_EMPTY); + + ArrayList values = new ArrayList(27); + values.add("XYZ"); + values.add("Lab"); + values.add("Luv"); + values.add("YCbCr"); + values.add("Yxy"); + values.add("YCCK"); + values.add("PhotoYCC"); + values.add("RGB"); + values.add("GRAY"); + values.add("HSV"); + values.add("HLS"); + values.add("CMYK"); + values.add("CMY"); + values.add("2CLR"); + values.add("3CLR"); + values.add("4CLR"); + values.add("5CLR"); + values.add("6CLR"); + values.add("7CLR"); + values.add("8CLR"); + values.add("9CLR"); + values.add("ACLR"); + values.add("BCLR"); + values.add("CCLR"); + values.add("DCLR"); + values.add("ECLR"); + values.add("FCLR"); + addAttribute("ColorSpaceType", "name", DATATYPE_STRING, true, null, values); + + addElement("NumChannels", "Chroma", CHILD_POLICY_EMPTY); + addAttribute("NumChannels", "value", DATATYPE_INTEGER, true, 0, Integer.MAX_VALUE); // list + // - + // why + // ? + + addElement("Gamma", "Chroma", CHILD_POLICY_EMPTY); + addAttribute("Gamma", "value", DATATYPE_FLOAT, true, null); + + addElement("BlackIsZero", "Chroma", CHILD_POLICY_EMPTY); + addBooleanAttribute("BlackIsZero", "value", true, true); + + addElement("Palette", "Chroma", 0, Integer.MAX_VALUE); // CHILD_POLICY_REPEAT + addElement("PaletteEntry", "Palette", CHILD_POLICY_EMPTY); + addAttribute("PaletteEntry", "index", DATATYPE_INTEGER, true, null); + addAttribute("PaletteEntry", "red", DATATYPE_INTEGER, true, null); + addAttribute("PaletteEntry", "green", DATATYPE_INTEGER, true, null); + addAttribute("PaletteEntry", "blue", DATATYPE_INTEGER, true, null); + addAttribute("PaletteEntry", "alpha", DATATYPE_INTEGER, false, "255"); + + addElement("BackgroundIndex", "Chroma", CHILD_POLICY_EMPTY); + addAttribute("BackgroundIndex", "value", DATATYPE_INTEGER, true, null); + + addElement("BackgroundColor", "Chroma", CHILD_POLICY_EMPTY); + addAttribute("BackgroundColor", "red", DATATYPE_INTEGER, true, null); + addAttribute("BackgroundColor", "green", DATATYPE_INTEGER, true, null); + addAttribute("BackgroundColor", "blue", DATATYPE_INTEGER, true, null); + + // COMPRESSION + addElement("Compression", standardMetadataFormatName, CHILD_POLICY_SOME); + + addElement("CompressionTypeName", "Compression", CHILD_POLICY_EMPTY); + addAttribute("CompressionTypeName", "value", DATATYPE_STRING, true, null); + + addElement("Lossless", "Compression", CHILD_POLICY_EMPTY); + addBooleanAttribute("Lossless", "value", true, true); + + addElement("NumProgressiveScans", "Compression", CHILD_POLICY_EMPTY); + addAttribute("NumProgressiveScans", "value", DATATYPE_INTEGER, true, null); + + addElement("BitRate", "Compression", CHILD_POLICY_EMPTY); + addAttribute("BitRate", "value", DATATYPE_FLOAT, true, null); + + // DATA + addElement("Data", standardMetadataFormatName, CHILD_POLICY_SOME); + + addElement("PlanarConfiguration", "Data", CHILD_POLICY_EMPTY); + values = new ArrayList(4); + values.add("PixelInterleaved"); + values.add("PlaneInterleaved"); + values.add("LineInterleaved"); + values.add("TileInterleaved"); + addAttribute("PlanarConfiguration", "value", DATATYPE_STRING, true, null, values); + + addElement("SampleFormat", "Data", CHILD_POLICY_EMPTY); + values = new ArrayList(4); + values.add("SignedIntegral"); + values.add("UnsignedIntegral"); + values.add("Real"); + values.add("Index"); + addAttribute("SampleFormat", "value", DATATYPE_STRING, true, null, values); + + addElement("BitsPerSample", "Data", CHILD_POLICY_EMPTY); + addAttribute("BitsPerSample", "value", DATATYPE_INTEGER, true, 1, Integer.MAX_VALUE); // list + + addElement("SignificantBitsPerSample", "Data", CHILD_POLICY_EMPTY); + addAttribute("SignificantBitsPerSample", "value", DATATYPE_INTEGER, true, 1, + Integer.MAX_VALUE); // list + + addElement("SampleMSB", "Data", CHILD_POLICY_EMPTY); + addAttribute("SampleMSB", "value", DATATYPE_INTEGER, true, 1, Integer.MAX_VALUE); // list + + // DIMENSION + addElement("Dimension", standardMetadataFormatName, CHILD_POLICY_SOME); + + addElement("PixelAspectRatio", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("PixelAspectRatio", "value", DATATYPE_FLOAT, true, null); + + addElement("ImageOrientation", "Dimension", CHILD_POLICY_EMPTY); + values = new ArrayList(8); + values.add("Normal"); + values.add("Rotate90"); + values.add("Rotate180"); + values.add("Rotate270"); + values.add("FlipH"); + values.add("FlipV"); + values.add("FlipHRotate90"); + values.add("FlipVRotate90"); + addAttribute("ImageOrientation", "value", DATATYPE_STRING, true, null, values); + + addElement("HorizontalPixelSize", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("HorizontalPixelSize", "value", DATATYPE_FLOAT, true, null); + + addElement("VerticalPixelSize", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("VerticalPixelSize", "value", DATATYPE_FLOAT, true, null); + + addElement("HorizontalPhysicalPixelSpacing", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("HorizontalPhysicalPixelSpacing", "value", DATATYPE_FLOAT, true, null); + + addElement("VerticalPhysicalPixelSpacing", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("VerticalPhysicalPixelSpacing", "value", DATATYPE_FLOAT, true, null); + + addElement("HorizontalPosition", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("HorizontalPosition", "value", DATATYPE_FLOAT, true, null); + + addElement("VerticalPosition", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("VerticalPosition", "value", DATATYPE_FLOAT, true, null); + + addElement("HorizontalPixelOffset", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("HorizontalPixelOffset", "value", DATATYPE_INTEGER, true, null); + + addElement("VerticalPixelOffset", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("VerticalPixelOffset", "value", DATATYPE_INTEGER, true, null); + + addElement("HorizontalScreenSize", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("HorizontalScreenSize", "value", DATATYPE_INTEGER, true, null); + + addElement("VerticalScreenSize", "Dimension", CHILD_POLICY_EMPTY); + addAttribute("VerticalScreenSize", "value", DATATYPE_INTEGER, true, null); + + // DOCUMENT + addElement("Document", standardMetadataFormatName, CHILD_POLICY_SOME); + + addElement("FormatVersion", "Document", CHILD_POLICY_EMPTY); + addAttribute("FormatVersion", "value", DATATYPE_STRING, true, null); + + addElement("SubimageInterpretation", "Document", CHILD_POLICY_EMPTY); + values = new ArrayList(14); + values.add("Standalone"); + values.add("SinglePage"); + values.add("FullResolution"); + values.add("ReducedResolution"); + values.add("PyramidLayer"); + values.add("Preview"); + values.add("VolumeSlice"); + values.add("ObjectView"); + values.add("Panorama"); + values.add("AnimationFrame"); + values.add("TransparencyMask"); + values.add("CompositingLayer"); + values.add("SpectralSlice"); + values.add("Unknown"); + addAttribute("SubimageInterpretation", "value", DATATYPE_STRING, true, null, values); + + addElement("ImageCreationTime", "Document", CHILD_POLICY_EMPTY); + addAttribute("ImageCreationTime", "year", DATATYPE_INTEGER, true, null); + addAttribute("ImageCreationTime", "month", DATATYPE_INTEGER, true, null, "1", "12", true, + true); + addAttribute("ImageCreationTime", "day", DATATYPE_INTEGER, true, null, "1", "31", true, + true); + addAttribute("ImageCreationTime", "hour", DATATYPE_INTEGER, false, "0", "0", "23", true, + true); + addAttribute("ImageCreationTime", "minute", DATATYPE_INTEGER, false, "0", "0", "59", true, + true); + addAttribute("ImageCreationTime", "second", DATATYPE_INTEGER, false, "0", "0", "60", true, + true); + + addElement("ImageModificationTime", "Document", CHILD_POLICY_EMPTY); + addAttribute("ImageModificationTime", "year", DATATYPE_INTEGER, true, null); + addAttribute("ImageModificationTime", "month", DATATYPE_INTEGER, true, null, "1", "12", + true, true); + addAttribute("ImageModificationTime", "day", DATATYPE_INTEGER, true, null, "1", "31", true, + true); + addAttribute("ImageModificationTime", "hour", DATATYPE_INTEGER, false, "0", "0", "23", + true, true); + addAttribute("ImageModificationTime", "minute", DATATYPE_INTEGER, false, "0", "0", "59", + true, true); + addAttribute("ImageModificationTime", "second", DATATYPE_INTEGER, false, "0", "0", "60", + true, true); + + // TEXT + addElement("Text", standardMetadataFormatName, 0, Integer.MAX_VALUE); // CHILD_POLICY_REPEAT + + addElement("TextEntry", "Text", CHILD_POLICY_EMPTY); + addAttribute("TextEntry", "keyword", DATATYPE_STRING, false, null); + addAttribute("TextEntry", "value", DATATYPE_STRING, true, null); + addAttribute("TextEntry", "language", DATATYPE_STRING, false, null); + addAttribute("TextEntry", "encoding", DATATYPE_STRING, false, null); + values = new ArrayList(5); + values.add("none"); + values.add("lzw"); + values.add("zip"); + values.add("bzip"); + values.add("other"); + addAttribute("TextEntry", "compression", DATATYPE_STRING, false, "none", values); + + // TRANSPARENCY + addElement("Transparency", standardMetadataFormatName, CHILD_POLICY_SOME); + + addElement("Alpha", "Transparency", CHILD_POLICY_EMPTY); + values = new ArrayList(3); + values.add("none"); + values.add("premultiplied"); + values.add("nonpremultiplied"); + addAttribute("Alpha", "value", DATATYPE_STRING, false, "none", values); + + addElement("TransparentIndex", "Transparency", CHILD_POLICY_EMPTY); + addAttribute("TransparentIndex", "value", DATATYPE_INTEGER, true, null); + + addElement("TransparentColor", "Transparency", CHILD_POLICY_EMPTY); + addAttribute("TransparentColor", "value", DATATYPE_INTEGER, true, 0, Integer.MAX_VALUE); + + addElement("TileTransparencies", "Transparency", 0, Integer.MAX_VALUE); // CHILD_POLICY_REPEAT + + addElement("TransparentTile", "TileTransparencies", CHILD_POLICY_EMPTY); + addAttribute("TransparentTile", "x", DATATYPE_INTEGER, true, null); + addAttribute("TransparentTile", "y", DATATYPE_INTEGER, true, null); + + addElement("TileOpacities", "Transparency", 0, Integer.MAX_VALUE); // CHILD_POLICY_REPEAT + + addElement("OpaqueTile", "TileOpacities", CHILD_POLICY_EMPTY); + addAttribute("OpaqueTile", "x", DATATYPE_INTEGER, true, null); + addAttribute("OpaqueTile", "y", DATATYPE_INTEGER, true, null); + } +} diff --git a/awt/javax/imageio/metadata/IIOStandardMetadataFormatResources.properties b/awt/javax/imageio/metadata/IIOStandardMetadataFormatResources.properties new file mode 100644 index 000000000..d18580898 --- /dev/null +++ b/awt/javax/imageio/metadata/IIOStandardMetadataFormatResources.properties @@ -0,0 +1,133 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +# Descriptions of elements and attributes of the plugin neutral metadata format +# (see IIOStandardMetadataFormat) + +# Messages for EN locale +Chroma=Chroma (color) information +ColorSpaceType=The raw color space of the image +ColorSpaceType/name=The raw color space of the image +NumChannels=The number of channels in the raw image, including alpha +NumChannels/value=The number of channels in the raw image, including alpha +Gamma=The image gamma +Gamma/value=The image gamma +BlackIsZero=True if smaller values represent darker shades +BlackIsZero/value=True if smaller values represent darker shades +Palette=Palette-color information +PaletteEntry=A palette entry +PaletteEntry/index=The index of the palette entry +PaletteEntry/red=The red value for the palette entry +PaletteEntry/green=The green value for the palette entry +PaletteEntry/blue=The blue value for the palette entry +PaletteEntry/alpha=The alpha value for the palette entry +BackgroundIndex=A palette index to be used as a background +BackgroundIndex/value=A palette index to be used as a background +BackgroundColor=An RGB triple to be used as a background +BackgroundColor/red=The red background value +BackgroundColor/green=The green background value +BackgroundColor/blue=The blue background value + +Compression=Compression information +CompressionTypeName=The name of the compression scheme in use +CompressionTypeName/value=The name of the compression scheme in use +Lossless=True if the compression scheme is lossless +Lossless/value=True if the compression scheme is lossless +NumProgressiveScans=The number of progressive scans used in the image encoding +NumProgressiveScans/value=The number of progressive scans used in the image encoding +BitRate=The estimated bit rate of the compression scheme +BitRate/value=The estimated bit rate of the compression scheme + +Data=Information on the image layout +PlanarConfiguration=The organization of image samples in the stream +PlanarConfiguration/value=The organization of image samples in the stream +SampleFormat=The numeric format of image samples +SampleFormat/value=The numeric format of image samples +BitsPerSample=The number of bits per sample +BitsPerSample/value=A list of integers, one per channel +SignificantBitsPerSample=The number of significant bits per sample +SignificantBitsPerSample/value=A list of integers, one per channel +SampleMSB=The position of the most significant bit of each sample +SampleMSB/value=A list of integers, one per channel + +Dimension=Dimension information +PixelAspectRatio=The width of a pixel divided by its height +PixelAspectRatio/value=The width of a pixel divided by its height +ImageOrientation=The desired orientation of the image in terms of flips and counter-clockwise rotations +ImageOrientation/value=The desired orientation of the image in terms of flips and counter-clockwise rotations +HorizontalPixelSize=The width of a pixel, in millimeters, as it should be rendered on media +HorizontalPixelSize/value=The width of a pixel, in millimeters, as it should be rendered on media +VerticalPixelSize=The height of a pixel, in millimeters, as it should be rendered on media +VerticalPixelSize/value=The height of a pixel, in millimeters, as it should be rendered on media +HorizontalPhysicalPixelSpacing=The horizontal distance in the subject of the image, in millimeters, represented by one pixel at the center of the image +HorizontalPhysicalPixelSpacing/value=The horizontal distance in the subject of the image, in millimeters, represented by one pixel at the center of the image +VerticalPhysicalPixelSpacing=The vertical distance in the subject of the image, in millimeters, represented by one pixel at the center of the image +VerticalPhysicalPixelSpacing/value=The vertical distance in the subject of the image, in millimeters, represented by one pixel at the center of the image +HorizontalPosition=The horizontal position, in millimeters, where the image should be rendered on media +HorizontalPosition/value=The horizontal position, in millimeters, where the image should be rendered on media +VerticalPosition=The vertical position, in millimeters, where the image should be rendered on media +VerticalPosition/value=The vertical position, in millimeters, where the image should be rendered on media +HorizontalPixelOffset=The horizonal position, in pixels, where the image should be rendered onto a raster display +HorizontalPixelOffset/value=The horizonal position, in pixels, where the image should be rendered onto a raster display +VerticalPixelOffset=The vertical position, in pixels, where the image should be rendered onto a raster display +VerticalPixelOffset/value=The vertical position, in pixels, where the image should be rendered onto a raster display +HorizontalScreenSize=The width, in pixels, of the raster display into which the image should be rendered +HorizontalScreenSize/value=The width, in pixels, of the raster display into which the image should be rendered +VerticalScreenSize=The height, in pixels, of the raster display into which the image should be rendered +VerticalScreenSize/value=The height, in pixels, of the raster display into which the image should be rendered + +Document=Document information +FormatVersion=The version of the format used by the stream +FormatVersion/value=The version of the format used by the stream +SubimageInterpretation=The interpretation of this image in relation to the other images stored in the same stream +SubimageInterpretation/value=The interpretation of this image in relation to the other images stored in the same stream +ImageCreationTime=The time of image creation +ImageCreationTime/year=The full year (e.g., 1967, not 67) +ImageCreationTime/month=The month, with January = 1 +ImageCreationTime/day=The day of the month +ImageCreationTime/hour=The hour from 0 to 23 +ImageCreationTime/minute=The minute from 0 to 59 +ImageCreationTime/second=The second from 0 to 60 (60 = leap second) +ImageModificationTime=The time of the last image modification +ImageModificationTime/year=The full year (e.g., 1967, not 67) +ImageModificationTime/month=The month, with January = 1 +ImageModificationTime/day=The day of the month +ImageModificationTime/hour=The hour from 0 to 23 +ImageModificationTime/minute=The minute from 0 to 59 +ImageModificationTime/second=The second from 0 to 60 (60 = leap second) + +Text=Text information +TextEntry=A text entry +TextEntry/keyword=A keyword associated with the text entry +TextEntry/value=the text entry +TextEntry/language=The language of the text +TextEntry/encoding=The encoding of the text +TextEntry/compression=The method used to compress the text + +Transparency=Transparency information +Alpha=The type of alpha information contained in the image +Alpha/value=The type of alpha information contained in the image +TransparentIndex=A palette index to be treated as transparent +TransparentIndex/value=A palette index to be treated as transparent +TransparentColor=An RGB color to be treated as transparent +TransparentColor/value=An RGB color to be treated as transparent +TileTransparencies=A list of completely transparent tiles +TransparentTile=The index of a completely transparent tile +TransparentTile/x=The tile's X index +TransparentTile/y=The tile's Y index +TileOpacities=A list of completely opaque tiles +OpaqueTile=The index of a completely opaque tile +OpaqueTile/x=The tile's X index +OpaqueTile/y=The tile's Y index diff --git a/awt/javax/imageio/metadata/package.html b/awt/javax/imageio/metadata/package.html new file mode 100644 index 000000000..29bd51b2c --- /dev/null +++ b/awt/javax/imageio/metadata/package.html @@ -0,0 +1,8 @@ + + +

    + This package contains classes which allows to read and write describing metadata of image files. +

    + @since Android 1.0 + + diff --git a/awt/javax/imageio/package.html b/awt/javax/imageio/package.html new file mode 100644 index 000000000..2fd614874 --- /dev/null +++ b/awt/javax/imageio/package.html @@ -0,0 +1,8 @@ + + +

    + This package contains classes and interfaces which provides an Image I/O API. The contained classes and interfaces allow reading and writing image files of different formats. +

    + @since Android 1.0 + + diff --git a/awt/javax/imageio/plugins/bmp/BMPImageWriteParam.java b/awt/javax/imageio/plugins/bmp/BMPImageWriteParam.java new file mode 100644 index 000000000..ecfb20ad3 --- /dev/null +++ b/awt/javax/imageio/plugins/bmp/BMPImageWriteParam.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.plugins.bmp; + +import javax.imageio.ImageWriteParam; +import java.util.Locale; + +/** + * The BMPImageWriteParam class allows encoding an image in BMP format. + * + * @since Android 1.0 + */ +public class BMPImageWriteParam extends ImageWriteParam { + + /** + * The top down. + */ + private boolean topDown; // Default is bottom-up + + /** + * Instantiates a new BMPImageWriteParam with default values of all + * parameters. + */ + public BMPImageWriteParam() { + this(null); + } + + /** + * Instantiates a new BMPImageWriteParam with the specified Locale. + * + * @param locale + * the specified Locale. + */ + public BMPImageWriteParam(Locale locale) { + super(locale); + + // Set the compression + canWriteCompressed = true; + compressionTypes = new String[] { + "BI_RGB", "BI_RLE8", "BI_RLE4", "BI_BITFIELDS" + }; + compressionType = compressionTypes[0]; + } + + /** + * Sets true if the data will be written in a top-down order, false + * otherwise. + * + * @param topDown + * the new top-down value. + */ + public void setTopDown(boolean topDown) { + this.topDown = topDown; + } + + /** + * Returns true if the data is written in top-down order, false otherwise. + * + * @return true if the data is written in top-down order, false otherwise. + */ + public boolean isTopDown() { + return topDown; + } +} diff --git a/awt/javax/imageio/plugins/bmp/package.html b/awt/javax/imageio/plugins/bmp/package.html new file mode 100644 index 000000000..9494a102d --- /dev/null +++ b/awt/javax/imageio/plugins/bmp/package.html @@ -0,0 +1,8 @@ + + +

    + This package contains auxiliary classes for the built-in BMP image plug-in. +

    + @since Android 1.0 + + diff --git a/awt/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java b/awt/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java new file mode 100644 index 000000000..67b504be2 --- /dev/null +++ b/awt/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.plugins.jpeg; + +/** + * The JPEGHuffmanTable class represents a single JPEG Huffman table. It + * contains the standard tables from the JPEG specification. + * + * @since Android 1.0 + */ +public class JPEGHuffmanTable { + + /** + * The standard DC luminance Huffman table . + */ + public static final JPEGHuffmanTable StdDCLuminance = new JPEGHuffmanTable(new short[] { + 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 + }, new short[] { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0A, 0x0B + }, false); + + /** + * The standard DC chrominance Huffman table. + */ + public static final JPEGHuffmanTable StdDCChrominance = new JPEGHuffmanTable(new short[] { + 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 + }, new short[] { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0A, 0x0B + }, false); + + /** + * The standard AC luminance Huffman table. + */ + public static final JPEGHuffmanTable StdACLuminance = new JPEGHuffmanTable(new short[] { + 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7D + }, new short[] { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, + 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, + 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, + 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, + 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, + 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA + }, false); + + /** + * The standard AC chrominance Huffman table. + */ + public static final JPEGHuffmanTable StdACChrominance = new JPEGHuffmanTable(new short[] { + 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 + }, new short[] { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, + 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, + 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, + 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, + 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, + 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, + 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA + }, false); + + /** + * The lengths. + */ + private short lengths[]; + + /** + * The values. + */ + private short values[]; + + /** + * Instantiates a new jPEG huffman table. + * + * @param lengths + * the lengths + * @param values + * the values + * @param copy + * the copy + */ + JPEGHuffmanTable(short[] lengths, short[] values, boolean copy) { + // Construction of standard tables without checks + // The third param is dummy + // Could be also used for copying of the existing tables + this.lengths = lengths; + this.values = values; + } + + /** + * Instantiates a new JPEGHuffmanTable. + * + * @param lengths + * the array of shorts lengths. + * @param values + * the array of shorts containing the values in order of + * increasing code length. + */ + public JPEGHuffmanTable(short[] lengths, short[] values) { + if (lengths == null) { + throw new IllegalArgumentException("lengths array is null!"); + } + if (values == null) { + throw new IllegalArgumentException("values array is null!"); + } + if (lengths.length > 16) { // According to the spec + throw new IllegalArgumentException("lengths array is too long!"); + } + if (values.length > 256) { // According to the spec + throw new IllegalArgumentException("values array is too long"); + } + for (short length : lengths) { + if (length < 0) { + throw new IllegalArgumentException("Values in lengths array must be non-negative."); + } + } + for (short value : values) { + if (value < 0) { + throw new IllegalArgumentException("Values in values array must be non-negative."); + } + } + + checkHuffmanTable(lengths, values); + + this.lengths = new short[lengths.length]; + this.values = new short[values.length]; + System.arraycopy(lengths, 0, this.lengths, 0, lengths.length); + System.arraycopy(values, 0, this.values, 0, values.length); + } + + /** + * Gets an array of lengths in the Huffman table. + * + * @return the array of short values representing the length values in the + * Huffman table. + */ + public short[] getLengths() { + short newLengths[] = new short[lengths.length]; + System.arraycopy(lengths, 0, newLengths, 0, lengths.length); + return newLengths; + } + + /** + * Gets an array of values represented by increasing length of their codes. + * + * @return the array of values. + */ + public short[] getValues() { + short newValues[] = new short[values.length]; + System.arraycopy(values, 0, newValues, 0, values.length); + return newValues; + } + + /** + * Check huffman table. + * + * @param lengths + * the lengths. + * @param values + * the values. + */ + private static void checkHuffmanTable(short[] lengths, short[] values) { + int numLeaves = 0; + int possibleLeaves = 2; + for (short length : lengths) { + numLeaves += length; + possibleLeaves -= length; + if (possibleLeaves < 0) { + throw new IllegalArgumentException( + "Invalid Huffman table provided, lengths are incorrect."); + } + possibleLeaves <<= 1; + } + + if (values.length != numLeaves) { + throw new IllegalArgumentException( + "Invalid Huffman table provided, sum of lengths != values."); + } + } + + /** + * Returns the string representation of this JPEGHuffmanTable object. + * + * @return the string representation of this JPEGHuffmanTable object. + */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("JPEGHuffmanTable:\nlengths:"); + for (short length : lengths) { + sb.append(' ').append(length); + } + + sb.append("\nvalues:"); + for (short value : values) { + sb.append(' ').append(value); + } + + return sb.toString(); + } +} diff --git a/awt/javax/imageio/plugins/jpeg/JPEGImageReadParam.java b/awt/javax/imageio/plugins/jpeg/JPEGImageReadParam.java new file mode 100644 index 000000000..2f3a9a8fb --- /dev/null +++ b/awt/javax/imageio/plugins/jpeg/JPEGImageReadParam.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.plugins.jpeg; + +import javax.imageio.ImageReadParam; + +/** + * The JPEGImageReadParam class provides functionality to set Huffman tables and + * quantization tables when using the JPEG reader plug-in. + * + * @since Android 1.0 + */ +public class JPEGImageReadParam extends ImageReadParam { + + /** + * The q tables. + */ + private JPEGQTable qTables[]; + + /** + * The dc huffman tables. + */ + private JPEGHuffmanTable dcHuffmanTables[]; + + /** + * The ac huffman tables. + */ + private JPEGHuffmanTable acHuffmanTables[]; + + /** + * Instantiates a new JPEGImageReadParam. + */ + public JPEGImageReadParam() { + } + + /** + * Returns true if tables are set, false otherwise. + * + * @return true, if tables are set, false otherwise. + */ + public boolean areTablesSet() { + return qTables != null; + } + + /** + * Sets the quantization and Huffman tables for using in decoding streams. + * + * @param qTables + * the quantization tables. + * @param DCHuffmanTables + * the standart DC Huffman tables. + * @param ACHuffmanTables + * the standart AC huffman tables. + */ + public void setDecodeTables(JPEGQTable[] qTables, JPEGHuffmanTable[] DCHuffmanTables, + JPEGHuffmanTable[] ACHuffmanTables) { + if (qTables == null || DCHuffmanTables == null || ACHuffmanTables == null) { + throw new IllegalArgumentException("Invalid JPEG table arrays"); + } + if (DCHuffmanTables.length != ACHuffmanTables.length) { + throw new IllegalArgumentException("Invalid JPEG table arrays"); + } + if (qTables.length > 4 || DCHuffmanTables.length > 4) { + throw new IllegalArgumentException("Invalid JPEG table arrays"); + } + + // Do the shallow copy, it should be enough + this.qTables = qTables.clone(); + dcHuffmanTables = DCHuffmanTables.clone(); + acHuffmanTables = ACHuffmanTables.clone(); + } + + /** + * Unset all decoded tables. + */ + public void unsetDecodeTables() { + qTables = null; + dcHuffmanTables = null; + acHuffmanTables = null; + } + + /** + * Gets the quantization tables. + * + * @return the quantization tables, or null. + */ + public JPEGQTable[] getQTables() { + return qTables == null ? null : qTables.clone(); + } + + /** + * Gets the DC Huffman tables. + * + * @return the DC Huffman tables which are set, or null. + */ + public JPEGHuffmanTable[] getDCHuffmanTables() { + return dcHuffmanTables == null ? null : dcHuffmanTables.clone(); + } + + /** + * Gets the AC Huffman tables. + * + * @return the AC Huffman tables which are set, or null. + */ + public JPEGHuffmanTable[] getACHuffmanTables() { + return acHuffmanTables == null ? null : acHuffmanTables.clone(); + } +} diff --git a/awt/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java b/awt/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java new file mode 100644 index 000000000..b9799112e --- /dev/null +++ b/awt/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.plugins.jpeg; + +import org.apache.harmony.x.imageio.plugins.jpeg.JPEGConsts; + +import javax.imageio.ImageWriteParam; +import java.util.Locale; + +/** + * The JPEGImageWriteParam class allows to set JPEG Huffman tables and + * quantization when using the JPEG writer plug-in. + * + * @since Android 1.0 + */ +public class JPEGImageWriteParam extends ImageWriteParam { + + /** + * The Constant COMP_QUALITY_VALUES. + */ + private static final float[] COMP_QUALITY_VALUES = { + 0.05f, 0.75f, 0.95f + }; + + /** + * The Constant COMP_QUALITY_DESCRIPTIONS. + */ + private static final String[] COMP_QUALITY_DESCRIPTIONS = { + "Minimum useful", "Visually lossless", "Maximum useful" + }; + + /** + * The q tables. + */ + private JPEGQTable[] qTables; + + /** + * The dc huffman tables. + */ + private JPEGHuffmanTable[] dcHuffmanTables; + + /** + * The ac huffman tables. + */ + private JPEGHuffmanTable[] acHuffmanTables; + + /** + * The optimize huffman tables. + */ + private boolean optimizeHuffmanTables; + + /** + * Instantiates a new JPEGImageWriteParam object with the specified Locale. + * + * @param locale + * the Locale. + */ + public JPEGImageWriteParam(Locale locale) { + super(locale); + + canWriteProgressive = true; + progressiveMode = ImageWriteParam.MODE_DISABLED; + + canWriteCompressed = true; + compressionTypes = new String[] { + "JPEG" + }; + compressionType = compressionTypes[0]; + compressionQuality = JPEGConsts.DEFAULT_JPEG_COMPRESSION_QUALITY; + } + + /** + * Returns true if tables are set, false otherwise. + * + * @return true, if tables are set, false otherwise. + */ + public boolean areTablesSet() { + return qTables != null; + } + + /** + * Sets the quantization and Huffman tables for using in encoding streams. + * + * @param qTables + * the quantization tables. + * @param DCHuffmanTables + * the standart DC Huffman tables. + * @param ACHuffmanTables + * the standart AC huffman tables. + */ + public void setEncodeTables(JPEGQTable[] qTables, JPEGHuffmanTable[] DCHuffmanTables, + JPEGHuffmanTable[] ACHuffmanTables) { + if (qTables == null || DCHuffmanTables == null || ACHuffmanTables == null) { + throw new IllegalArgumentException("Invalid JPEG table arrays"); + } + if (DCHuffmanTables.length != ACHuffmanTables.length) { + throw new IllegalArgumentException("Invalid JPEG table arrays"); + } + if (qTables.length > 4 || DCHuffmanTables.length > 4) { + throw new IllegalArgumentException("Invalid JPEG table arrays"); + } + + // Do the shallow copy, it should be enough + this.qTables = qTables.clone(); + dcHuffmanTables = DCHuffmanTables.clone(); + acHuffmanTables = ACHuffmanTables.clone(); + } + + /** + * Unset all encoded tables. + */ + public void unsetEncodeTables() { + qTables = null; + dcHuffmanTables = null; + acHuffmanTables = null; + } + + /** + * Gets the DC Huffman tables. + * + * @return the DC Huffman tables which are set, or null. + */ + public JPEGHuffmanTable[] getDCHuffmanTables() { + return dcHuffmanTables == null ? null : dcHuffmanTables.clone(); + } + + /** + * Gets the AC Huffman tables. + * + * @return the AC Huffman tables which are set, or null. + */ + public JPEGHuffmanTable[] getACHuffmanTables() { + return acHuffmanTables == null ? null : acHuffmanTables.clone(); + } + + /** + * Gets the quantization tables. + * + * @return the quantization tables, or null. + */ + public JPEGQTable[] getQTables() { + return qTables == null ? null : qTables.clone(); + } + + @Override + public String[] getCompressionQualityDescriptions() { + super.getCompressionQualityDescriptions(); + return COMP_QUALITY_DESCRIPTIONS.clone(); + } + + @Override + public float[] getCompressionQualityValues() { + super.getCompressionQualityValues(); + return COMP_QUALITY_VALUES.clone(); + } + + /** + * Sets the flag indicated that the writer will generate optimized Huffman + * tables for the image as part of the writing process. + * + * @param optimize + * the flag of optimizing huffman tables. + */ + public void setOptimizeHuffmanTables(boolean optimize) { + optimizeHuffmanTables = optimize; + } + + /** + * Returns true if the writer generates optimized Huffman tables, false + * otherwise. + * + * @return true, if the writer generates optimized Huffman tables, false + * otherwise. + */ + public boolean getOptimizeHuffmanTables() { + return optimizeHuffmanTables; + } + + @Override + public boolean isCompressionLossless() { + if (getCompressionMode() != MODE_EXPLICIT) { + throw new IllegalStateException("Compression mode not MODE_EXPLICIT!"); + } + return false; + } + + @Override + public void unsetCompression() { + if (getCompressionMode() != MODE_EXPLICIT) { + throw new IllegalStateException("Compression mode not MODE_EXPLICIT!"); + } + compressionQuality = JPEGConsts.DEFAULT_JPEG_COMPRESSION_QUALITY; + } +} diff --git a/awt/javax/imageio/plugins/jpeg/JPEGQTable.java b/awt/javax/imageio/plugins/jpeg/JPEGQTable.java new file mode 100644 index 000000000..3461d4669 --- /dev/null +++ b/awt/javax/imageio/plugins/jpeg/JPEGQTable.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.plugins.jpeg; + +/** + * The JPEGQTable class represents a single JPEG quantization table and provides + * for the standard tables taken from the JPEG specification. + * + * @since Android 1.0 + */ +public class JPEGQTable { + + /** + * The Constant SIZE. + */ + private final static int SIZE = 64; + + /** + * The Constant BASELINE_MAX. + */ + private final static int BASELINE_MAX = 255; + + /** + * The Constant MAX. + */ + private final static int MAX = 32767; + + /** + * The table. + */ + private int[] theTable; + + /* + * K1 & K2 tables can be found in the JPEG format specification at + * http://www.w3.org/Graphics/JPEG/itu-t81.pdf + */ + + /** + * The Constant K1LumTable. + */ + private static final int[] K1LumTable = new int[] { + 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, + 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, + 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, + 103, 99 + }; + + /** + * The Constant K2ChrTable. + */ + private static final int[] K2ChrTable = new int[] { + 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, + 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 + }; + + /** + * The K1Luminance indicates standard table K.1 from JPEG specification and + * produces "good" quality output. + */ + public static final JPEGQTable K1Luminance = new JPEGQTable(K1LumTable); + + /** + * The K1Div2Luminance indicates K.1 table from JPEG specification with all + * elements divided by 2 and produces "very good" quality output. + */ + public static final JPEGQTable K1Div2Luminance = K1Luminance.getScaledInstance(0.5f, true); + + /** + * The K2Chrominance indicates K.2 table from JPEG specification and + * produces "good" quality output. + */ + public static final JPEGQTable K2Chrominance = new JPEGQTable(K2ChrTable); + + /** + * The Constant K2Div2Chrominance indicates K.2 table from JPEG + * specification with all elements divided by 2 and produces "very good" + * quality output. + */ + public static final JPEGQTable K2Div2Chrominance = K2Chrominance.getScaledInstance(0.5f, true);; + + /** + * Instantiates a new JPEGQTable from the array, which should contain 64 + * elements in natural order. + * + * @param table + * the quantization table. + */ + public JPEGQTable(int[] table) { + if (table == null) { + throw new IllegalArgumentException("table should not be NULL"); + } + if (table.length != SIZE) { + throw new IllegalArgumentException("illegal table size: " + table.length); + } + theTable = table.clone(); + } + + /** + * Gets the current quantization table as an array of integer values. + * + * @return the current quantization table as an array of integer values. + */ + public int[] getTable() { + return theTable.clone(); + } + + /** + * Gets the scaled instance as quantization table where the values are + * multiplied by the scaleFactor and then clamped if forceBaseline is true. + * + * @param scaleFactor + * the scale factor of table. + * @param forceBaseline + * the force baseline flag, the values should be clamped if true. + * @return the new quantization table. + */ + public JPEGQTable getScaledInstance(float scaleFactor, boolean forceBaseline) { + int table[] = new int[SIZE]; + + int maxValue = forceBaseline ? BASELINE_MAX : MAX; + + for (int i = 0; i < theTable.length; i++) { + int rounded = Math.round(theTable[i] * scaleFactor); + if (rounded < 1) { + rounded = 1; + } + if (rounded > maxValue) { + rounded = maxValue; + } + table[i] = rounded; + } + return new JPEGQTable(table); + } + + /** + * Returns the string representation of this JPEGQTable object. + * + * @return the string representation of this JPEGQTable object. + */ + @Override + public String toString() { + // -- TODO more informative info + return "JPEGQTable"; + } +} diff --git a/awt/javax/imageio/plugins/jpeg/package.html b/awt/javax/imageio/plugins/jpeg/package.html new file mode 100644 index 000000000..14575c417 --- /dev/null +++ b/awt/javax/imageio/plugins/jpeg/package.html @@ -0,0 +1,8 @@ + + +

    + This package contains auxiliary classes for the built-in JPEG image plug-in. +

    + @since Android 1.0 + + diff --git a/awt/javax/imageio/spi/IIORegistry.java b/awt/javax/imageio/spi/IIORegistry.java new file mode 100644 index 000000000..01ddeaafe --- /dev/null +++ b/awt/javax/imageio/spi/IIORegistry.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.spi; + +import java.util.Arrays; + +import org.apache.harmony.x.imageio.plugins.jpeg.JPEGImageReaderSpi; +import org.apache.harmony.x.imageio.plugins.jpeg.JPEGImageWriterSpi; +import org.apache.harmony.x.imageio.plugins.png.PNGImageReaderSpi; +import org.apache.harmony.x.imageio.plugins.png.PNGImageWriterSpi; +import org.apache.harmony.x.imageio.spi.FileIISSpi; +import org.apache.harmony.x.imageio.spi.FileIOSSpi; +import org.apache.harmony.x.imageio.spi.InputStreamIISSpi; +import org.apache.harmony.x.imageio.spi.OutputStreamIOSSpi; +import org.apache.harmony.x.imageio.spi.RAFIISSpi; +import org.apache.harmony.x.imageio.spi.RAFIOSSpi; + +/* + * @author Rustem V. Rafikov, Viskov Nikolay + * @version $Revision: 1.3 $ + */ + +/** + * The IIORegistry class registers service provider instances (SPI). Service + * provider instances are recognized by specific meta-information in the JAR + * files containing them. The JAR files with SPI classes are loaded from the + * application class path. + * + * @since Android 1.0 + */ +public final class IIORegistry extends ServiceRegistry { + + /** + * The instance. + */ + private static IIORegistry instance; + + /** + * The Constant CATEGORIES. + */ + private static final Class[] CATEGORIES = new Class[] { + javax.imageio.spi.ImageWriterSpi.class, javax.imageio.spi.ImageReaderSpi.class, + javax.imageio.spi.ImageInputStreamSpi.class, + // javax.imageio.spi.ImageTranscoderSpi.class, + javax.imageio.spi.ImageOutputStreamSpi.class + }; + + /** + * Instantiates a new IIO registry. + */ + private IIORegistry() { + super(Arrays.> asList(CATEGORIES).iterator()); + registerBuiltinSpis(); + registerApplicationClasspathSpis(); + } + + /** + * Register built-in SPIs. + */ + private void registerBuiltinSpis() { + registerServiceProvider(new JPEGImageWriterSpi()); + registerServiceProvider(new JPEGImageReaderSpi()); + registerServiceProvider(new PNGImageReaderSpi()); + registerServiceProvider(new PNGImageWriterSpi()); + registerServiceProvider(new FileIOSSpi()); + registerServiceProvider(new FileIISSpi()); + registerServiceProvider(new RAFIOSSpi()); + registerServiceProvider(new RAFIISSpi()); + registerServiceProvider(new OutputStreamIOSSpi()); + registerServiceProvider(new InputStreamIISSpi()); + // -- TODO implement + } + + /** + * Gets the default IIORegistry instance. + * + * @return the default IIORegistry instance. + */ + public static IIORegistry getDefaultInstance() { + // TODO implement own instance for each ThreadGroup (see also + // ThreadLocal) + synchronized (IIORegistry.class) { + if (instance == null) { + instance = new IIORegistry(); + } + return instance; + } + } + + /** + * Registers all service providers from the application class path. + */ + public void registerApplicationClasspathSpis() { + // -- TODO implement for non-builtin plugins + } +} diff --git a/awt/javax/imageio/spi/IIOServiceProvider.java b/awt/javax/imageio/spi/IIOServiceProvider.java new file mode 100644 index 000000000..e9476773a --- /dev/null +++ b/awt/javax/imageio/spi/IIOServiceProvider.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.spi; + +import java.util.Locale; + +/** + * The IIOServiceProvider abstract class provides base functionality for ImageIO + * service provider interfaces (SPIs). + * + * @since Android 1.0 + */ +public abstract class IIOServiceProvider implements RegisterableService { + + /** + * The vendor name of this service provider. + */ + protected String vendorName; + + /** + * The version of this service provider. + */ + protected String version; + + /** + * Instantiates a new IIOServiceProvider. + * + * @param vendorName + * the vendor name of service provider. + * @param version + * the version of service provider. + */ + public IIOServiceProvider(String vendorName, String version) { + if (vendorName == null) { + throw new NullPointerException("vendor name cannot be NULL"); + } + if (version == null) { + throw new NullPointerException("version name cannot be NULL"); + } + this.vendorName = vendorName; + this.version = version; + } + + /** + * Instantiates a new IIOServiceProvider. + */ + public IIOServiceProvider() { + throw new UnsupportedOperationException("Not supported yet"); + } + + public void onRegistration(ServiceRegistry registry, Class category) { + // the default impl. does nothing + } + + public void onDeregistration(ServiceRegistry registry, Class category) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets the vendor name of this service provider. + * + * @return the vendor name of this service provider. + */ + public String getVendorName() { + return vendorName; + } + + /** + * Gets the version of this service provider. + * + * @return the version of this service provider. + */ + public String getVersion() { + return version; + } + + /** + * Gets a description of this service provider. The result string should be + * localized for the specified Locale. + * + * @param locale + * the specified Locale. + * @return the description of this service provider. + */ + public abstract String getDescription(Locale locale); +} diff --git a/awt/javax/imageio/spi/ImageInputStreamSpi.java b/awt/javax/imageio/spi/ImageInputStreamSpi.java new file mode 100644 index 000000000..fc859a871 --- /dev/null +++ b/awt/javax/imageio/spi/ImageInputStreamSpi.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.spi; + +import java.io.File; +import java.io.IOException; +import javax.imageio.stream.ImageInputStream; + +/** + * The ImageInputStreamSpi abstract class is a service provider interface (SPI) + * for ImageInputStreams. + * + * @since Android 1.0 + */ +public abstract class ImageInputStreamSpi extends IIOServiceProvider implements RegisterableService { + + /** + * The input class. + */ + protected Class inputClass; + + /** + * Instantiates a new ImageInputStreamSpi. + */ + protected ImageInputStreamSpi() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Instantiates a new ImageInputStreamSpi. + * + * @param vendorName + * the vendor name. + * @param version + * the version. + * @param inputClass + * the input class. + */ + public ImageInputStreamSpi(String vendorName, String version, Class inputClass) { + super(vendorName, version); + this.inputClass = inputClass; + } + + /** + * Gets an input Class object that represents class or interface that must + * be implemented by an input source. + * + * @return the input class. + */ + public Class getInputClass() { + return inputClass; + } + + /** + * Returns true if the ImageInputStream can use a cache file. If this method + * returns false, the value of the useCache parameter of + * createInputStreamInstance will be ignored. The default implementation + * returns false. + * + * @return true, if the ImageInputStream can use a cache file, false + * otherwise. + */ + public boolean canUseCacheFile() { + return false; // -- def + } + + /** + * Returns true if the ImageInputStream implementation requires the use of a + * cache file. The default implementation returns false. + * + * @return true, if the ImageInputStream implementation requires the use of + * a cache file, false otherwise. + */ + public boolean needsCacheFile() { + return false; // def + } + + /** + * Creates the ImageInputStream associated with this service provider. The + * input object should be an instance of the class returned by the + * getInputClass method. This method uses the specified directory for the + * cache file if the useCache parameter is true. + * + * @param input + * the input Object. + * @param useCache + * the flag indicating if a cache file is needed or not. + * @param cacheDir + * the cache directory. + * @return the ImageInputStream. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract ImageInputStream createInputStreamInstance(Object input, boolean useCache, + File cacheDir) throws IOException; + + /** + * Creates the ImageInputStream associated with this service provider. The + * input object should be an instance of the class returned by getInputClass + * method. This method uses the default system directory for the cache file, + * if it is needed. + * + * @param input + * the input Object. + * @return the ImageInputStream. + * @throws IOException + * if an I/O exception has occurred. + */ + public ImageInputStream createInputStreamInstance(Object input) throws IOException { + return createInputStreamInstance(input, true, null); + } +} diff --git a/awt/javax/imageio/spi/ImageOutputStreamSpi.java b/awt/javax/imageio/spi/ImageOutputStreamSpi.java new file mode 100644 index 000000000..b7a9a5c33 --- /dev/null +++ b/awt/javax/imageio/spi/ImageOutputStreamSpi.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.spi; + +import javax.imageio.stream.ImageOutputStream; +import java.io.IOException; +import java.io.File; + +/** + * The ImageOutputStreamSpi abstract class is a service provider interface (SPI) + * for ImageOutputStreams. + * + * @since Android 1.0 + */ +public abstract class ImageOutputStreamSpi extends IIOServiceProvider implements + RegisterableService { + + /** + * The output class. + */ + protected Class outputClass; + + /** + * Instantiates a new ImageOutputStreamSpi. + */ + protected ImageOutputStreamSpi() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Instantiates a new ImageOutputStreamSpi. + * + * @param vendorName + * the vendor name. + * @param version + * the version. + * @param outputClass + * the output class. + */ + public ImageOutputStreamSpi(String vendorName, String version, Class outputClass) { + super(vendorName, version); + this.outputClass = outputClass; + } + + /** + * Gets an output Class object that represents the class or interface that + * must be implemented by an output source. + * + * @return the output class. + */ + public Class getOutputClass() { + return outputClass; + } + + /** + * Returns true if the ImageOutputStream can use a cache file. If this + * method returns false, the value of the useCache parameter of + * createOutputStreamInstance will be ignored. The default implementation + * returns false. + * + * @return true, if the ImageOutputStream can use a cache file, false + * otherwise. + */ + public boolean canUseCacheFile() { + return false; // def + } + + /** + * Returns true if the ImageOutputStream implementation requires the use of + * a cache file. The default implementation returns false. + * + * @return true, if the ImageOutputStream implementation requires the use of + * a cache file, false otherwise. + */ + public boolean needsCacheFile() { + return false; // def + } + + /** + * Creates the ImageOutputStream associated with this service provider. The + * output object should be an instance of the class returned by + * getOutputClass method. This method uses the default system directory for + * the cache file, if it is needed. + * + * @param output + * the output Object. + * @return the ImageOutputStream. + * @throws IOException + * if an I/O exception has occurred. + */ + public ImageOutputStream createOutputStreamInstance(Object output) throws IOException { + return createOutputStreamInstance(output, true, null); + } + + /** + * Creates the ImageOutputStream associated with this service provider. The + * output object should be an instance of the class returned by + * getInputClass method. This method uses the specified directory for the + * cache file, if the useCache parameter is true. + * + * @param output + * the output Object. + * @param useCache + * the flag indicating if cache file is needed or not. + * @param cacheDir + * the cache directory. + * @return the ImageOutputStream. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract ImageOutputStream createOutputStreamInstance(Object output, boolean useCache, + File cacheDir) throws IOException; +} diff --git a/awt/javax/imageio/spi/ImageReaderSpi.java b/awt/javax/imageio/spi/ImageReaderSpi.java new file mode 100644 index 000000000..0528d250b --- /dev/null +++ b/awt/javax/imageio/spi/ImageReaderSpi.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.spi; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.ImageReader; +import java.io.IOException; + +/** + * The ImageReaderSpi abstract class is a service provider interface (SPI) for + * ImageReaders. + * + * @since Android 1.0 + */ +public abstract class ImageReaderSpi extends ImageReaderWriterSpi { + + /** + * The STANDARD_INPUT_TYPE contains ImageInputStream.class. + */ + public static final Class[] STANDARD_INPUT_TYPE = new Class[] { + ImageInputStream.class + }; + + /** + * The input types. + */ + protected Class[] inputTypes; + + /** + * The writer SPI names. + */ + protected String[] writerSpiNames; + + /** + * Instantiates a new ImageReaderSpi. + */ + protected ImageReaderSpi() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Instantiates a new ImageReaderSpi. + * + * @param vendorName + * the vendor name. + * @param version + * the version. + * @param names + * the format names. + * @param suffixes + * the array of strings representing the file suffixes. + * @param MIMETypes + * the an array of strings representing MIME types. + * @param pluginClassName + * the plug-in class name. + * @param inputTypes + * the input types. + * @param writerSpiNames + * the array of strings with class names of all associated + * ImageWriters. + * @param supportsStandardStreamMetadataFormat + * the value indicating if stream metadata can be described by + * standard metadata format. + * @param nativeStreamMetadataFormatName + * the native stream metadata format name, returned by + * getNativeStreamMetadataFormatName. + * @param nativeStreamMetadataFormatClassName + * the native stream metadata format class name, returned by + * getNativeStreamMetadataFormat. + * @param extraStreamMetadataFormatNames + * the extra stream metadata format names, returned by + * getExtraStreamMetadataFormatNames. + * @param extraStreamMetadataFormatClassNames + * the extra stream metadata format class names, returned by + * getStreamMetadataFormat. + * @param supportsStandardImageMetadataFormat + * the value indicating if image metadata can be described by + * standard metadata format. + * @param nativeImageMetadataFormatName + * the native image metadata format name, returned by + * getNativeImageMetadataFormatName. + * @param nativeImageMetadataFormatClassName + * the native image metadata format class name, returned by + * getNativeImageMetadataFormat. + * @param extraImageMetadataFormatNames + * the extra image metadata format names, returned by + * getExtraImageMetadataFormatNames. + * @param extraImageMetadataFormatClassNames + * the extra image metadata format class names, returned by + * getImageMetadataFormat. + */ + public ImageReaderSpi(String vendorName, String version, String[] names, String[] suffixes, + String[] MIMETypes, String pluginClassName, Class[] inputTypes, + String[] writerSpiNames, boolean supportsStandardStreamMetadataFormat, + String nativeStreamMetadataFormatName, String nativeStreamMetadataFormatClassName, + String[] extraStreamMetadataFormatNames, String[] extraStreamMetadataFormatClassNames, + boolean supportsStandardImageMetadataFormat, String nativeImageMetadataFormatName, + String nativeImageMetadataFormatClassName, String[] extraImageMetadataFormatNames, + String[] extraImageMetadataFormatClassNames) { + super(vendorName, version, names, suffixes, MIMETypes, pluginClassName, + supportsStandardStreamMetadataFormat, nativeStreamMetadataFormatName, + nativeStreamMetadataFormatClassName, extraStreamMetadataFormatNames, + extraStreamMetadataFormatClassNames, supportsStandardImageMetadataFormat, + nativeImageMetadataFormatName, nativeImageMetadataFormatClassName, + extraImageMetadataFormatNames, extraImageMetadataFormatClassNames); + + if (inputTypes == null || inputTypes.length == 0) { + throw new NullPointerException("input types array cannot be NULL or empty"); + } + this.inputTypes = inputTypes; + this.writerSpiNames = writerSpiNames; + } + + /** + * Gets an array of Class objects whose types can be used as input for this + * reader. + * + * @return the input types. + */ + public Class[] getInputTypes() { + return inputTypes; + } + + /** + * Returns true if the format of source object is supported by this reader. + * + * @param source + * the source object to be decoded (for example an + * ImageInputStream). + * @return true, if the format of source object is supported by this reader, + * false otherwise. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract boolean canDecodeInput(Object source) throws IOException; + + /** + * Returns an instance of the ImageReader implementation for this service + * provider. + * + * @return the ImageReader. + * @throws IOException + * if an I/O exception has occurred. + */ + public ImageReader createReaderInstance() throws IOException { + return createReaderInstance(null); + } + + /** + * Returns an instance of the ImageReader implementation for this service + * provider. + * + * @param extension + * the a plug-in specific extension object, or null. + * @return the ImageReader. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract ImageReader createReaderInstance(Object extension) throws IOException; + + /** + * Checks whether or not the specified ImageReader object is an instance of + * the ImageReader associated with this service provider or not. + * + * @param reader + * the ImageReader. + * @return true, if the specified ImageReader object is an instance of the + * ImageReader associated with this service provider, false + * otherwise. + */ + public boolean isOwnReader(ImageReader reader) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets an array of strings with names of the ImageWriterSpi classes that + * support the internal metadata representation used by the ImageReader of + * this service provider, or null if there are no such ImageWriters. + * + * @return the array of strings with names of the ImageWriterSpi classes. + */ + public String[] getImageWriterSpiNames() { + throw new UnsupportedOperationException("Not supported yet"); + } +} diff --git a/awt/javax/imageio/spi/ImageReaderWriterSpi.java b/awt/javax/imageio/spi/ImageReaderWriterSpi.java new file mode 100644 index 000000000..9ca08b5ea --- /dev/null +++ b/awt/javax/imageio/spi/ImageReaderWriterSpi.java @@ -0,0 +1,344 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.spi; + +import org.apache.harmony.x.imageio.metadata.IIOMetadataUtils; + +import javax.imageio.metadata.IIOMetadataFormat; + +/** + * The ImageReaderWriterSpi class is a superclass for the ImageReaderSpi and + * ImageWriterSpi SPIs. + * + * @since Android 1.0 + */ +public abstract class ImageReaderWriterSpi extends IIOServiceProvider implements + RegisterableService { + + /** + * The names. + */ + protected String[] names; + + /** + * The suffixes. + */ + protected String[] suffixes; + + /** + * The MIME types. + */ + protected String[] MIMETypes; + + /** + * The plug-in class name. + */ + protected String pluginClassName; + + /** + * Whether the reader/writer supports standard stream metadata format. + */ + protected boolean supportsStandardStreamMetadataFormat; + + /** + * The native stream metadata format name. + */ + protected String nativeStreamMetadataFormatName; + + /** + * The native stream metadata format class name. + */ + protected String nativeStreamMetadataFormatClassName; + + /** + * The extra stream metadata format names. + */ + protected String[] extraStreamMetadataFormatNames; + + /** + * The extra stream metadata format class names. + */ + protected String[] extraStreamMetadataFormatClassNames; + + /** + * Whether the reader/writer supports standard image metadata format. + */ + protected boolean supportsStandardImageMetadataFormat; + + /** + * The native image metadata format name. + */ + protected String nativeImageMetadataFormatName; + + /** + * The native image metadata format class name. + */ + protected String nativeImageMetadataFormatClassName; + + /** + * The extra image metadata format names. + */ + protected String[] extraImageMetadataFormatNames; + + /** + * The extra image metadata format class names. + */ + protected String[] extraImageMetadataFormatClassNames; + + /** + * Instantiates a new ImageReaderWriterSpi. + * + * @param vendorName + * the vendor name. + * @param version + * the version. + * @param names + * the format names. + * @param suffixes + * the array of strings representing the file suffixes. + * @param MIMETypes + * the an array of strings representing MIME types. + * @param pluginClassName + * the plug-in class name. + * @param supportsStandardStreamMetadataFormat + * the value indicating if stream metadata can be described by + * standard metadata format. + * @param nativeStreamMetadataFormatName + * the native stream metadata format name, returned by + * getNativeStreamMetadataFormatName. + * @param nativeStreamMetadataFormatClassName + * the native stream metadata format class name, returned by + * getNativeStreamMetadataFormat. + * @param extraStreamMetadataFormatNames + * the extra stream metadata format names, returned by + * getExtraStreamMetadataFormatNames. + * @param extraStreamMetadataFormatClassNames + * the extra stream metadata format class names, returned by + * getStreamMetadataFormat. + * @param supportsStandardImageMetadataFormat + * the value indicating if image metadata can be described by + * standard metadata format. + * @param nativeImageMetadataFormatName + * the native image metadata format name, returned by + * getNativeImageMetadataFormatName. + * @param nativeImageMetadataFormatClassName + * the native image metadata format class name, returned by + * getNativeImageMetadataFormat. + * @param extraImageMetadataFormatNames + * the extra image metadata format names, returned by + * getExtraImageMetadataFormatNames. + * @param extraImageMetadataFormatClassNames + * the extra image metadata format class names, returned by + * getImageMetadataFormat. + */ + public ImageReaderWriterSpi(String vendorName, String version, String[] names, + String[] suffixes, String[] MIMETypes, String pluginClassName, + boolean supportsStandardStreamMetadataFormat, String nativeStreamMetadataFormatName, + String nativeStreamMetadataFormatClassName, String[] extraStreamMetadataFormatNames, + String[] extraStreamMetadataFormatClassNames, + boolean supportsStandardImageMetadataFormat, String nativeImageMetadataFormatName, + String nativeImageMetadataFormatClassName, String[] extraImageMetadataFormatNames, + String[] extraImageMetadataFormatClassNames) { + super(vendorName, version); + + if (names == null || names.length == 0) { + throw new NullPointerException("format names array cannot be NULL or empty"); + } + + if (pluginClassName == null) { + throw new NullPointerException("Plugin class name cannot be NULL"); + } + + // We clone all the arrays to be consistent with the fact that + // some methods of this class must return clones of the arrays + // as it is stated in the spec. + this.names = names.clone(); + this.suffixes = suffixes == null ? null : suffixes.clone(); + this.MIMETypes = MIMETypes == null ? null : MIMETypes.clone(); + this.pluginClassName = pluginClassName; + this.supportsStandardStreamMetadataFormat = supportsStandardStreamMetadataFormat; + this.nativeStreamMetadataFormatName = nativeStreamMetadataFormatName; + this.nativeStreamMetadataFormatClassName = nativeStreamMetadataFormatClassName; + + this.extraStreamMetadataFormatNames = extraStreamMetadataFormatNames == null ? null + : extraStreamMetadataFormatNames.clone(); + + this.extraStreamMetadataFormatClassNames = extraStreamMetadataFormatClassNames == null ? null + : extraStreamMetadataFormatClassNames.clone(); + + this.supportsStandardImageMetadataFormat = supportsStandardImageMetadataFormat; + this.nativeImageMetadataFormatName = nativeImageMetadataFormatName; + this.nativeImageMetadataFormatClassName = nativeImageMetadataFormatClassName; + + this.extraImageMetadataFormatNames = extraImageMetadataFormatNames == null ? null + : extraImageMetadataFormatNames.clone(); + + this.extraImageMetadataFormatClassNames = extraImageMetadataFormatClassNames == null ? null + : extraImageMetadataFormatClassNames.clone(); + } + + /** + * Instantiates a new ImageReaderWriterSpi. + */ + public ImageReaderWriterSpi() { + } + + /** + * Gets an array of strings representing names of the formats that can be + * used by the ImageReader or ImageWriter implementation associated with + * this service provider. + * + * @return the array of supported format names. + */ + public String[] getFormatNames() { + return names.clone(); + } + + /** + * Gets an array of strings representing file suffixes associated with the + * formats that can be used by the ImageReader or ImageWriter implementation + * of this service provider. + * + * @return the array of file suffixes. + */ + public String[] getFileSuffixes() { + return suffixes == null ? null : suffixes.clone(); + } + + /** + * Gets an array of strings with the names of additional formats of the + * image metadata objects produced or consumed by this plug-in. + * + * @return the array of extra image metadata format names. + */ + public String[] getExtraImageMetadataFormatNames() { + return extraImageMetadataFormatNames == null ? null : extraImageMetadataFormatNames.clone(); + } + + /** + * Gets an array of strings with the names of additional formats of the + * stream metadata objects produced or consumed by this plug-in. + * + * @return the array of extra stream metadata format names. + */ + public String[] getExtraStreamMetadataFormatNames() { + return extraStreamMetadataFormatNames == null ? null : extraStreamMetadataFormatNames + .clone(); + } + + /** + * Gets an IIOMetadataFormat object for the specified image metadata format + * name. + * + * @param formatName + * the format name. + * @return the IIOMetadataFormat, or null. + */ + public IIOMetadataFormat getImageMetadataFormat(String formatName) { + return IIOMetadataUtils.instantiateMetadataFormat(formatName, + supportsStandardImageMetadataFormat, nativeImageMetadataFormatName, + nativeImageMetadataFormatClassName, extraImageMetadataFormatNames, + extraImageMetadataFormatClassNames); + } + + /** + * Gets an IIOMetadataFormat object for the specified stream metadata format + * name. + * + * @param formatName + * the format name. + * @return the IIOMetadataFormat, or null. + */ + public IIOMetadataFormat getStreamMetadataFormat(String formatName) { + return IIOMetadataUtils.instantiateMetadataFormat(formatName, + supportsStandardStreamMetadataFormat, nativeStreamMetadataFormatName, + nativeStreamMetadataFormatClassName, extraStreamMetadataFormatNames, + extraStreamMetadataFormatClassNames); + } + + /** + * Gets an array of strings representing the MIME types of the formats that + * are supported by the ImageReader or ImageWriter implementation of this + * service provider. + * + * @return the array MIME types. + */ + public String[] getMIMETypes() { + return MIMETypes == null ? null : MIMETypes.clone(); + } + + /** + * Gets the name of the native image metadata format for this reader/writer, + * which allows for lossless encoding or decoding of the image metadata with + * the format. + * + * @return the string with native image metadata format name, or null. + */ + public String getNativeImageMetadataFormatName() { + return nativeImageMetadataFormatName; + } + + /** + * Gets the name of the native stream metadata format for this + * reader/writer, which allows for lossless encoding or decoding of the + * stream metadata with the format. + * + * @return the string with native stream metadata format name, or null. + */ + public String getNativeStreamMetadataFormatName() { + return nativeStreamMetadataFormatName; + } + + /** + * Gets the class name of the ImageReader or ImageWriter associated with + * this service provider. + * + * @return the class name. + */ + public String getPluginClassName() { + return pluginClassName; + } + + /** + * Checks if the standard metadata format is supported by the getAsTree and + * setFromTree methods for the image metadata objects produced or consumed + * by this reader or writer. + * + * @return true, if standard image metadata format is supported, false + * otherwise. + */ + public boolean isStandardImageMetadataFormatSupported() { + return supportsStandardImageMetadataFormat; + } + + /** + * Checks if the standard metadata format is supported by the getAsTree and + * setFromTree methods for the stream metadata objects produced or consumed + * by this reader or writer. + * + * @return true, if standard stream metadata format is supported, false + * otherwise. + */ + public boolean isStandardStreamMetadataFormatSupported() { + return supportsStandardStreamMetadataFormat; + } +} diff --git a/awt/javax/imageio/spi/ImageTranscoderSpi.java b/awt/javax/imageio/spi/ImageTranscoderSpi.java new file mode 100644 index 000000000..742af1908 --- /dev/null +++ b/awt/javax/imageio/spi/ImageTranscoderSpi.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.spi; + +import javax.imageio.ImageTranscoder; + +/** + * The ImageTranscoderSpi class is a service provider interface (SPI) for + * ImageTranscoders. + * + * @since Android 1.0 + */ +public abstract class ImageTranscoderSpi extends IIOServiceProvider implements RegisterableService { + + /** + * Instantiates a new ImageTranscoderSpi. + */ + protected ImageTranscoderSpi() { + } + + /** + * Instantiates a new ImageTranscoderSpi with the specified vendor name and + * version. + * + * @param vendorName + * the vendor name. + * @param version + * the version. + */ + public ImageTranscoderSpi(String vendorName, String version) { + super(vendorName, version); + } + + /** + * Gets the class name of an ImageReaderSpi that produces IIOMetadata + * objects that can be used as input to this transcoder. + * + * @return the class name of an ImageReaderSpi. + */ + public abstract String getReaderServiceProviderName(); + + /** + * Gets the class name of an ImageWriterSpi that produces IIOMetadata + * objects that can be used as input to this transcoder. + * + * @return the class name of an ImageWriterSpi. + */ + public abstract String getWriterServiceProviderName(); + + /** + * Creates an instance of the ImageTranscoder associated with this service + * provider. + * + * @return the ImageTranscoder instance. + */ + public abstract ImageTranscoder createTranscoderInstance(); +} diff --git a/awt/javax/imageio/spi/ImageWriterSpi.java b/awt/javax/imageio/spi/ImageWriterSpi.java new file mode 100644 index 000000000..bf2545592 --- /dev/null +++ b/awt/javax/imageio/spi/ImageWriterSpi.java @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.spi; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriter; +import java.awt.image.RenderedImage; +import java.io.IOException; + +/** + * The ImageWriterSpi abstract class is a service provider interface (SPI) for + * ImageWriters. + * + * @since Android 1.0 + */ +public abstract class ImageWriterSpi extends ImageReaderWriterSpi { + + /** + * The STANDARD_OUTPUT_TYPE contains ImageInputStream.class. + */ + public static final Class[] STANDARD_OUTPUT_TYPE = new Class[] { + ImageInputStream.class + }; + + /** + * The output types. + */ + protected Class[] outputTypes; + + /** + * The reader SPI names. + */ + protected String[] readerSpiNames; + + /** + * Instantiates a new ImageWriterSpi. + */ + protected ImageWriterSpi() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Instantiates a new ImageWriterSpi with the specified parameters. + * + * @param vendorName + * the vendor name. + * @param version + * the version. + * @param names + * the format names. + * @param suffixes + * the array of strings representing the file suffixes. + * @param MIMETypes + * the an array of strings representing MIME types. + * @param pluginClassName + * the plug-in class name. + * @param outputTypes + * the output types. + * @param readerSpiNames + * the array of strings with class names of all associated + * ImageReaders. + * @param supportsStandardStreamMetadataFormat + * the value indicating if stream metadata can be described by + * standard metadata format. + * @param nativeStreamMetadataFormatName + * the native stream metadata format name, returned by + * getNativeStreamMetadataFormatName. + * @param nativeStreamMetadataFormatClassName + * the native stream metadata format class name, returned by + * getNativeStreamMetadataFormat. + * @param extraStreamMetadataFormatNames + * the extra stream metadata format names, returned by + * getExtraStreamMetadataFormatNames. + * @param extraStreamMetadataFormatClassNames + * the extra stream metadata format class names, returned by + * getStreamMetadataFormat. + * @param supportsStandardImageMetadataFormat + * the value indicating if image metadata can be described by + * standard metadata format. + * @param nativeImageMetadataFormatName + * the native image metadata format name, returned by + * getNativeImageMetadataFormatName. + * @param nativeImageMetadataFormatClassName + * the native image metadata format class name, returned by + * getNativeImageMetadataFormat. + * @param extraImageMetadataFormatNames + * the extra image metadata format names, returned by + * getExtraImageMetadataFormatNames. + * @param extraImageMetadataFormatClassNames + * the extra image metadata format class names, returned by + * getImageMetadataFormat. + */ + public ImageWriterSpi(String vendorName, String version, String[] names, String[] suffixes, + String[] MIMETypes, String pluginClassName, Class[] outputTypes, + String[] readerSpiNames, boolean supportsStandardStreamMetadataFormat, + String nativeStreamMetadataFormatName, String nativeStreamMetadataFormatClassName, + String[] extraStreamMetadataFormatNames, String[] extraStreamMetadataFormatClassNames, + boolean supportsStandardImageMetadataFormat, String nativeImageMetadataFormatName, + String nativeImageMetadataFormatClassName, String[] extraImageMetadataFormatNames, + String[] extraImageMetadataFormatClassNames) { + super(vendorName, version, names, suffixes, MIMETypes, pluginClassName, + supportsStandardStreamMetadataFormat, nativeStreamMetadataFormatName, + nativeStreamMetadataFormatClassName, extraStreamMetadataFormatNames, + extraStreamMetadataFormatClassNames, supportsStandardImageMetadataFormat, + nativeImageMetadataFormatName, nativeImageMetadataFormatClassName, + extraImageMetadataFormatNames, extraImageMetadataFormatClassNames); + + if (outputTypes == null || outputTypes.length == 0) { + throw new NullPointerException("output types array cannot be NULL or empty"); + } + + this.outputTypes = outputTypes; + this.readerSpiNames = readerSpiNames; + } + + /** + * Returns true if the format of the writer's output is lossless. The + * default implementation returns true. + * + * @return true, if a format is lossless, false otherwise. + */ + public boolean isFormatLossless() { + return true; + } + + /** + * Gets an array of Class objects whose types can be used as output for this + * writer. + * + * @return the output types. + */ + public Class[] getOutputTypes() { + return outputTypes; + } + + /** + * Checks whether or not the ImageWriter implementation associated with this + * service provider can encode an image with the specified type. + * + * @param type + * the ImageTypeSpecifier. + * @return true, if an image with the specified type can be encoded, false + * otherwise. + */ + public abstract boolean canEncodeImage(ImageTypeSpecifier type); + + /** + * Checks whether or not the ImageWriter implementation associated with this + * service provider can encode the specified RenderedImage. + * + * @param im + * the RenderedImage. + * @return true, if RenderedImage can be encoded, false otherwise. + */ + public boolean canEncodeImage(RenderedImage im) { + return canEncodeImage(ImageTypeSpecifier.createFromRenderedImage(im)); + } + + /** + * Returns an instance of the ImageWriter implementation for this service + * provider. + * + * @return the ImageWriter. + * @throws IOException + * if an I/O exception has occurred. + */ + public ImageWriter createWriterInstance() throws IOException { + return createWriterInstance(null); + } + + /** + * Returns an instance of the ImageWriter implementation for this service + * provider. + * + * @param extension + * the a plug-in specific extension object, or null. + * @return the ImageWriter. + * @throws IOException + * if an I/O exception has occurred. + */ + public abstract ImageWriter createWriterInstance(Object extension) throws IOException; + + /** + * Checks whether or not the specified ImageWriter object is an instance of + * the ImageWriter associated with this service provider or not. + * + * @param writer + * the ImageWriter. + * @return true, if the specified ImageWriter object is an instance of the + * ImageWriter associated with this service provider, false + * otherwise. + */ + public boolean isOwnWriter(ImageWriter writer) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets an array of strings with names of the ImageReaderSpi classes that + * support the internal metadata representation used by the ImageWriter of + * this service provider, or null if there are no such ImageReaders. + * + * @return the array of strings with names of the ImageWriterSpi classes. + */ + public String[] getImageReaderSpiNames() { + return readerSpiNames; + } +} diff --git a/awt/javax/imageio/spi/RegisterableService.java b/awt/javax/imageio/spi/RegisterableService.java new file mode 100644 index 000000000..ae2f4d39f --- /dev/null +++ b/awt/javax/imageio/spi/RegisterableService.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.spi; + +/** + * The RegisterableService interface provides service provider objects that can + * be registered by a ServiceRegistry, and notifications that registration and + * deregistration have been performed. + * + * @since Android 1.0 + */ +public interface RegisterableService { + + /** + * This method is called when the object which implements this interface is + * registered to the specified category of the specified registry. + * + * @param registry + * the ServiceRegistry to be registered. + * @param category + * the class representing a category. + */ + void onRegistration(ServiceRegistry registry, Class category); + + /** + * This method is called when the object which implements this interface is + * deregistered to the specified category of the specified registry. + * + * @param registry + * the ServiceRegistry to be registered. + * @param category + * the class representing a category. + */ + void onDeregistration(ServiceRegistry registry, Class category); +} diff --git a/awt/javax/imageio/spi/ServiceRegistry.java b/awt/javax/imageio/spi/ServiceRegistry.java new file mode 100644 index 000000000..79b02a366 --- /dev/null +++ b/awt/javax/imageio/spi/ServiceRegistry.java @@ -0,0 +1,552 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.spi; + +import java.util.*; +import java.util.Map.Entry; + +/** + * The ServiceRegistry class provides ability to register, deregister, look up + * and obtain service provider instances (SPIs). A service means a set of + * interfaces and classes, and a service provider is an implementation of a + * service. Service providers can be associated with one or more categories. + * Each category is defined by a class or interface. Only a single instance of a + * each class is allowed to be registered as a category. + * + * @since Android 1.0 + */ +public class ServiceRegistry { + + /** + * The categories. + */ + CategoriesMap categories = new CategoriesMap(this); + + /** + * Instantiates a new ServiceRegistry with the specified categories. + * + * @param categoriesIterator + * an Iterator of Class objects for defining of categories. + */ + public ServiceRegistry(Iterator> categoriesIterator) { + if (null == categoriesIterator) { + throw new IllegalArgumentException("categories iterator should not be NULL"); + } + while (categoriesIterator.hasNext()) { + Class c = categoriesIterator.next(); + categories.addCategory(c); + } + } + + /** + * Looks up and instantiates the available providers of this service using + * the specified class loader. + * + * @param providerClass + * the Class object of the provider to be looked up. + * @param loader + * the class loader to be used. + * @return the iterator of providers objects for this service. + */ + public static Iterator lookupProviders(Class providerClass, ClassLoader loader) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Looks up and instantiates the available providers of this service using + * the context class loader. + * + * @param providerClass + * the Class object of the provider to be looked up. + * @return the iterator of providers objects for this service. + */ + public static Iterator lookupProviders(Class providerClass) { + return lookupProviders(providerClass, Thread.currentThread().getContextClassLoader()); + } + + /** + * Registers the specified service provider object in the specified + * categories. + * + * @param provider + * the specified provider to be registered. + * @param category + * the category. + * @return true, if no provider of the same class is registered in this + * category, false otherwise. + */ + public boolean registerServiceProvider(T provider, Class category) { + return categories.addProvider(provider, category); + } + + /** + * Registers a list of service providers. + * + * @param providers + * the list of service providers. + */ + public void registerServiceProviders(Iterator providers) { + for (Iterator iterator = providers; iterator.hasNext();) { + categories.addProvider(iterator.next(), null); + } + } + + /** + * Registers the specified service provider object in all categories. + * + * @param provider + * the service provider. + */ + public void registerServiceProvider(Object provider) { + categories.addProvider(provider, null); + } + + /** + * Deregisters the specifies service provider from the specified category. + * + * @param provider + * the service provider to be deregistered. + * @param category + * the specified category. + * @return true, if the provider was already registered in the specified + * category, false otherwise. + */ + public boolean deregisterServiceProvider(T provider, Class category) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Deregisters the specified service provider from all categories. + * + * @param provider + * the specified service provider. + */ + public void deregisterServiceProvider(Object provider) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets an Iterator of registered service providers in the specified + * category which satisfy the specified Filter. The useOrdering parameter + * indicates whether the iterator will return all of the server provider + * objects in a set order. + * + * @param category + * the specified category. + * @param filter + * the specified filter. + * @param useOrdering + * the flag indicating that providers are ordered in the returned + * Iterator. + * @return the iterator of registered service providers. + */ + @SuppressWarnings("unchecked") + public Iterator getServiceProviders(Class category, Filter filter, boolean useOrdering) { + return new FilteredIterator(filter, (Iterator)categories.getProviders(category, + useOrdering)); + } + + /** + * Gets an Iterator of all registered service providers in the specified + * category. The useOrdering parameter indicates whether the iterator will + * return all of the server provider objects in a set order. + * + * @param category + * the specified category. + * @param useOrdering + * the flag indicating that providers are ordered in the returned + * Iterator. + * @return the Iterator of service providers. + */ + @SuppressWarnings("unchecked") + public Iterator getServiceProviders(Class category, boolean useOrdering) { + return (Iterator)categories.getProviders(category, useOrdering); + } + + /** + * Gets the registered service provider object that has the specified class + * type. + * + * @param providerClass + * the specified provider class. + * @return the service provider object. + */ + public T getServiceProviderByClass(Class providerClass) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Sets an ordering between two service provider objects within the + * specified category. + * + * @param category + * the specified category. + * @param firstProvider + * the first provider. + * @param secondProvider + * the second provider. + * @return true, if a previously unset order was set. + */ + public boolean setOrdering(Class category, T firstProvider, T secondProvider) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Unsets an ordering between two service provider objects within the + * specified category. + * + * @param category + * the specified category. + * @param firstProvider + * the first provider. + * @param secondProvider + * the second provider. + * @return true, if a previously unset order was removed. + */ + public boolean unsetOrdering(Class category, T firstProvider, T secondProvider) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Deregisters all providers from the specified category. + * + * @param category + * the specified category. + */ + public void deregisterAll(Class category) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Deregister all providers from all categories. + */ + public void deregisterAll() { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Finalizes this object. + * + * @throws Throwable + * if an error occurs during finalization. + */ + @Override + public void finalize() throws Throwable { + // TODO uncomment when deregisterAll is implemented + // deregisterAll(); + } + + /** + * Checks whether the specified provider has been already registered. + * + * @param provider + * the provider to be checked. + * @return true, if the specified provider has been already registered, + * false otherwise. + */ + public boolean contains(Object provider) { + throw new UnsupportedOperationException("Not supported yet"); + } + + /** + * Gets an iterator of Class objects representing the current categories. + * + * @return the Iterator of Class objects. + */ + public Iterator> getCategories() { + return categories.list(); + } + + /** + * The ServiceRegistry.Filter interface is used by + * ServiceRegistry.getServiceProviders to filter providers according to the + * specified criterion. + * + * @since Android 1.0 + */ + public static interface Filter { + + /** + * Returns true if the specified provider satisfies the criterion of + * this Filter. + * + * @param provider + * the provider. + * @return true, if the specified provider satisfies the criterion of + * this Filter, false otherwise. + */ + boolean filter(Object provider); + } + + /** + * The Class CategoriesMap. + */ + private static class CategoriesMap { + + /** + * The categories. + */ + Map, ProvidersMap> categories = new HashMap, ProvidersMap>(); + + /** + * The registry. + */ + ServiceRegistry registry; + + /** + * Instantiates a new categories map. + * + * @param registry + * the registry. + */ + public CategoriesMap(ServiceRegistry registry) { + this.registry = registry; + } + + // -- TODO: useOrdering + /** + * Gets the providers. + * + * @param category + * the category. + * @param useOrdering + * the use ordering. + * @return the providers. + */ + Iterator getProviders(Class category, boolean useOrdering) { + ProvidersMap providers = categories.get(category); + if (null == providers) { + throw new IllegalArgumentException("Unknown category: " + category); + } + return providers.getProviders(useOrdering); + } + + /** + * List. + * + * @return the iterator< class>. + */ + Iterator> list() { + return categories.keySet().iterator(); + } + + /** + * Adds the category. + * + * @param category + * the category. + */ + void addCategory(Class category) { + categories.put(category, new ProvidersMap()); + } + + /** + * Adds a provider to the category. If category is + * null then the provider will be added to all categories + * which the provider is assignable from. + * + * @param provider + * provider to add. + * @param category + * category to add provider to. + * @return true, if there were such provider in some category. + */ + boolean addProvider(Object provider, Class category) { + if (provider == null) { + throw new IllegalArgumentException("provider should be != NULL"); + } + + boolean rt; + if (category == null) { + rt = findAndAdd(provider); + } else { + rt = addToNamed(provider, category); + } + + if (provider instanceof RegisterableService) { + ((RegisterableService)provider).onRegistration(registry, category); + } + + return rt; + } + + /** + * Adds the to named. + * + * @param provider + * the provider. + * @param category + * the category. + * @return true, if successful. + */ + private boolean addToNamed(Object provider, Class category) { + Object obj = categories.get(category); + + if (null == obj) { + throw new IllegalArgumentException("Unknown category: " + category); + } + + return ((ProvidersMap)obj).addProvider(provider); + } + + /** + * Find and add. + * + * @param provider + * the provider. + * @return true, if successful. + */ + private boolean findAndAdd(Object provider) { + boolean rt = false; + for (Entry, ProvidersMap> e : categories.entrySet()) { + if (e.getKey().isAssignableFrom(provider.getClass())) { + rt |= e.getValue().addProvider(provider); + } + } + return rt; + } + } + + /** + * The Class ProvidersMap. + */ + private static class ProvidersMap { + // -- TODO: providers ordering support + + /** + * The providers. + */ + Map, Object> providers = new HashMap, Object>(); + + /** + * Adds the provider. + * + * @param provider + * the provider. + * @return true, if successful. + */ + boolean addProvider(Object provider) { + return providers.put(provider.getClass(), provider) != null; + } + + /** + * Gets the provider classes. + * + * @return the provider classes. + */ + Iterator> getProviderClasses() { + return providers.keySet().iterator(); + } + + // -- TODO ordering + /** + * Gets the providers. + * + * @param userOrdering + * the user ordering. + * @return the providers. + */ + Iterator getProviders(boolean userOrdering) { + return providers.values().iterator(); + } + } + + /** + * The Class FilteredIterator. + */ + private static class FilteredIterator implements Iterator { + + /** + * The filter. + */ + private Filter filter; + + /** + * The backend. + */ + private Iterator backend; + + /** + * The next obj. + */ + private E nextObj; + + /** + * Instantiates a new filtered iterator. + * + * @param filter + * the filter. + * @param backend + * the backend. + */ + public FilteredIterator(Filter filter, Iterator backend) { + this.filter = filter; + this.backend = backend; + findNext(); + } + + /** + * Next. + * + * @return the e. + */ + public E next() { + if (nextObj == null) { + throw new NoSuchElementException(); + } + E tmp = nextObj; + findNext(); + return tmp; + } + + /** + * Checks for next. + * + * @return true, if successful. + */ + public boolean hasNext() { + return nextObj != null; + } + + /** + * Removes the. + */ + public void remove() { + throw new UnsupportedOperationException(); + } + + /** + * Sets nextObj to a next provider matching the criterion given by the + * filter. + */ + private void findNext() { + nextObj = null; + while (backend.hasNext()) { + E o = backend.next(); + if (filter.filter(o)) { + nextObj = o; + return; + } + } + } + } +} diff --git a/awt/javax/imageio/spi/package.html b/awt/javax/imageio/spi/package.html new file mode 100644 index 000000000..18ceff486 --- /dev/null +++ b/awt/javax/imageio/spi/package.html @@ -0,0 +1,8 @@ + + +

    + This package provides several Service Provider Interface (SPI) classes for readers, writers, transcoders and streams to handle images. +

    + @since Android 1.0 + + diff --git a/awt/javax/imageio/stream/FileCacheImageInputStream.java b/awt/javax/imageio/stream/FileCacheImageInputStream.java new file mode 100644 index 000000000..710ac6660 --- /dev/null +++ b/awt/javax/imageio/stream/FileCacheImageInputStream.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.stream; + +import java.io.*; + +/** + * The FileCacheImageInputStream class is an implementation of ImageInputStream + * which reads from its InputStream and uses a temporary file as a cache. + * + * @since Android 1.0 + */ +public class FileCacheImageInputStream extends ImageInputStreamImpl { + + /** + * The is. + */ + private InputStream is; + + /** + * The file. + */ + private File file; + + /** + * The raf. + */ + private RandomAccessFile raf; + + /** + * Instantiates a new FileCacheImageInputStream from the specified + * InputStream and using the specified File as its cache directory. + * + * @param stream + * the InputStream for reading. + * @param cacheDir + * the cache directory where the cache file will be created. + * @throws IOException + * if an I/O exception has occurred. + */ + public FileCacheImageInputStream(InputStream stream, File cacheDir) throws IOException { + if (stream == null) { + throw new IllegalArgumentException("stream == null!"); + } + is = stream; + + if (cacheDir == null || cacheDir.isDirectory()) { + file = File.createTempFile(FileCacheImageOutputStream.IIO_TEMP_FILE_PREFIX, null, + cacheDir); + file.deleteOnExit(); + } else { + throw new IllegalArgumentException("Not a directory!"); + } + + raf = new RandomAccessFile(file, "rw"); + } + + @Override + public int read() throws IOException { + bitOffset = 0; + + if (streamPos >= raf.length()) { + int b = is.read(); + + if (b < 0) { + return -1; + } + + raf.seek(streamPos++); + raf.write(b); + return b; + } + + raf.seek(streamPos++); + return raf.read(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + bitOffset = 0; + + if (streamPos >= raf.length()) { + int nBytes = is.read(b, off, len); + + if (nBytes < 0) { + return -1; + } + + raf.seek(streamPos); + raf.write(b, off, nBytes); + streamPos += nBytes; + return nBytes; + } + + raf.seek(streamPos); + int nBytes = raf.read(b, off, len); + streamPos += nBytes; + return nBytes; + } + + @Override + public boolean isCached() { + return true; + } + + @Override + public boolean isCachedFile() { + return true; + } + + @Override + public boolean isCachedMemory() { + return false; + } + + @Override + public void close() throws IOException { + super.close(); + raf.close(); + file.delete(); + } +} diff --git a/awt/javax/imageio/stream/FileCacheImageOutputStream.java b/awt/javax/imageio/stream/FileCacheImageOutputStream.java new file mode 100644 index 000000000..135afab37 --- /dev/null +++ b/awt/javax/imageio/stream/FileCacheImageOutputStream.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.stream; + +import java.io.IOException; +import java.io.File; +import java.io.OutputStream; +import java.io.RandomAccessFile; + +/** + * The FileCacheImageOutputStream class is an implementation of + * ImageOutputStream that writes to its OutputStream using a temporary file as a + * cache. + * + * @since Android 1.0 + */ +public class FileCacheImageOutputStream extends ImageOutputStreamImpl { + + /** + * The Constant IIO_TEMP_FILE_PREFIX. + */ + static final String IIO_TEMP_FILE_PREFIX = "iioCache"; + + /** + * The Constant MAX_BUFFER_LEN. + */ + static final int MAX_BUFFER_LEN = 1048575; // 1 MB - is it not too much? + + /** + * The os. + */ + private OutputStream os; + + /** + * The file. + */ + private File file; + + /** + * The raf. + */ + private RandomAccessFile raf; + + /** + * Instantiates a FileCacheImageOutputStream. + * + * @param stream + * the OutputStream for writing. + * @param cacheDir + * the cache directory where the cache file will be created. + * @throws IOException + * if an I/O exception has occurred. + */ + public FileCacheImageOutputStream(OutputStream stream, File cacheDir) throws IOException { + if (stream == null) { + throw new IllegalArgumentException("stream == null!"); + } + os = stream; + + if (cacheDir == null || cacheDir.isDirectory()) { + file = File.createTempFile(IIO_TEMP_FILE_PREFIX, null, cacheDir); + file.deleteOnExit(); + } else { + throw new IllegalArgumentException("Not a directory!"); + } + + raf = new RandomAccessFile(file, "rw"); + } + + @Override + public void close() throws IOException { + flushBefore(raf.length()); + super.close(); + raf.close(); + file.delete(); + } + + @Override + public boolean isCached() { + return true; + } + + @Override + public boolean isCachedFile() { + return true; + } + + @Override + public boolean isCachedMemory() { + return false; + } + + @Override + public void write(int b) throws IOException { + flushBits(); // See the flushBits method description + + raf.write(b); + streamPos++; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + flushBits(); // See the flushBits method description + + raf.write(b, off, len); + streamPos += len; + } + + @Override + public int read() throws IOException { + bitOffset = 0; // Should reset + + int res = raf.read(); + if (res >= 0) { + streamPos++; + } + + return res; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + bitOffset = 0; + + int numRead = raf.read(b, off, len); + if (numRead > 0) { + streamPos += numRead; + } + + return numRead; + } + + @Override + public void flushBefore(long pos) throws IOException { + long readFromPos = flushedPos; + super.flushBefore(pos); + + long bytesToRead = pos - readFromPos; + raf.seek(readFromPos); + + if (bytesToRead < MAX_BUFFER_LEN) { + byte buffer[] = new byte[(int)bytesToRead]; + raf.readFully(buffer); + os.write(buffer); + } else { + byte buffer[] = new byte[MAX_BUFFER_LEN]; + while (bytesToRead > 0) { + int count = (int)Math.min(MAX_BUFFER_LEN, bytesToRead); + raf.readFully(buffer, 0, count); + os.write(buffer, 0, count); + bytesToRead -= count; + } + } + + os.flush(); + + if (pos != streamPos) { + raf.seek(streamPos); // Reset the position + } + } + + @Override + public void seek(long pos) throws IOException { + if (pos < flushedPos) { + throw new IndexOutOfBoundsException(); + } + + raf.seek(pos); + streamPos = raf.getFilePointer(); + bitOffset = 0; + } + + @Override + public long length() { + try { + return raf.length(); + } catch (IOException e) { + return -1L; + } + } +} diff --git a/awt/javax/imageio/stream/FileImageInputStream.java b/awt/javax/imageio/stream/FileImageInputStream.java new file mode 100644 index 000000000..b9b6002b3 --- /dev/null +++ b/awt/javax/imageio/stream/FileImageInputStream.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.stream; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.File; +import java.io.FileNotFoundException; + +/** + * The FileImageInputStream class implements ImageInputStream and obtains its + * input data from a File or RandomAccessFile. + * + * @since Android 1.0 + */ +public class FileImageInputStream extends ImageInputStreamImpl { + + /** + * The raf. + */ + RandomAccessFile raf; + + /** + * Instantiates a new FileImageInputStream from the specified File. + * + * @param f + * the File of input data. + * @throws FileNotFoundException + * if the specified file doesn't exist. + * @throws IOException + * if an I/O exception has occurred. + */ + @SuppressWarnings( { + "DuplicateThrows" + }) + public FileImageInputStream(File f) throws FileNotFoundException, IOException { + if (f == null) { + throw new IllegalArgumentException("f == null!"); + } + + raf = new RandomAccessFile(f, "r"); + } + + /** + * Instantiates a new FileImageInputStream from the specified + * RandomAccessFile. + * + * @param raf + * the RandomAccessFile of input data. + */ + public FileImageInputStream(RandomAccessFile raf) { + if (raf == null) { + throw new IllegalArgumentException("raf == null!"); + } + + this.raf = raf; + } + + @Override + public int read() throws IOException { + bitOffset = 0; + + int res = raf.read(); + if (res != -1) { + streamPos++; + } + return res; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + bitOffset = 0; + + int numRead = raf.read(b, off, len); + if (numRead >= 0) { + streamPos += numRead; + } + + return numRead; + } + + @Override + public long length() { + try { + return raf.length(); + } catch (IOException e) { + return -1L; + } + } + + @Override + public void seek(long pos) throws IOException { + if (pos < getFlushedPosition()) { + throw new IndexOutOfBoundsException(); + } + + raf.seek(pos); + streamPos = raf.getFilePointer(); + bitOffset = 0; + } + + @Override + public void close() throws IOException { + super.close(); + raf.close(); + } +} diff --git a/awt/javax/imageio/stream/FileImageOutputStream.java b/awt/javax/imageio/stream/FileImageOutputStream.java new file mode 100644 index 000000000..2730ba6a9 --- /dev/null +++ b/awt/javax/imageio/stream/FileImageOutputStream.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.stream; + +import java.io.*; + +/** + * The FileImageOutputStream class implements ImageOutputStream and writes the + * output data to a File or RandomAccessFile. + * + * @since Android 1.0 + */ +public class FileImageOutputStream extends ImageOutputStreamImpl { + + /** + * The file. + */ + RandomAccessFile file; + + /** + * Instantiates a new FileImageOutputStream with the specified File. + * + * @param f + * the output File. + * @throws FileNotFoundException + * if the file not found. + * @throws IOException + * if an I/O exception has occurred. + */ + public FileImageOutputStream(File f) throws FileNotFoundException, IOException { + this(f != null ? new RandomAccessFile(f, "rw") : null); + } + + /** + * Instantiates a new FileImageOutputStream with the specified + * RandomAccessFile. + * + * @param raf + * the output RandomAccessFile. + */ + public FileImageOutputStream(RandomAccessFile raf) { + if (raf == null) { + throw new IllegalArgumentException("file should not be NULL"); + } + file = raf; + } + + @Override + public void write(int b) throws IOException { + checkClosed(); + // according to the spec for ImageOutputStreamImpl#flushBits() + flushBits(); + file.write(b); + streamPos++; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + checkClosed(); + // according to the spec for ImageOutputStreamImpl#flushBits() + flushBits(); + file.write(b, off, len); + streamPos += len; + } + + @Override + public int read() throws IOException { + checkClosed(); + int rt = file.read(); + if (rt != -1) { + streamPos++; + } + return rt; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + checkClosed(); + int rt = file.read(b, off, len); + if (rt != -1) { + streamPos += rt; + } + return rt; + } + + @Override + public long length() { + try { + checkClosed(); + return file.length(); + } catch (IOException e) { + return super.length(); // -1L + } + } + + @Override + public void seek(long pos) throws IOException { + // -- checkClosed() is performed in super.seek() + super.seek(pos); + file.seek(pos); + streamPos = file.getFilePointer(); + } + + @Override + public void close() throws IOException { + super.close(); + file.close(); + } +} diff --git a/awt/javax/imageio/stream/IIOByteBuffer.java b/awt/javax/imageio/stream/IIOByteBuffer.java new file mode 100644 index 000000000..867d80843 --- /dev/null +++ b/awt/javax/imageio/stream/IIOByteBuffer.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Sergey I. Salishev + * @version $Revision: 1.2 $ + */ + +package javax.imageio.stream; + +// +// @author Sergey I. Salishev +// @version $Revision: 1.2 $ +// + +/** + * The IIOByteBuffer class represents a byte array with offset and length that + * is used by ImageInputStream for obtaining a sequence of bytes. + * + * @since Android 1.0 + */ +public class IIOByteBuffer { + + /** + * The data. + */ + private byte[] data; + + /** + * The offset. + */ + private int offset; + + /** + * The length. + */ + private int length; + + /** + * Instantiates a new IIOByteBuffer. + * + * @param data + * the byte array. + * @param offset + * the offset in the array. + * @param length + * the length of array. + */ + public IIOByteBuffer(byte[] data, int offset, int length) { + this.data = data; + this.offset = offset; + this.length = length; + } + + /** + * Gets the byte array of this IIOByteBuffer. + * + * @return the byte array. + */ + public byte[] getData() { + return data; + } + + /** + * Gets the length in the array which will be used. + * + * @return the length of the data. + */ + public int getLength() { + return length; + } + + /** + * Gets the offset of this IIOByteBuffer. + * + * @return the offset of this IIOByteBuffer. + */ + public int getOffset() { + return offset; + } + + /** + * Sets the new data array to this IIOByteBuffer object. + * + * @param data + * the new data array. + */ + public void setData(byte[] data) { + this.data = data; + } + + /** + * Sets the length of data which will be used. + * + * @param length + * the new length. + */ + public void setLength(int length) { + this.length = length; + } + + /** + * Sets the offset in the data array of this IIOByteBuffer. + * + * @param offset + * the new offset. + */ + public void setOffset(int offset) { + this.offset = offset; + } +} diff --git a/awt/javax/imageio/stream/ImageInputStream.java b/awt/javax/imageio/stream/ImageInputStream.java new file mode 100644 index 000000000..3dec5d296 --- /dev/null +++ b/awt/javax/imageio/stream/ImageInputStream.java @@ -0,0 +1,502 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.2 $ + */ + +package javax.imageio.stream; + +import java.io.DataInput; +import java.io.IOException; +import java.nio.ByteOrder; + +/** + * The ImageInputStream represents input stream interface that is used by + * ImageReaders. + * + * @since Android 1.0 + */ +public interface ImageInputStream extends DataInput { + + /** + * Sets the specified byte order for reading of data values from this + * stream. + * + * @param byteOrder + * the byte order. + */ + void setByteOrder(ByteOrder byteOrder); + + /** + * Gets the byte order. + * + * @return the byte order. + */ + ByteOrder getByteOrder(); + + /** + * Reads a byte from the stream. + * + * @return the byte of the stream, or -1 for EOF indicating. + * @throws IOException + * if an I/O exception has occurred. + */ + int read() throws IOException; + + /** + * Reads number of bytes which is equal to the specified array's length and + * stores a result to this array. + * + * @param b + * the byte array. + * @return the number of read bytes, or -1 indicated EOF. + * @throws IOException + * if an I/O exception has occurred. + */ + int read(byte[] b) throws IOException; + + /** + * Reads the number of bytes specified by len parameter from the stream and + * stores a result to the specified array with the specified offset. + * + * @param b + * the byte array. + * @param off + * the offset. + * @param len + * the number of bytes to be read. + * @return the number of read bytes, or -1 indicated EOF. + * @throws IOException + * if an I/O exception has occurred. + */ + int read(byte[] b, int off, int len) throws IOException; + + /** + * Reads the number of bytes specified by len parameter from the stream, and + * modifies the specified IIOByteBuffer with the byte array, offset, and + * length. + * + * @param buf + * the IIOByteBuffer. + * @param len + * the number of bytes to be read. + * @throws IOException + * if an I/O exception has occurred. + */ + void readBytes(IIOByteBuffer buf, int len) throws IOException; + + /** + * Reads a byte from the stream and returns a boolean true value if it is + * non zero, false if it is zero. + * + * @return the boolean value for read byte. + * @throws IOException + * if an I/O exception has occurred. + */ + boolean readBoolean() throws IOException; + + /** + * Reads a byte from the stream and returns its value as signed byte. + * + * @return the signed byte value for read byte. + * @throws IOException + * if an I/O exception has occurred. + */ + byte readByte() throws IOException; + + /** + * Reads a byte from the stream and returns its value as an integer. + * + * @return the unsigned byte value for read byte as an integer. + * @throws IOException + * if an I/O exception has occurred. + */ + int readUnsignedByte() throws IOException; + + /** + * Reads 2 bytes from the stream, and returns the result as a short. + * + * @return the signed short value from the stream. + * @throws IOException + * if an I/O exception has occurred. + */ + short readShort() throws IOException; + + /** + * Reads 2 bytes from the stream and returns its value as an unsigned short. + * + * @return a unsigned short value coded in an integer. + * @throws IOException + * if an I/O exception has occurred. + */ + int readUnsignedShort() throws IOException; + + /** + * Reads 2 bytes from the stream and returns their unsigned char value. + * + * @return the unsigned char value. + * @throws IOException + * if an I/O exception has occurred. + */ + char readChar() throws IOException; + + /** + * Reads 4 bytes from the stream, and returns the result as an integer. + * + * @return the signed integer value from the stream. + * @throws IOException + * if an I/O exception has occurred. + */ + int readInt() throws IOException; + + /** + * Reads 4 bytes from the stream and returns its value as long. + * + * @return the unsigned integer value as long. + * @throws IOException + * if an I/O exception has occurred. + */ + long readUnsignedInt() throws IOException; + + /** + * Reads 8 bytes from the stream, and returns the result as a long. + * + * @return the long value from the stream. + * @throws IOException + * if an I/O exception has occurred. + */ + long readLong() throws IOException; + + /** + * Reads 4 bytes from the stream, and returns the result as a float. + * + * @return the float value from the stream. + * @throws IOException + * if an I/O exception has occurred. + */ + float readFloat() throws IOException; + + /** + * Reads 8 bytes from the stream, and returns the result as a double. + * + * @return the double value from the stream. + * @throws IOException + * if an I/O exception has occurred. + */ + double readDouble() throws IOException; + + /** + * Reads a line from the stream. + * + * @return the string contained the line from the stream. + * @throws IOException + * if an I/O exception has occurred. + */ + String readLine() throws IOException; + + /** + * Reads bytes from the stream in a string that has been encoded in a + * modified UTF-8 format. + * + * @return the string read from stream and modified UTF-8 format. + * @throws IOException + * if an I/O exception has occurred. + */ + String readUTF() throws IOException; + + /** + * Reads the specified number of bytes from the stream, and stores the + * result into the specified array starting at the specified index offset. + * + * @param b + * the byte array. + * @param off + * the offset. + * @param len + * the number of bytes to be read. + * @throws IOException + * if an I/O exception has occurred. + */ + void readFully(byte[] b, int off, int len) throws IOException; + + /** + * Reads number of bytes from the stream which is equal to the specified + * array's length, and stores them into this array. + * + * @param b + * the byte array. + * @throws IOException + * if an I/O exception has occurred. + */ + void readFully(byte[] b) throws IOException; + + /** + * Reads the specified number of shorts from the stream, and stores the + * result into the specified array starting at the specified index offset. + * + * @param s + * the short array. + * @param off + * the offset. + * @param len + * the number of shorts to be read. + * @throws IOException + * if an I/O exception has occurred. + */ + void readFully(short[] s, int off, int len) throws IOException; + + /** + * Reads the specified number of chars from the stream, and stores the + * result into the specified array starting at the specified index offset. + * + * @param c + * the char array. + * @param off + * the offset. + * @param len + * the number of chars to be read. + * @throws IOException + * if an I/O exception has occurred. + */ + void readFully(char[] c, int off, int len) throws IOException; + + /** + * Reads the specified number of integer from the stream, and stores the + * result into the specified array starting at the specified index offset. + * + * @param i + * the integer array. + * @param off + * the offset. + * @param len + * the number of integer to be read. + * @throws IOException + * if an I/O exception has occurred. + */ + void readFully(int[] i, int off, int len) throws IOException; + + /** + * Reads the specified number of longs from the stream, and stores the + * result into the specified array starting at the specified index offset. + * + * @param l + * the long array. + * @param off + * the offset. + * @param len + * the number of longs to be read. + * @throws IOException + * if an I/O exception has occurred. + */ + void readFully(long[] l, int off, int len) throws IOException; + + /** + * Reads the specified number of floats from the stream, and stores the + * result into the specified array starting at the specified index offset. + * + * @param f + * the float array. + * @param off + * the offset. + * @param len + * the number of floats to be read. + * @throws IOException + * if an I/O exception has occurred. + */ + void readFully(float[] f, int off, int len) throws IOException; + + /** + * Reads the specified number of doubles from the stream, and stores the + * result into the specified array starting at the specified index offset. + * + * @param d + * the double array. + * @param off + * the offset. + * @param len + * the number of doubles to be read. + * @throws IOException + * if an I/O exception has occurred. + */ + void readFully(double[] d, int off, int len) throws IOException; + + /** + * Gets the stream position. + * + * @return the stream position. + * @throws IOException + * if an I/O exception has occurred. + */ + long getStreamPosition() throws IOException; + + /** + * Gets the bit offset. + * + * @return the bit offset. + * @throws IOException + * if an I/O exception has occurred. + */ + int getBitOffset() throws IOException; + + /** + * Sets the bit offset to an integer between 0 and 7. + * + * @param bitOffset + * the bit offset. + * @throws IOException + * if an I/O exception has occurred. + */ + void setBitOffset(int bitOffset) throws IOException; + + /** + * Reads a bit from the stream and returns the value 0 or 1. + * + * @return the value of single bit: 0 or 1. + * @throws IOException + * if an I/O exception has occurred. + */ + int readBit() throws IOException; + + /** + * Read the specified number of bits and returns their values as long. + * + * @param numBits + * the number of bits to be read. + * @return the bit string as a long. + * @throws IOException + * if an I/O exception has occurred. + */ + long readBits(int numBits) throws IOException; + + /** + * Returns the length of the stream. + * + * @return the length of the stream, or -1 if unknown. + * @throws IOException + * if an I/O exception has occurred. + */ + long length() throws IOException; + + /** + * Skips the specified number of bytes by moving stream position. + * + * @param n + * the number of bytes. + * @return the actual skipped number of bytes. + * @throws IOException + * if an I/O exception has occurred. + */ + int skipBytes(int n) throws IOException; + + /** + * Skips the specified number of bytes by moving stream position. + * + * @param n + * the number of bytes. + * @return the actual skipped number of bytes. + * @throws IOException + * if an I/O exception has occurred. + */ + long skipBytes(long n) throws IOException; + + /** + * Sets the current stream position to the specified location. + * + * @param pos + * a file pointer position. + * @throws IOException + * if an I/O exception has occurred. + */ + void seek(long pos) throws IOException; + + /** + * Marks a position in the stream to be returned to by a subsequent call to + * reset. + */ + void mark(); + + /** + * Returns the file pointer to its previous position. + * + * @throws IOException + * if an I/O exception has occurred. + */ + void reset() throws IOException; + + /** + * Flushes the initial position in this stream prior to the specified stream + * position. + * + * @param pos + * the position. + * @throws IOException + * if an I/O exception has occurred. + */ + void flushBefore(long pos) throws IOException; + + /** + * Flushes the initial position in this stream prior to the current stream + * position. + * + * @throws IOException + * if an I/O exception has occurred. + */ + void flush() throws IOException; + + /** + * Gets the flushed position. + * + * @return the flushed position. + */ + long getFlushedPosition(); + + /** + * Returns true if this ImageInputStream caches data in order to allow + * seeking backwards. + * + * @return true, if this ImageInputStream caches data in order to allow + * seeking backwards, false otherwise. + */ + boolean isCached(); + + /** + * Returns true if this ImageInputStream caches data in order to allow + * seeking backwards, and keeps it in memory. + * + * @return true, if this ImageInputStream caches data in order to allow + * seeking backwards, and keeps it in memory. + */ + boolean isCachedMemory(); + + /** + * Returns true if this ImageInputStream caches data in order to allow + * seeking backwards, and keeps it in a temporary file. + * + * @return true, if this ImageInputStream caches data in order to allow + * seeking backwards, and keeps it in a temporary file. + */ + boolean isCachedFile(); + + /** + * Closes this stream. + * + * @throws IOException + * if an I/O exception has occurred. + */ + void close() throws IOException; +} diff --git a/awt/javax/imageio/stream/ImageInputStreamImpl.java b/awt/javax/imageio/stream/ImageInputStreamImpl.java new file mode 100644 index 000000000..d79da4185 --- /dev/null +++ b/awt/javax/imageio/stream/ImageInputStreamImpl.java @@ -0,0 +1,418 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.stream; + +import java.io.EOFException; +import java.io.IOException; +import java.nio.ByteOrder; + +/** + * The ImageInputStreamImpl abstract class implements the ImageInputStream + * interface. + * + * @since Android 1.0 + */ +public abstract class ImageInputStreamImpl implements ImageInputStream { + + /** + * The byte order. + */ + protected ByteOrder byteOrder = ByteOrder.BIG_ENDIAN; + + /** + * The stream position. + */ + protected long streamPos = 0; + + /** + * The flushed position. + */ + protected long flushedPos = 0; + + /** + * The bit offset. + */ + protected int bitOffset = 0; + + /** + * The closed. + */ + private boolean closed = false; + + /** + * The position stack. + */ + private final PositionStack posStack = new PositionStack(); + + /** + * Instantiates a new ImageInputStreamImpl. + */ + public ImageInputStreamImpl() { + } + + /** + * Check if the stream is closed and if true, throws an IOException. + * + * @throws IOException + * if the stream is closed. + */ + protected final void checkClosed() throws IOException { + if (closed) { + throw new IOException("stream is closed"); + } + } + + public void setByteOrder(ByteOrder byteOrder) { + this.byteOrder = byteOrder; + } + + public ByteOrder getByteOrder() { + return byteOrder; + } + + public abstract int read() throws IOException; + + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + public abstract int read(byte[] b, int off, int len) throws IOException; + + public void readBytes(IIOByteBuffer buf, int len) throws IOException { + if (buf == null) { + throw new NullPointerException("buffer is NULL"); + } + + byte[] b = new byte[len]; + len = read(b, 0, b.length); + + buf.setData(b); + buf.setOffset(0); + buf.setLength(len); + } + + public boolean readBoolean() throws IOException { + int b = read(); + if (b < 0) { + throw new EOFException("EOF reached"); + } + return b != 0; + } + + public byte readByte() throws IOException { + int b = read(); + if (b < 0) { + throw new EOFException("EOF reached"); + } + return (byte)b; + } + + public int readUnsignedByte() throws IOException { + int b = read(); + if (b < 0) { + throw new EOFException("EOF reached"); + } + return b; + } + + public short readShort() throws IOException { + int b1 = read(); + int b2 = read(); + + if (b1 < 0 || b2 < 0) { + throw new EOFException("EOF reached"); + } + + return byteOrder == ByteOrder.BIG_ENDIAN ? (short)((b1 << 8) | (b2 & 0xff)) + : (short)((b2 << 8) | (b1 & 0xff)); + } + + public int readUnsignedShort() throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public char readChar() throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public int readInt() throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public long readUnsignedInt() throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public long readLong() throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public float readFloat() throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public double readDouble() throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public String readLine() throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public String readUTF() throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void readFully(byte[] b, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void readFully(byte[] b) throws IOException { + readFully(b, 0, b.length); + } + + public void readFully(short[] s, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void readFully(char[] c, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void readFully(int[] i, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void readFully(long[] l, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void readFully(float[] f, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void readFully(double[] d, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public long getStreamPosition() throws IOException { + checkClosed(); + return streamPos; + } + + public int getBitOffset() throws IOException { + checkClosed(); + return bitOffset; + } + + public void setBitOffset(int bitOffset) throws IOException { + checkClosed(); + this.bitOffset = bitOffset; + } + + public int readBit() throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public long readBits(int numBits) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public long length() { + return -1L; + } + + public int skipBytes(int n) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public long skipBytes(long n) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void seek(long pos) throws IOException { + checkClosed(); + if (pos < getFlushedPosition()) { + throw new IllegalArgumentException("trying to seek before flushed pos"); + } + bitOffset = 0; + streamPos = pos; + } + + public void mark() { + try { + posStack.push(getStreamPosition()); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException("Stream marking error"); + } + } + + public void reset() throws IOException { + // -- TODO bit pos + if (!posStack.isEmpty()) { + long p = posStack.pop(); + if (p < flushedPos) { + throw new IOException("marked position lies in the flushed portion of the stream"); + } + seek(p); + } + } + + public void flushBefore(long pos) throws IOException { + if (pos > getStreamPosition()) { + throw new IndexOutOfBoundsException("Trying to flush outside of current position"); + } + if (pos < flushedPos) { + throw new IndexOutOfBoundsException("Trying to flush within already flushed portion"); + } + flushedPos = pos; + // -- TODO implement + } + + public void flush() throws IOException { + flushBefore(getStreamPosition()); + } + + public long getFlushedPosition() { + return flushedPos; + } + + public boolean isCached() { + return false; // def + } + + public boolean isCachedMemory() { + return false; // def + } + + public boolean isCachedFile() { + return false; // def + } + + public void close() throws IOException { + checkClosed(); + closed = true; + + } + + /** + * Finalizes this object. + * + * @throws Throwable + * if an error occurs. + */ + @Override + protected void finalize() throws Throwable { + if (!closed) { + try { + close(); + } finally { + super.finalize(); + } + } + } + + /** + * The Class PositionStack. + */ + private static class PositionStack { + + /** + * The Constant SIZE. + */ + private static final int SIZE = 10; + + /** + * The values. + */ + private long[] values = new long[SIZE]; + + /** + * The pos. + */ + private int pos = 0; + + /** + * Push. + * + * @param v + * the v. + */ + void push(long v) { + if (pos >= values.length) { + ensure(pos + 1); + } + values[pos++] = v; + } + + /** + * Pop. + * + * @return the long. + */ + long pop() { + return values[--pos]; + } + + /** + * Checks if is empty. + * + * @return true, if is empty. + */ + boolean isEmpty() { + return pos == 0; + } + + /** + * Ensure. + * + * @param size + * the size. + */ + private void ensure(int size) { + long[] arr = new long[Math.max(2 * values.length, size)]; + System.arraycopy(values, 0, arr, 0, values.length); + values = arr; + } + } +} diff --git a/awt/javax/imageio/stream/ImageOutputStream.java b/awt/javax/imageio/stream/ImageOutputStream.java new file mode 100644 index 000000000..28ec93277 --- /dev/null +++ b/awt/javax/imageio/stream/ImageOutputStream.java @@ -0,0 +1,307 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.2 $ + */ + +package javax.imageio.stream; + +import java.io.DataOutput; +import java.io.IOException; + +/** + * The ImageOutputStream represents output stream interface that is used by + * ImageWriters. + * + * @since Android 1.0 + */ +public interface ImageOutputStream extends DataOutput, ImageInputStream { + + /** + * Writes a single byte to the stream at the current position. + * + * @param b + * the integer value, of which the 8 lowest bits will be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void write(int b) throws IOException; + + /** + * Writes the bytes array to the stream. + * + * @param b + * the byte array to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void write(byte[] b) throws IOException; + + /** + * Writes a number of bytes from the specified byte array beginning from the + * specified offset. + * + * @param b + * the byte array. + * @param off + * the offset. + * @param len + * the number of bytes to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void write(byte[] b, int off, int len) throws IOException; + + /** + * Writes the specified boolean value to the stream, 1 if it is true, 0 if + * it is false. + * + * @param b + * the boolean value to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeBoolean(boolean b) throws IOException; + + /** + * Writes the 8 lowest bits of the specified integer value to the stream. + * + * @param b + * the specified integer value. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeByte(int b) throws IOException; + + /** + * Writes a short value to the output stream. + * + * @param v + * the short value to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeShort(int v) throws IOException; + + /** + * Writes the 16 lowest bits of the specified integer value to the stream. + * + * @param v + * the specified integer value. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeChar(int v) throws IOException; + + /** + * Writes an integer value to the output stream. + * + * @param v + * the integer value to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeInt(int v) throws IOException; + + /** + * Write long. + * + * @param v + * the long value. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeLong(long v) throws IOException; + + /** + * Writes a float value to the output stream. + * + * @param v + * the float which contains value to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeFloat(float v) throws IOException; + + /** + * Writes a double value to the output stream. + * + * @param v + * the double which contains value to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeDouble(double v) throws IOException; + + /** + * Writes the specified string to the stream. + * + * @param s + * the string to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeBytes(String s) throws IOException; + + /** + * Writes the specified String to the output stream. + * + * @param s + * the String to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeChars(String s) throws IOException; + + /** + * Writes 2 bytes to the output stream in the modified UTF-8 representation + * of every character of the specified string. + * + * @param s + * the specified string to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeUTF(String s) throws IOException; + + /** + * Flushes the initial position in this stream prior to the specified stream + * position. + * + * @param pos + * the position. + * @throws IOException + * if an I/O exception has occurred. + */ + void flushBefore(long pos) throws IOException; + + /** + * Writes a len number of short values from the specified array to the + * stream. + * + * @param s + * the shorts array to be written. + * @param off + * the offset in the char array. + * @param len + * the length of chars to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeShorts(short[] s, int off, int len) throws IOException; + + /** + * Writes a len number of chars to the stream. + * + * @param c + * the char array to be written. + * @param off + * the offset in the char array. + * @param len + * the length of chars to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeChars(char[] c, int off, int len) throws IOException; + + /** + * Writes a len number of integer values from the specified array to the + * stream. + * + * @param i + * the integer array to be written. + * @param off + * the offset in the char array. + * @param len + * the length of chars to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeInts(int[] i, int off, int len) throws IOException; + + /** + * Writes a len number of long values from the specified array to the + * stream. + * + * @param l + * the long array to be written. + * @param off + * the offset in the char array. + * @param len + * the length of chars to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeLongs(long[] l, int off, int len) throws IOException; + + /** + * Writes a len number of float values from the specified array to the + * stream. + * + * @param f + * the float array to be written. + * @param off + * the offset in the char array. + * @param len + * the length of chars to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeFloats(float[] f, int off, int len) throws IOException; + + /** + * Writes a len number of double values from the specified array to the + * stream. + * + * @param d + * the double array to be written. + * @param off + * the offset in the char array. + * @param len + * the length of chars to be written. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeDoubles(double[] d, int off, int len) throws IOException; + + /** + * Writes a single bit at the current position. + * + * @param bit + * the integer whose least significant bit is to be written to + * the stream. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeBit(int bit) throws IOException; + + /** + * Writes a sequence of bits beginning from the current position. + * + * @param bits + * the long value containing the bits to be written, starting + * with the bit in position numBits - 1 down to the least + * significant bit. + * @param numBits + * the number of significant bit, it can be between 0 and 64. + * @throws IOException + * if an I/O exception has occurred. + */ + void writeBits(long bits, int numBits) throws IOException; + +} diff --git a/awt/javax/imageio/stream/ImageOutputStreamImpl.java b/awt/javax/imageio/stream/ImageOutputStreamImpl.java new file mode 100644 index 000000000..0fef78f1d --- /dev/null +++ b/awt/javax/imageio/stream/ImageOutputStreamImpl.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +package javax.imageio.stream; + +import java.io.IOException; +import java.nio.ByteOrder; + +/* + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ + +/** + * The ImageOutputStreamImpl abstract class implements the ImageOutputStream + * interface. + * + * @since Android 1.0 + */ +public abstract class ImageOutputStreamImpl extends ImageInputStreamImpl implements + ImageOutputStream { + + /** + * Instantiates a new ImageOutputStreamImpl. + */ + public ImageOutputStreamImpl() { + } + + public abstract void write(int b) throws IOException; + + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + public abstract void write(byte[] b, int off, int len) throws IOException; + + public void writeBoolean(boolean v) throws IOException { + write(v ? 1 : 0); + } + + public void writeByte(int v) throws IOException { + write(v); + } + + public void writeShort(int v) throws IOException { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + + } else { + + } + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeChar(int v) throws IOException { + writeShort(v); + } + + public void writeInt(int v) throws IOException { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + + } else { + + } + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeLong(long v) throws IOException { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + + } else { + + } + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeFloat(float v) throws IOException { + writeInt(Float.floatToIntBits(v)); + } + + public void writeDouble(double v) throws IOException { + writeLong(Double.doubleToLongBits(v)); + } + + public void writeBytes(String s) throws IOException { + write(s.getBytes()); + } + + public void writeChars(String s) throws IOException { + char[] chs = s.toCharArray(); + writeChars(chs, 0, chs.length); + } + + public void writeUTF(String s) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeShorts(short[] s, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeChars(char[] c, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeInts(int[] i, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeLongs(long[] l, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeFloats(float[] f, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeDoubles(double[] d, int off, int len) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeBit(int bit) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + public void writeBits(long bits, int numBits) throws IOException { + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Flushes the bits. This method should be called in the write methods by + * subclasses. + * + * @throws IOException + * if an I/O exception has occurred. + */ + protected final void flushBits() throws IOException { + if (bitOffset == 0) { + return; + } + + // -- TODO implement + throw new UnsupportedOperationException("Not implemented yet"); + } +} diff --git a/awt/javax/imageio/stream/MemoryCacheImageInputStream.java b/awt/javax/imageio/stream/MemoryCacheImageInputStream.java new file mode 100644 index 000000000..d7fc79139 --- /dev/null +++ b/awt/javax/imageio/stream/MemoryCacheImageInputStream.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.stream; + +import org.apache.harmony.x.imageio.stream.RandomAccessMemoryCache; + +import java.io.IOException; +import java.io.InputStream; + +/** + * The MemoryCacheImageInputStream class implements ImageInputStream using a + * memory buffer for caching the data. + * + * @since Android 1.0 + */ +public class MemoryCacheImageInputStream extends ImageInputStreamImpl { + + /** + * The is. + */ + private InputStream is; + + /** + * The ramc. + */ + private RandomAccessMemoryCache ramc = new RandomAccessMemoryCache(); + + /** + * Instantiates a new MemoryCacheImageInputStream which reads from the + * specified InputStream. + * + * @param stream + * the InputStream to be read. + */ + public MemoryCacheImageInputStream(InputStream stream) { + if (stream == null) { + throw new IllegalArgumentException("stream == null!"); + } + is = stream; + } + + @Override + public int read() throws IOException { + bitOffset = 0; + + if (streamPos >= ramc.length()) { + int count = (int)(streamPos - ramc.length() + 1); + int bytesAppended = ramc.appendData(is, count); + + if (bytesAppended < count) { + return -1; + } + } + + int res = ramc.getData(streamPos); + if (res >= 0) { + streamPos++; + } + return res; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + bitOffset = 0; + + if (streamPos >= ramc.length()) { + int count = (int)(streamPos - ramc.length() + len); + ramc.appendData(is, count); + } + + int res = ramc.getData(b, off, len, streamPos); + if (res > 0) { + streamPos += res; + } + return res; + } + + @Override + public boolean isCached() { + return true; + } + + @Override + public boolean isCachedFile() { + return false; + } + + @Override + public boolean isCachedMemory() { + return true; + } + + @Override + public void close() throws IOException { + super.close(); + ramc.close(); + } + + @Override + public void flushBefore(long pos) throws IOException { + super.flushBefore(pos); + ramc.freeBefore(getFlushedPosition()); + } +} diff --git a/awt/javax/imageio/stream/MemoryCacheImageOutputStream.java b/awt/javax/imageio/stream/MemoryCacheImageOutputStream.java new file mode 100644 index 000000000..1df40a342 --- /dev/null +++ b/awt/javax/imageio/stream/MemoryCacheImageOutputStream.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 javax.imageio.stream; + +import org.apache.harmony.x.imageio.stream.RandomAccessMemoryCache; + +import java.io.OutputStream; +import java.io.IOException; + +/** + * The MemoryCacheImageOutputStream class implements ImageOutputStream using a + * memory buffer for caching the data. + * + * @since Android 1.0 + */ +public class MemoryCacheImageOutputStream extends ImageOutputStreamImpl { + + /** + * The os. + */ + OutputStream os; + + /** + * The ramc. + */ + RandomAccessMemoryCache ramc = new RandomAccessMemoryCache(); + + /** + * Instantiates a new MemoryCacheImageOutputStream which writes to the + * specified OutputStream. + * + * @param stream + * the OutputStream. + */ + public MemoryCacheImageOutputStream(OutputStream stream) { + if (stream == null) { + throw new IllegalArgumentException("stream == null!"); + } + os = stream; + } + + @Override + public void write(int b) throws IOException { + flushBits(); // See the flushBits method description + + ramc.putData(b, streamPos); + streamPos++; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + flushBits(); // See the flushBits method description + + ramc.putData(b, off, len, streamPos); + streamPos += len; + } + + @Override + public int read() throws IOException { + bitOffset = 0; + + int res = ramc.getData(streamPos); + if (res >= 0) { + streamPos++; + } + return res; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + bitOffset = 0; + + int res = ramc.getData(b, off, len, streamPos); + if (res > 0) { + streamPos += res; + } + return res; + } + + @Override + public long length() { + return ramc.length(); + } + + @Override + public boolean isCached() { + return true; + } + + @Override + public boolean isCachedMemory() { + return true; + } + + @Override + public boolean isCachedFile() { + return false; + } + + @Override + public void close() throws IOException { + flushBefore(length()); + super.close(); + ramc.close(); + } + + @Override + public void flushBefore(long pos) throws IOException { + long flushedPosition = getFlushedPosition(); + super.flushBefore(pos); + + long newFlushedPosition = getFlushedPosition(); + int nBytes = (int)(newFlushedPosition - flushedPosition); + + ramc.getData(os, nBytes, flushedPosition); + ramc.freeBefore(newFlushedPosition); + + os.flush(); + } +} diff --git a/awt/javax/imageio/stream/package.html b/awt/javax/imageio/stream/package.html new file mode 100644 index 000000000..6cf53c3c0 --- /dev/null +++ b/awt/javax/imageio/stream/package.html @@ -0,0 +1,8 @@ + + +

    + This package contains classes and interfaces for handling images with low-level I/O operations. +

    + @since Android 1.0 + + diff --git a/awt/org/apache/harmony/awt/ChoiceStyle.java b/awt/org/apache/harmony/awt/ChoiceStyle.java new file mode 100644 index 000000000..93b7aada2 --- /dev/null +++ b/awt/org/apache/harmony/awt/ChoiceStyle.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ +package org.apache.harmony.awt; + +/** + * ChoiceStyle. + * Is used to define custom choice properties: + * width and x screen coordinate of the list popup window. + */ +public interface ChoiceStyle { + + int getPopupX(int x, int width, int choiceWidth, int screenWidth); + int getPopupWidth(int choiceWidth); + +} diff --git a/awt/org/apache/harmony/awt/ClipRegion.java b/awt/org/apache/harmony/awt/ClipRegion.java new file mode 100644 index 000000000..c89a81dd1 --- /dev/null +++ b/awt/org/apache/harmony/awt/ClipRegion.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov, Anton Avtamonov + * @version $Revision$ + */ +package org.apache.harmony.awt; + +import java.awt.Component; +import java.awt.Rectangle; + +import org.apache.harmony.awt.gl.MultiRectArea; +import org.apache.harmony.awt.internal.nls.Messages; + +public class ClipRegion extends Rectangle { + private final MultiRectArea clip; + + public ClipRegion(final MultiRectArea clip) { + this.clip = new MultiRectArea(clip); + setBounds(clip.getBounds()); + } + + public MultiRectArea getClip() { + return clip; + } + + @Override + public String toString() { + String str = clip.toString(); + int i = str.indexOf('['); + str = str.substring(i); + if (clip.getRectCount() == 1) { + str = str.substring(1, str.length() - 1); + } + return getClass().getName() + str; + } + + + public void convertRegion(final Component child, final Component parent) { + convertRegion(child, clip, parent); + } + + public void intersect(final Rectangle rect) { + clip.intersect(rect); + } + + @Override + public boolean isEmpty() { + return clip.isEmpty(); + } + + public static void convertRegion(final Component child, + final MultiRectArea region, + final Component parent) { + int x = 0, y = 0; + Component c = child; + //???AWT + /* + for (; c != null && c != parent; c = c.getParent()) { + x += c.getX(); + y += c.getY(); + } + */ + if (c == null) { + // awt.51=Component expected to be a parent + throw new IllegalArgumentException(Messages.getString("awt.51")); //$NON-NLS-1$ + } + region.translate(x, y); + } +} diff --git a/awt/org/apache/harmony/awt/ComponentInternals.java b/awt/org/apache/harmony/awt/ComponentInternals.java new file mode 100644 index 000000000..c3597846e --- /dev/null +++ b/awt/org/apache/harmony/awt/ComponentInternals.java @@ -0,0 +1,212 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt; + +//???AWT +//import java.awt.Component; +//import java.awt.Container; +//import java.awt.Dialog; +import java.awt.Dimension; +//import java.awt.Image; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +//import java.awt.Window; +//import java.awt.Choice; +import java.lang.reflect.InvocationTargetException; + +import org.apache.harmony.awt.gl.MultiRectArea; +//import org.apache.harmony.awt.text.TextFieldKit; +//import org.apache.harmony.awt.text.TextKit; +//import org.apache.harmony.awt.wtk.NativeWindow; + +import org.apache.harmony.luni.util.NotImplementedException; + +/** + * The accessor to AWT private API + */ +public abstract class ComponentInternals { + + /** + * @return the ComponentInternals instance to serve the requests + */ + public static ComponentInternals getComponentInternals() { + return ContextStorage.getComponentInternals(); + } + + /** + * This method must be called by AWT to establish the connection + * @param internals - implementation of ComponentInternals created by AWT + */ + public static void setComponentInternals(ComponentInternals internals) { + ContextStorage.setComponentInternals(internals); + } + + /** + * The accessor to native resource connected to a component. + * It returns non-null value only if component + * already has the native resource + */ + //public abstract NativeWindow getNativeWindow(Component component); + + /** + * Connect Window object to existing native resource + * @param nativeWindowId - id of native window to attach + * @return Window object with special behaviour that + * restricts manupulation with that window + */ + //public abstract Window attachNativeWindow(long nativeWindowId); + + /** + * Start mouse grab in "client" mode. + * All mouse events in AWT components will be reported as usual, + * mouse events that occured outside of AWT components will be sent to + * the window passed as grabWindow parameter. When mouse grab is canceled + * (because of click in non-AWT window or by task switching) + * the whenCanceled callback is called + * + * @param grabWindow - window that will own the grab + * @param whenCanceled - callback called when grab is canceled by user's action + */ + //public abstract void startMouseGrab(Window grabWindow, Runnable whenCanceled); + + /** + * End mouse grab and resume normal processing of mouse events + */ + //public abstract void endMouseGrab(); + + /** + * Set the popup flag of the window to true. + * This window won't be controlled by window manager on Linux. + * Call this method before the window is shown first time + * @param window - the window that should become popup one + */ + //public abstract void makePopup(Window window); + + /** + * This method must be called by Graphics at the beginning of drawImage() + * to store image drawing parameters (defined by application developer) in component + * + * @param comp - component that draws the image + * @param image - image to be drawn + * @param destLocation - location of the image upon the component's surface. Never null. + * @param destSize - size of the component's area to be filled with the image. + * Equals to null if size parameters omitted in drawImage. + * @param source - area of the image to be drawn on the component. + * Equals to null if src parameters omitted in drawImage. + */ + /* + public abstract void onDrawImage(Component comp, Image image, Point destLocation, + Dimension destSize, Rectangle source); +*/ + /** + * Sets system's caret position. + * This method should be called by text component to synchronize our caret position + * with system's caret position. + * @param x + * @param y + */ + //public abstract void setCaretPos(Component c, int x, int y); + + /** + * NEVER USE IT. FORGET IT. IT DOES NOT EXIST. + * See Toolkit.unsafeInvokeAndWait(Runnable). + * + * Accessor for Toolkit.unsafeInvokeAndWait(Runnable) method. + * For use in exceptional cases only. + * Read comments for Toolkit.unsafeInvokeAndWait(Runnable) before use. + */ + /* + public abstract void unsafeInvokeAndWait(Runnable runnable) + throws InterruptedException, InvocationTargetException; + + public abstract TextKit getTextKit(Component comp); + + public abstract void setTextKit(Component comp, TextKit kit); + + public abstract TextFieldKit getTextFieldKit(Component comp); + + public abstract void setTextFieldKit(Component comp, TextFieldKit kit); +*/ + /** + * Terminate event dispatch thread, completely destroy AWT context.
    + * Intended for multi-context mode, in single-context mode does nothing. + * + */ + public abstract void shutdown(); + + /** + * Sets mouse events preprocessor for event queue + */ + //public abstract void setMouseEventPreprocessor(MouseEventPreprocessor preprocessor); + + /** + * Create customized Choice using style + */ + //public abstract Choice createCustomChoice(ChoiceStyle style); + + //public abstract Insets getNativeInsets(Window w); + + /** + * Region to be repainted (could be null). Use this in overridden repaint() + */ + //public abstract MultiRectArea getRepaintRegion(Component c); + + //public abstract MultiRectArea subtractPendingRepaintRegion(Component c, MultiRectArea mra); + + /** + * Returns true if the window was at least once painted due to native paint events + */ + //public abstract boolean wasPainted(Window w); + + /** + * The component's region hidden behind top-level windows + * (belonging to both this Java app and all other apps), and behind + * heavyweight components overlapping with passed component + */ + //public abstract MultiRectArea getObscuredRegion(Component c); + + /** + * An accessor to Container.addObscuredRegions() method + * @see java.awt.Container#addObscuredRegions(MultiRectArea, Component) + */ + //public abstract void addObscuredRegions(MultiRectArea mra, Component c, Container container); + + /** + * Makes it possible to call protected Toolkit.setDesktopProperty() + * method from any class outside of java.awt package + */ + public abstract void setDesktopProperty(String name, Object value); + + /** + * Makes it possible to start/stop dialog modal loop + * from anywhere outside of java.awt package + */ + //public abstract void runModalLoop(Dialog dlg); + //public abstract void endModalLoop(Dialog dlg); + + /** + * Sets component's visible flag only + * (the component is not actually shown/hidden) + */ + //public abstract void setVisibleFlag(Component comp, boolean visible); + +} diff --git a/awt/org/apache/harmony/awt/ContextStorage.java b/awt/org/apache/harmony/awt/ContextStorage.java new file mode 100644 index 000000000..d44648aa3 --- /dev/null +++ b/awt/org/apache/harmony/awt/ContextStorage.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt; + +import java.awt.*; + +//???AWT +//import org.apache.harmony.awt.datatransfer.*; +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.awt.wtk.*; + + +public final class ContextStorage { + + private static volatile boolean multiContextMode = false; + private volatile boolean shutdownPending = false; + + private static final ContextStorage globalContext = new ContextStorage(); + + private Toolkit toolkit; + private ComponentInternals componentInternals; + //???AWT: private DTK dtk; + private WTK wtk; + private GraphicsEnvironment graphicsEnvironment; + + private class ContextLock {} + private final Object contextLock = new ContextLock(); + private final Synchronizer synchronizer = new Synchronizer(); + + public static void activateMultiContextMode() { + // TODO: checkPermission + multiContextMode = true; + } + + public static void setDefaultToolkit(Toolkit newToolkit) { + // TODO: checkPermission + getCurrentContext().toolkit = newToolkit; + } + + public static Toolkit getDefaultToolkit() { + return getCurrentContext().toolkit; + } + + //???AWT + /* + public static void setDTK(DTK dtk) { + // TODO: checkPermission + getCurrentContext().dtk = dtk; + } + + public static DTK getDTK() { + return getCurrentContext().dtk; + } + */ + + public static Synchronizer getSynchronizer() { + return getCurrentContext().synchronizer; + } + + public static ComponentInternals getComponentInternals() { + return getCurrentContext().componentInternals; + } + + static void setComponentInternals(ComponentInternals internals) { + // TODO: checkPermission + getCurrentContext().componentInternals = internals; + } + + public static Object getContextLock() { + return getCurrentContext().contextLock; + } + + public static WindowFactory getWindowFactory() { + return getCurrentContext().wtk.getWindowFactory(); + } + + public static void setWTK(WTK wtk) { + getCurrentContext().wtk = wtk; + } + + public static NativeIM getNativeIM() { + return getCurrentContext().wtk.getNativeIM(); + } + + public static NativeEventQueue getNativeEventQueue() { + return getCurrentContext().wtk.getNativeEventQueue(); + } + + public static GraphicsEnvironment getGraphicsEnvironment() { + return getCurrentContext().graphicsEnvironment; + } + + public static void setGraphicsEnvironment(GraphicsEnvironment environment) { + getCurrentContext().graphicsEnvironment = environment; + } + + private static ContextStorage getCurrentContext() { + return multiContextMode ? getContextThreadGroup().context : globalContext; + } + + private static ContextThreadGroup getContextThreadGroup() { + + Thread thread = Thread.currentThread(); + ThreadGroup group = thread.getThreadGroup(); + while (group != null) { + if (group instanceof ContextThreadGroup) { + return (ContextThreadGroup)group; + } + group = group.getParent(); + } + // awt.59=Application has run out of context thread group + throw new RuntimeException(Messages.getString("awt.59")); //$NON-NLS-1$ + } + + public static boolean shutdownPending() { + return getCurrentContext().shutdownPending; + } + + void shutdown() { + if (!multiContextMode) { + return; + } + shutdownPending = true; + + //???AWT: componentInternals.shutdown(); + + synchronized(contextLock) { + toolkit = null; + componentInternals = null; + //???AWT: dtk = null; + wtk = null; + graphicsEnvironment = null; + } + } + +} diff --git a/awt/org/apache/harmony/awt/ContextThreadGroup.java b/awt/org/apache/harmony/awt/ContextThreadGroup.java new file mode 100644 index 000000000..4f0af52fe --- /dev/null +++ b/awt/org/apache/harmony/awt/ContextThreadGroup.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt; + +public class ContextThreadGroup extends ThreadGroup { + + final ContextStorage context = new ContextStorage(); + + public ContextThreadGroup(String name) { + super(name); + } + + public void dispose() { + context.shutdown(); + } +} diff --git a/awt/org/apache/harmony/awt/ListenerList.java b/awt/org/apache/harmony/awt/ListenerList.java new file mode 100644 index 000000000..f5c55f10a --- /dev/null +++ b/awt/org/apache/harmony/awt/ListenerList.java @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.harmony.awt; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EventListener; +import java.util.Iterator; +import java.util.List; + +/** + * List of AWT listeners. It is for 3 purposes. + * 1. To support list modification from listeners + * 2. To ensure call for all listeners as atomic operation + * 3. To support system listeners that are needed for built-in AWT components + */ +public class ListenerList implements Serializable { + private static final long serialVersionUID = 9180703263299648154L; + + private transient ArrayList systemList; + private transient ArrayList userList; + + public ListenerList() { + super(); + } + + /** + * Adds system listener to this list. + * + * @param listener - listener to be added. + */ + public void addSystemListener(T listener) { + if (systemList == null) { + systemList = new ArrayList(); + } + systemList.add(listener); + } + + /** + * Adds user (public) listener to this list. + * + * @param listener - listener to be added. + */ + public void addUserListener(T listener) { + if (listener == null) { + return; + } + // transactionally replace old list + synchronized (this) { + if (userList == null) { + userList = new ArrayList(); + userList.add(listener); + return; + } + ArrayList newList = new ArrayList(userList); + newList.add(listener); + userList = newList; + } + } + + /** + * Removes user (public) listener to this list. + * + * @param listener - listener to be removed. + */ + public void removeUserListener(Object listener) { + if (listener == null) { + return; + } + // transactionally replace old list + synchronized (this) { + if (userList == null || !userList.contains(listener)) { + return; + } + ArrayList newList = new ArrayList(userList); + newList.remove(listener); + userList = (newList.size() > 0 ? newList : null); + } + } + + /** + * Gets all user (public) listeners in one array. + * + * @param emptyArray - empty array, it's for deriving particular listeners class. + * @return array of all user listeners. + */ + public AT[] getUserListeners(AT[] emptyArray){ + synchronized (this) { + return (userList != null ? userList.toArray(emptyArray) : emptyArray); + + } + } + + /** + * Gets all user (public) listeners in one list. + * + * @return list of all user listeners. + */ + public List getUserListeners() { + synchronized (this) { + if (userList == null || userList.isEmpty()) { + return Collections.emptyList(); + } + return new ArrayList(userList); + } + } + + public List getSystemListeners() { + synchronized (this) { + if (systemList == null || systemList.isEmpty()) { + return Collections.emptyList(); + } + return new ArrayList(systemList); + } + } + + /** + * Gets iterator for user listeners. + * + * @return iterator for user listeners. + */ + public Iterator getUserIterator() { + synchronized (this) { + if (userList == null) { + List emptyList = Collections.emptyList(); + return emptyList.iterator(); + } + return new ReadOnlyIterator(userList.iterator()); + } + } + + /** + * Gets iterator for system listeners. + * + * @return iterator for system listeners. + */ + public Iterator getSystemIterator() { + return systemList.iterator(); + } + + private static ArrayList getOnlySerializable(ArrayList list) { + if (list == null) { + return null; + } + + ArrayList result = new ArrayList(); + for (Iterator it = list.iterator(); it.hasNext();) { + Object obj = it.next(); + if (obj instanceof Serializable) { + result.add(obj); + } + } + + return (result.size() != 0) ? result : null; + } + + private void writeObject(ObjectOutputStream stream) throws IOException { + + stream.defaultWriteObject(); + + stream.writeObject(getOnlySerializable(systemList)); + stream.writeObject(getOnlySerializable(userList)); + } + + @SuppressWarnings("unchecked") + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + + stream.defaultReadObject(); + + systemList = (ArrayList)stream.readObject(); + userList = (ArrayList)stream.readObject(); + } + +} diff --git a/awt/org/apache/harmony/awt/ReadOnlyIterator.java b/awt/org/apache/harmony/awt/ReadOnlyIterator.java new file mode 100644 index 000000000..671653fb9 --- /dev/null +++ b/awt/org/apache/harmony/awt/ReadOnlyIterator.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt; + +import java.util.Iterator; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * ReadOnlyIterator + */ +public final class ReadOnlyIterator implements Iterator { + + private final Iterator it; + + public ReadOnlyIterator(Iterator it) { + if (it == null) { + throw new NullPointerException(); + } + this.it = it; + } + + public void remove() { + // awt.50=Iterator is read-only + throw new UnsupportedOperationException(Messages.getString("awt.50")); //$NON-NLS-1$ + } + + public boolean hasNext() { + return it.hasNext(); + } + + public E next() { + return it.next(); + } +} diff --git a/awt/org/apache/harmony/awt/gl/AwtImageBackdoorAccessor.java b/awt/org/apache/harmony/awt/gl/AwtImageBackdoorAccessor.java new file mode 100644 index 000000000..bd5f6c680 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/AwtImageBackdoorAccessor.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + * Created on 23.11.2005 + * + */ + + +package org.apache.harmony.awt.gl; + +import java.awt.Image; +import java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; +import java.awt.image.DataBufferInt; + +import org.apache.harmony.awt.gl.image.DataBufferListener; + +/** + * This class give an opportunity to get access to private data of + * some java.awt.image classes + * Implementation of this class placed in java.awt.image package + */ + +public abstract class AwtImageBackdoorAccessor { + + static protected AwtImageBackdoorAccessor inst; + + public static AwtImageBackdoorAccessor getInstance(){ + // First we need to run the static initializer in the DataBuffer class to resolve inst. + new DataBufferInt(0); + return inst; + } + + public abstract Surface getImageSurface(Image image); + public abstract boolean isGrayPallete(IndexColorModel icm); + + public abstract Object getData(DataBuffer db); + public abstract int[] getDataInt(DataBuffer db); + public abstract byte[] getDataByte(DataBuffer db); + public abstract short[] getDataShort(DataBuffer db); + public abstract short[] getDataUShort(DataBuffer db); + public abstract double[] getDataDouble(DataBuffer db); + public abstract float[] getDataFloat(DataBuffer db); + public abstract void releaseData(DataBuffer db); + + public abstract void addDataBufferListener(DataBuffer db, DataBufferListener listener); + public abstract void removeDataBufferListener(DataBuffer db); + public abstract void validate(DataBuffer db); +} diff --git a/awt/org/apache/harmony/awt/gl/CommonGraphics2D.java b/awt/org/apache/harmony/awt/gl/CommonGraphics2D.java new file mode 100644 index 000000000..a33c38b3d --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/CommonGraphics2D.java @@ -0,0 +1,1132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ +package org.apache.harmony.awt.gl; + + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Paint; +import java.awt.PaintContext; +import java.awt.Point; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.Toolkit; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.image.AffineTransformOp; +import java.awt.image.ImageObserver; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; +import java.awt.image.renderable.RenderableImage; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.PathIterator; +import java.awt.geom.RoundRectangle2D; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +import org.apache.harmony.awt.gl.Surface; +import org.apache.harmony.awt.gl.image.OffscreenImage; +import org.apache.harmony.awt.gl.render.Blitter; +import org.apache.harmony.awt.gl.render.JavaArcRasterizer; +import org.apache.harmony.awt.gl.render.JavaLineRasterizer; +import org.apache.harmony.awt.gl.render.JavaShapeRasterizer; +import org.apache.harmony.awt.gl.render.JavaTextRenderer; +import org.apache.harmony.awt.gl.render.NullBlitter; + +/* + * List of abstract methods to implement in subclusses + * Graphics.copyArea(int x, int y, int width, int height, int dx, int dy) + * Graphics.create() + * Graphics2D.getDeviceConfiguration() + * CommonGraphics2D.fillMultiRectAreaColor(MultiRectArea mra); + * CommonGraphics2D.fillMultiRectAreaPaint(MultiRectArea mra); + */ + +/** + * CommonGraphics2D class is a super class for all system-dependent + * implementations. It implements major part of Graphics and Graphics2D + * abstract methods. + *

    CommonGraphics2D Class Internals

    + *

    Line and Shape Rasterizers

    + *

    + * The CommonGraphics2D class splits all shapes into a set of rectangles + * to unify the drawing process for different operating systems and architectures. + * For this purpose Java 2D* uses the JavaShapeRasterizer and the JavaLineRasterizer + * classes from the org.apache.harmony.awt.gl.render package. The JavaShapeRasterizer + * class splits an object implementing a Shape interface into a set of rectangles and + * produces a MultiRectArea object. The JavaLineRasterizer class makes line drawing + * more accurate and processes lines with strokes, which are instances of the BasicStroke + * class. + *

    + *

    + * To port the shape drawing to another platform you just need to override + * rectangle-drawing methods. However, if your operating system has functions to draw + * particular shapes, you can optimize your subclass of the CommonGraphics2D class by + * using this functionality in overridden methods. + *

    + + *

    Blitters

    + *

    + * Blitter classes draw images on the display or buffered images. All blitters inherit + * the org.apache.harmony.awt.gl.render.Blitter interface. + *

    + *

    Blitters are divided into: + *

      + *
    • Native blitters for simple types of images, which the underlying native library + * can draw.
    • + *
    • Java* blitters for those types of images, which the underlying native library + * cannot handle.
    • + *

    + *

    + * DRL Java 2D* also uses blitters to fill the shapes and the user-defined subclasses + * of the java.awt.Paint class with paints, which the system does not support. + *

    + * + *

    Text Renderers

    + *

    + *Text renderers draw strings and glyph vectors. All text renderers are subclasses + *of the org.apache.harmony.awt.gl.TextRenderer class. + *

    + * + */ +public abstract class CommonGraphics2D extends Graphics2D { + protected Surface dstSurf = null; + protected Blitter blitter = NullBlitter.getInstance(); + protected RenderingHints hints = new RenderingHints(null); + + // Clipping things + protected MultiRectArea clip = null; + + protected Paint paint = Color.WHITE; + protected Color fgColor = Color.WHITE; + protected Color bgColor = Color.BLACK; + + protected Composite composite = AlphaComposite.SrcOver; + + protected Stroke stroke = new BasicStroke(); + + //TODO: Think more about FontRenderContext + protected FontRenderContext frc = new FontRenderContext(null, false, false); + + protected JavaShapeRasterizer jsr = new JavaShapeRasterizer(); + + protected Font font = new Font("Dialog", Font.PLAIN, 12);; //$NON-NLS-1$ + + protected TextRenderer jtr = JavaTextRenderer.inst; + + // Current graphics transform + protected AffineTransform transform = new AffineTransform(); + protected double[] matrix = new double[6]; + + // Original user->device translation as transform and point + //public AffineTransform origTransform = new AffineTransform(); + public Point origPoint = new Point(0, 0); + + + // Print debug output or not + protected static final boolean debugOutput = "1".equals(System.getProperty("g2d.debug")); //$NON-NLS-1$ //$NON-NLS-2$ + + // Constructors + protected CommonGraphics2D() { + } + + protected CommonGraphics2D(int tx, int ty) { + this(tx, ty, null); + } + + protected CommonGraphics2D(int tx, int ty, MultiRectArea clip) { + setTransform(AffineTransform.getTranslateInstance(tx, ty)); + //origTransform = AffineTransform.getTranslateInstance(tx, ty); + origPoint = new Point(tx, ty); + setClip(clip); + } + + // Public methods + @Override + public void addRenderingHints(Map hints) { + this.hints.putAll(hints); + } + + @Override + public void clearRect(int x, int y, int width, int height) { + Color c = getColor(); + Paint p = getPaint(); + setColor(getBackground()); + fillRect(x, y, width, height); + setColor(c); + setPaint(p); + if (debugOutput) { + System.err.println("CommonGraphics2D.clearRect("+x+", "+y+", "+width+", "+height+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + } + } + + @Override + public void clipRect(int x, int y, int width, int height) { + clip(new Rectangle(x, y, width, height)); + } + + + @Override + public void clip(Shape s) { + if (s == null) { + clip = null; + return; + } + + MultiRectArea mra = null; + if (s instanceof MultiRectArea) { + mra = new MultiRectArea((MultiRectArea)s); + mra.translate((int)transform.getTranslateX(), (int)transform.getTranslateY()); + } else { + int type = transform.getType(); + if(s instanceof Rectangle && (type & (AffineTransform.TYPE_IDENTITY | + AffineTransform.TYPE_TRANSLATION)) != 0){ + mra = new MultiRectArea((Rectangle)s); + if(type == AffineTransform.TYPE_TRANSLATION){ + mra.translate((int)transform.getTranslateX(), (int)transform.getTranslateY()); + } + } else { + s = transform.createTransformedShape(s); + mra = jsr.rasterize(s, 0.5); + } + } + + if (clip == null) { + setTransformedClip(mra); + } else { + clip.intersect(mra); + setTransformedClip(clip); + } + } + + @Override + public void dispose() { + // Do nothing for Java only classes + } + + + + + /*************************************************************************** + * + * Draw methods + * + ***************************************************************************/ + + @Override + public void draw(Shape s) { + if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1) { + //TODO: Think about drawing the shape in one fillMultiRectArea call + BasicStroke bstroke = (BasicStroke)stroke; + JavaLineRasterizer.LineDasher ld = (bstroke.getDashArray() == null)?null:new JavaLineRasterizer.LineDasher(bstroke.getDashArray(), bstroke.getDashPhase()); + PathIterator pi = s.getPathIterator(transform, 0.5); + float []points = new float[6]; + int x1 = Integer.MIN_VALUE; + int y1 = Integer.MIN_VALUE; + int cx1 = Integer.MIN_VALUE; + int cy1 = Integer.MIN_VALUE; + while (!pi.isDone()) { + switch (pi.currentSegment(points)) { + case PathIterator.SEG_MOVETO: + x1 = (int)Math.floor(points[0]); + y1 = (int)Math.floor(points[1]); + cx1 = x1; + cy1 = y1; + break; + case PathIterator.SEG_LINETO: + int x2 = (int)Math.floor(points[0]); + int y2 = (int)Math.floor(points[1]); + fillMultiRectArea(JavaLineRasterizer.rasterize(x1, y1, x2, y2, null, ld, false)); + x1 = x2; + y1 = y2; + break; + case PathIterator.SEG_CLOSE: + x2 = cx1; + y2 = cy1; + fillMultiRectArea(JavaLineRasterizer.rasterize(x1, y1, x2, y2, null, ld, false)); + x1 = x2; + y1 = y2; + break; + } + pi.next(); + } + } else { + s = stroke.createStrokedShape(s); + s = transform.createTransformedShape(s); + fillMultiRectArea(jsr.rasterize(s, 0.5)); + } + } + + @Override + public void drawArc(int x, int y, int width, int height, int sa, int ea) { + if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1 && + ((BasicStroke)stroke).getDashArray() == null && + (transform.isIdentity() || transform.getType() == AffineTransform.TYPE_TRANSLATION)) { + Point p = new Point(x, y); + transform.transform(p, p); + MultiRectArea mra = JavaArcRasterizer.rasterize(x, y, width, height, sa, ea, clip); + fillMultiRectArea(mra); + return; + } + draw(new Arc2D.Float(x, y, width, height, sa, ea, Arc2D.OPEN)); + } + + + @Override + public boolean drawImage(Image image, int x, int y, Color bgcolor, + ImageObserver imageObserver) { + + if(image == null) { + return true; + } + + boolean done = false; + boolean somebits = false; + Surface srcSurf = null; + if(image instanceof OffscreenImage){ + OffscreenImage oi = (OffscreenImage) image; + if((oi.getState() & ImageObserver.ERROR) != 0) { + return false; + } + done = oi.prepareImage(imageObserver); + somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; + srcSurf = oi.getImageSurface(); + }else{ + done = true; + srcSurf = Surface.getImageSurface(image); + } + + if(done || somebits) { + int w = srcSurf.getWidth(); + int h = srcSurf.getHeight(); + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, (AffineTransform) transform.clone(), + composite, bgcolor, clip); + } + return done; + } + + @Override + public boolean drawImage(Image image, int x, int y, ImageObserver imageObserver) { + return drawImage(image, x, y, null, imageObserver); + } + + @Override + public boolean drawImage(Image image, int x, int y, int width, int height, + Color bgcolor, ImageObserver imageObserver) { + + if(image == null) { + return true; + } + if(width == 0 || height == 0) { + return true; + } + + boolean done = false; + boolean somebits = false; + Surface srcSurf = null; + + if(image instanceof OffscreenImage){ + OffscreenImage oi = (OffscreenImage) image; + if((oi.getState() & ImageObserver.ERROR) != 0) { + return false; + } + done = oi.prepareImage(imageObserver); + somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; + srcSurf = oi.getImageSurface(); + }else{ + done = true; + srcSurf = Surface.getImageSurface(image); + } + + if(done || somebits) { + int w = srcSurf.getWidth(); + int h = srcSurf.getHeight(); + if(w == width && h == height){ + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, + (AffineTransform) transform.clone(), + composite, bgcolor, clip); + }else{ + AffineTransform xform = new AffineTransform(); + xform.setToScale((float)width / w, (float)height / h); + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, + (AffineTransform) transform.clone(), + xform, composite, bgcolor, clip); + } + } + return done; + } + + @Override + public boolean drawImage(Image image, int x, int y, int width, int height, + ImageObserver imageObserver) { + return drawImage(image, x, y, width, height, null, imageObserver); + } + + @Override + public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, Color bgcolor, + ImageObserver imageObserver) { + + if(image == null) { + return true; + } + if(dx1 == dx2 || dy1 == dy2 || sx1 == sx2 || sy1 == sy2) { + return true; + } + + boolean done = false; + boolean somebits = false; + Surface srcSurf = null; + if(image instanceof OffscreenImage){ + OffscreenImage oi = (OffscreenImage) image; + if((oi.getState() & ImageObserver.ERROR) != 0) { + return false; + } + done = oi.prepareImage(imageObserver); + somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; + srcSurf = oi.getImageSurface(); + }else{ + done = true; + srcSurf = Surface.getImageSurface(image); + } + + if(done || somebits) { + + int dstX = dx1; + int dstY = dy1; + int srcX = sx1; + int srcY = sy1; + + int dstW = dx2 - dx1; + int dstH = dy2 - dy1; + int srcW = sx2 - sx1; + int srcH = sy2 - sy1; + + if(srcW == dstW && srcH == dstH){ + blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH, + (AffineTransform) transform.clone(), + composite, bgcolor, clip); + }else{ + AffineTransform xform = new AffineTransform(); + xform.setToScale((float)dstW / srcW, (float)dstH / srcH); + blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH, + (AffineTransform) transform.clone(), + xform, composite, bgcolor, clip); + } + } + return done; + } + + @Override + public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, ImageObserver imageObserver) { + + return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, + imageObserver); + } + + @Override + public void drawImage(BufferedImage bufImage, BufferedImageOp op, + int x, int y) { + + if(bufImage == null) { + return; + } + + if(op == null) { + drawImage(bufImage, x, y, null); + } else if(op instanceof AffineTransformOp){ + AffineTransformOp atop = (AffineTransformOp) op; + AffineTransform xform = atop.getTransform(); + Surface srcSurf = Surface.getImageSurface(bufImage); + int w = srcSurf.getWidth(); + int h = srcSurf.getHeight(); + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, + (AffineTransform) transform.clone(), xform, + composite, null, clip); + } else { + bufImage = op.filter(bufImage, null); + Surface srcSurf = Surface.getImageSurface(bufImage); + int w = srcSurf.getWidth(); + int h = srcSurf.getHeight(); + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, + (AffineTransform) transform.clone(), + composite, null, clip); + } + } + + @Override + public boolean drawImage(Image image, AffineTransform trans, + ImageObserver imageObserver) { + + if(image == null) { + return true; + } + if(trans == null || trans.isIdentity()) { + return drawImage(image, 0, 0, imageObserver); + } + + boolean done = false; + boolean somebits = false; + Surface srcSurf = null; + if(image instanceof OffscreenImage){ + OffscreenImage oi = (OffscreenImage) image; + if((oi.getState() & ImageObserver.ERROR) != 0) { + return false; + } + done = oi.prepareImage(imageObserver); + somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; + srcSurf = oi.getImageSurface(); + }else{ + done = true; + srcSurf = Surface.getImageSurface(image); + } + + if(done || somebits) { + int w = srcSurf.getWidth(); + int h = srcSurf.getHeight(); + AffineTransform xform = (AffineTransform) transform.clone(); + xform.concatenate(trans); + blitter.blit(0, 0, srcSurf, 0, 0, dstSurf, w, h, xform, composite, + null, clip); + } + return done; + } + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + if (debugOutput) { + System.err.println("CommonGraphics2D.drawLine("+x1+", "+y1+", "+x2+", "+y2+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + } + + if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1) { + BasicStroke bstroke = (BasicStroke)stroke; + Point p1 = new Point(x1, y1); + Point p2 = new Point(x2, y2); + transform.transform(p1, p1); + transform.transform(p2, p2); + JavaLineRasterizer.LineDasher ld = (bstroke.getDashArray() == null)?null:new JavaLineRasterizer.LineDasher(bstroke.getDashArray(), bstroke.getDashPhase()); + MultiRectArea mra = JavaLineRasterizer.rasterize(p1.x, p1.y, p2.x, p2.y, null, ld, false); + fillMultiRectArea(mra); + return; + } + draw(new Line2D.Float(x1, y1, x2, y2)); + } + + @Override + public void drawOval(int x, int y, int width, int height) { + if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1 && + ((BasicStroke)stroke).getDashArray() == null && + (transform.isIdentity() || transform.getType() == AffineTransform.TYPE_TRANSLATION)) { + Point p = new Point(x, y); + transform.transform(p, p); + MultiRectArea mra = JavaArcRasterizer.rasterize(x, y, width, height, 0, 360, clip); + fillMultiRectArea(mra); + return; + } + draw(new Ellipse2D.Float(x, y, width, height)); + } + + @Override + public void drawPolygon(int[] xpoints, int[] ypoints, int npoints) { + draw(new Polygon(xpoints, ypoints, npoints)); + } + + @Override + public void drawPolygon(Polygon polygon) { + draw(polygon); + } + + @Override + public void drawPolyline(int[] xpoints, int[] ypoints, int npoints) { + for (int i = 0; i < npoints-1; i++) { + drawLine(xpoints[i], ypoints[i], xpoints[i+1], ypoints[i+1]); + } + } + + @Override + public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + if (img == null) { + return; + } + + double scaleX = xform.getScaleX(); + double scaleY = xform.getScaleY(); + if (scaleX == 1 && scaleY == 1) { + drawRenderedImage(img.createDefaultRendering(), xform); + } else { + int width = (int)Math.round(img.getWidth()*scaleX); + int height = (int)Math.round(img.getHeight()*scaleY); + xform = (AffineTransform)xform.clone(); + xform.scale(1, 1); + drawRenderedImage(img.createScaledRendering(width, height, null), xform); + } + } + + @Override + public void drawRenderedImage(RenderedImage rimg, AffineTransform xform) { + if (rimg == null) { + return; + } + + Image img = null; + + if (rimg instanceof Image) { + img = (Image)rimg; + } else { + //TODO: Create new class to provide Image interface for RenderedImage or rewrite this method + img = new BufferedImage(rimg.getColorModel(), rimg.copyData(null), false, null); + } + + drawImage(img, xform, null); + } + + @Override + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + if (debugOutput) { + System.err.println("CommonGraphics2D.drawRoundRect("+x+", "+y+", "+width+", "+height+","+arcWidth+", "+arcHeight+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ + } + + draw(new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight)); + } + + + + + + /*************************************************************************** + * + * String methods + * + ***************************************************************************/ + + @Override + public void drawString(AttributedCharacterIterator iterator, float x, float y) { + GlyphVector gv = font.createGlyphVector(frc, iterator); + drawGlyphVector(gv, x, y); + } + + @Override + public void drawString(AttributedCharacterIterator iterator, int x, int y) { + drawString(iterator, (float)x, (float)y); + } + + @Override + public void drawString(String str, int x, int y) { + drawString(str, (float)x, (float)y); + } + + @Override + public void drawString(String str, float x, float y) { + if (debugOutput) { + System.err.println("CommonGraphics2D.drawString("+str+", "+x+", "+y+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + AffineTransform at = (AffineTransform)this.getTransform().clone(); + AffineTransform fontTransform = font.getTransform(); + at.concatenate(fontTransform); + + double[] matrix = new double[6]; + if (!at.isIdentity()){ + + int atType = at.getType(); + at.getMatrix(matrix); + + // TYPE_TRANSLATION + if (atType == AffineTransform.TYPE_TRANSLATION){ + jtr.drawString(this, str, + (float)(x+fontTransform.getTranslateX()), + (float)(y+fontTransform.getTranslateY())); + return; + } + // TODO: we use slow type of drawing strings when Font object + // in Graphics has transforms, we just fill outlines. New textrenderer + // is to be implemented. + Shape sh = font.createGlyphVector(this.getFontRenderContext(), str).getOutline(x, y); + this.fill(sh); + + } else { + jtr.drawString(this, str, x, y); + } + + } + + @Override + public void drawGlyphVector(GlyphVector gv, float x, float y) { + + AffineTransform at = gv.getFont().getTransform(); + + double[] matrix = new double[6]; + if ((at != null) && (!at.isIdentity())){ + + int atType = at.getType(); + at.getMatrix(matrix); + + // TYPE_TRANSLATION + if ((atType == AffineTransform.TYPE_TRANSLATION) && + ((gv.getLayoutFlags() & GlyphVector.FLAG_HAS_TRANSFORMS) == 0)){ + jtr.drawGlyphVector(this, gv, (int)(x+matrix[4]), (int)(y+matrix[5])); + return; + } + } else { + if (((gv.getLayoutFlags() & GlyphVector.FLAG_HAS_TRANSFORMS) == 0)){ + jtr.drawGlyphVector(this, gv, x, y); + return; + } + } + + // TODO: we use slow type of drawing strings when Font object + // in Graphics has transforms, we just fill outlines. New textrenderer + // is to be implemented. + + Shape sh = gv.getOutline(x, y); + this.fill(sh); + + } + + + + + /*************************************************************************** + * + * Fill methods + * + ***************************************************************************/ + + @Override + public void fill(Shape s) { + s = transform.createTransformedShape(s); + MultiRectArea mra = jsr.rasterize(s, 0.5); + fillMultiRectArea(mra); + } + + @Override + public void fillArc(int x, int y, int width, int height, int sa, int ea) { + fill(new Arc2D.Float(x, y, width, height, sa, ea, Arc2D.PIE)); + } + + @Override + public void fillOval(int x, int y, int width, int height) { + fill(new Ellipse2D.Float(x, y, width, height)); + } + + @Override + public void fillPolygon(int[] xpoints, int[] ypoints, int npoints) { + fill(new Polygon(xpoints, ypoints, npoints)); + } + + @Override + public void fillPolygon(Polygon polygon) { + fill(polygon); + } + + @Override + public void fillRect(int x, int y, int width, int height) { + if (debugOutput) { + System.err.println("CommonGraphics2D.fillRect("+x+", "+y+", "+width+", "+height+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + } + + fill(new Rectangle(x, y, width, height)); + } + + @Override + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + if (debugOutput) { + System.err.println("CommonGraphics2D.fillRoundRect("+x+", "+y+", "+width+", "+height+","+arcWidth+", "+arcHeight+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ + } + + fill(new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight)); + } + + + + + /*************************************************************************** + * + * Get methods + * + ***************************************************************************/ + + @Override + public Color getBackground() { + return bgColor; + } + + @Override + public Shape getClip() { + if (clip == null) { + return null; + } + + MultiRectArea res = new MultiRectArea(clip); + res.translate(-Math.round((float)transform.getTranslateX()), -Math.round((float)transform.getTranslateY())); + return res; + } + + @Override + public Rectangle getClipBounds() { + if (clip == null) { + return null; + } + + Rectangle res = (Rectangle) clip.getBounds().clone(); + res.translate(-Math.round((float)transform.getTranslateX()), -Math.round((float)transform.getTranslateY())); + return res; + } + + @Override + public Color getColor() { + return fgColor; + } + + @Override + public Composite getComposite() { + return composite; + } + + @Override + public Font getFont() { + return font; + } + + @SuppressWarnings("deprecation") + @Override + public FontMetrics getFontMetrics(Font font) { + return Toolkit.getDefaultToolkit().getFontMetrics(font); + } + + @Override + public FontRenderContext getFontRenderContext() { + return frc; + } + + @Override + public Paint getPaint() { + return paint; + } + + @Override + public Object getRenderingHint(RenderingHints.Key key) { + return hints.get(key); + } + + @Override + public RenderingHints getRenderingHints() { + return hints; + } + + @Override + public Stroke getStroke() { + return stroke; + } + + @Override + public AffineTransform getTransform() { + return (AffineTransform)transform.clone(); + } + + @Override + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { + //TODO: Implement method.... + return false; + } + + + + + /*************************************************************************** + * + * Transformation methods + * + ***************************************************************************/ + + @Override + public void rotate(double theta) { + transform.rotate(theta); + transform.getMatrix(matrix); + } + + @Override + public void rotate(double theta, double x, double y) { + transform.rotate(theta, x, y); + transform.getMatrix(matrix); + } + + @Override + public void scale(double sx, double sy) { + transform.scale(sx, sy); + transform.getMatrix(matrix); + } + + @Override + public void shear(double shx, double shy) { + transform.shear(shx, shy); + transform.getMatrix(matrix); + } + + @Override + public void transform(AffineTransform at) { + transform.concatenate(at); + transform.getMatrix(matrix); + } + + @Override + public void translate(double tx, double ty) { + if (debugOutput) { + System.err.println("CommonGraphics2D.translate("+tx+", "+ty+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + transform.translate(tx, ty); + transform.getMatrix(matrix); + } + + @Override + public void translate(int tx, int ty) { + if (debugOutput) { + System.err.println("CommonGraphics2D.translate("+tx+", "+ty+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + transform.translate(tx, ty); + transform.getMatrix(matrix); + } + + + + + /*************************************************************************** + * + * Set methods + * + ***************************************************************************/ + + @Override + public void setBackground(Color color) { + bgColor = color; + } + + @Override + public void setClip(int x, int y, int width, int height) { + setClip(new Rectangle(x, y, width, height)); + } + + @Override + public void setClip(Shape s) { + if (s == null) { + setTransformedClip(null); + if (debugOutput) { + System.err.println("CommonGraphics2D.setClip(null)"); //$NON-NLS-1$ + } + return; + } + + if (debugOutput) { + System.err.println("CommonGraphics2D.setClip("+s.getBounds()+")"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (s instanceof MultiRectArea) { + MultiRectArea nclip = new MultiRectArea((MultiRectArea)s); + nclip.translate(Math.round((float)transform.getTranslateX()), Math.round((float)transform.getTranslateY())); + setTransformedClip(nclip); + } else { + int type = transform.getType(); + if(s instanceof Rectangle && (type & (AffineTransform.TYPE_IDENTITY | + AffineTransform.TYPE_TRANSLATION)) != 0){ + MultiRectArea nclip = new MultiRectArea((Rectangle)s); + if(type == AffineTransform.TYPE_TRANSLATION){ + nclip.translate((int)transform.getTranslateX(), (int)transform.getTranslateY()); + } + setTransformedClip(nclip); + } else { + s = transform.createTransformedShape(s); + setTransformedClip(jsr.rasterize(s, 0.5)); + } + } + } + + @Override + public void setColor(Color color) { + if (color != null) { + fgColor = color; + paint = color; + } + } + + @Override + public void setComposite(Composite composite) { + this.composite = composite; + } + + @Override + public void setFont(Font font) { + this.font = font; + } + + @Override + public void setPaint(Paint paint) { + if (paint == null) + return; + + this.paint = paint; + if (paint instanceof Color) { + fgColor = (Color)paint; + } + } + + @Override + public void setPaintMode() { + composite = AlphaComposite.SrcOver; + } + + @Override + public void setRenderingHint(RenderingHints.Key key, Object value) { + hints.put(key, value); + } + + @Override + public void setRenderingHints(Map hints) { + this.hints.clear(); + this.hints.putAll(hints); + } + + @Override + public void setStroke(Stroke stroke) { + this.stroke = stroke; + } + + @Override + public void setTransform(AffineTransform transform) { + this.transform = transform; + + transform.getMatrix(matrix); + } + + @Override + public void setXORMode(Color color) { + composite = new XORComposite(color); + } + + + // Protected methods + protected void setTransformedClip(MultiRectArea clip) { + this.clip = clip; + } + + /** + * This method fills the given MultiRectArea with current paint. + * It calls fillMultiRectAreaColor and fillMultiRectAreaPaint + * methods depending on the type of current paint. + * @param mra MultiRectArea to fill + */ + protected void fillMultiRectArea(MultiRectArea mra) { + if (clip != null) { + mra.intersect(clip); + } + + // Return if all stuff is clipped + if (mra.rect[0] < 5) { + return; + } + + if (debugOutput) { + System.err.println("CommonGraphics2D.fillMultiRectArea("+mra+")"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (paint instanceof Color){ + fillMultiRectAreaColor(mra); + }else{ + fillMultiRectAreaPaint(mra); + } + } + + /** + * This method fills the given MultiRectArea with solid color. + * @param mra MultiRectArea to fill + */ + protected void fillMultiRectAreaColor(MultiRectArea mra) { + fillMultiRectAreaPaint(mra); + } + + /** + * This method fills the given MultiRectArea with any paint. + * @param mra MultiRectArea to fill + */ + protected void fillMultiRectAreaPaint(MultiRectArea mra) { + Rectangle rec = mra.getBounds(); + int x = rec.x; + int y = rec.y; + int w = rec.width; + int h = rec.height; + if(w <= 0 || h <= 0) { + return; + } + PaintContext pc = paint.createContext(null, rec, rec, transform, hints); + Raster r = pc.getRaster(x, y, w, h); + WritableRaster wr; + if(r instanceof WritableRaster){ + wr = (WritableRaster) r; + }else{ + wr = r.createCompatibleWritableRaster(); + wr.setRect(r); + } + Surface srcSurf = new ImageSurface(pc.getColorModel(), wr); + blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, + composite, null, mra); + srcSurf.dispose(); + } + + /** + * Copies graphics class fields. + * Used in create method + * + * @param copy Graphics class to copy + */ + protected void copyInternalFields(CommonGraphics2D copy) { + if (clip == null) { + copy.setTransformedClip(null); + } else { + copy.setTransformedClip(new MultiRectArea(clip)); + } + copy.setBackground(bgColor); + copy.setColor(fgColor); + copy.setPaint(paint); + copy.setComposite(composite); + copy.setStroke(stroke); + copy.setFont(font); + copy.setTransform(new AffineTransform(transform)); + //copy.origTransform = new AffineTransform(origTransform); + copy.origPoint = new Point(origPoint); + } +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/gl/CommonGraphics2DFactory.java b/awt/org/apache/harmony/awt/gl/CommonGraphics2DFactory.java new file mode 100644 index 000000000..27e3ef078 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/CommonGraphics2DFactory.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko, Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.peer.FontPeer; + +import org.apache.harmony.awt.gl.font.FontMetricsImpl; +import org.apache.harmony.awt.wtk.GraphicsFactory; + +/** + * Common GraphicsFactory implementation + * + */ +public abstract class CommonGraphics2DFactory implements GraphicsFactory { + + // static instance of CommonGraphics2DFactory + public static CommonGraphics2DFactory inst; + + /** + * Returns FontMetrics object that keeps metrics of the specified font. + * + * @param font specified Font + * @return FontMetrics object corresponding to the specified Font object + */ + public FontMetrics getFontMetrics(Font font) { + FontMetrics fm; + for (FontMetrics element : cacheFM) { + fm = element; + if (fm == null){ + break; + } + + if (fm.getFont().equals(font)){ + return fm; + } + } + fm = new FontMetricsImpl(font); + + System.arraycopy(cacheFM, 0, cacheFM, 1, cacheFM.length -1); + cacheFM[0] = fm; + + return fm; + } + // Font methods + + public FontPeer getFontPeer(Font font) { + return getFontManager().getFontPeer(font.getName(), font.getStyle(), font.getSize()); + } + + /** + * Embeds font from gile with specified path into the system. + * + * @param fontFilePath path to the font file + * @return Font object that was created from the file. + */ + public abstract Font embedFont(String fontFilePath); + +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/gl/CommonGraphicsEnvironment.java b/awt/org/apache/harmony/awt/gl/CommonGraphicsEnvironment.java new file mode 100644 index 000000000..5c78e50d5 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/CommonGraphicsEnvironment.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko, Oleg V. Khaschansky + * @version $Revision$ + */ +package org.apache.harmony.awt.gl; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Locale; + +import org.apache.harmony.awt.gl.image.BufferedImageGraphics2D; + +/** + * Common GraphicsEnvironment implementation + * + */ +public abstract class CommonGraphicsEnvironment extends GraphicsEnvironment { + + @Override + public Graphics2D createGraphics(BufferedImage bufferedImage) { + return new BufferedImageGraphics2D(bufferedImage); + } + + @Override + public String[] getAvailableFontFamilyNames(Locale locale) { + Font[] fonts = getAllFonts(); + ArrayList familyNames = new ArrayList(); + + for (Font element : fonts) { + String name = element.getFamily(locale); + if (!familyNames.contains(name)) { + familyNames.add(name); + } + } + + return familyNames.toArray(new String[familyNames.size()]); + } + + @Override + public Font[] getAllFonts() { + return CommonGraphics2DFactory.inst.getFontManager().getAllFonts(); + } + + @Override + public String[] getAvailableFontFamilyNames() { + return CommonGraphics2DFactory.inst.getFontManager().getAllFamilies(); + } +} diff --git a/awt/org/apache/harmony/awt/gl/Crossing.java b/awt/org/apache/harmony/awt/gl/Crossing.java new file mode 100644 index 000000000..ae7fb0eb4 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/Crossing.java @@ -0,0 +1,889 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ +package org.apache.harmony.awt.gl; + +import java.awt.Shape; +import java.awt.geom.PathIterator; + +public class Crossing { + + /** + * Allowable tolerance for bounds comparison + */ + static final double DELTA = 1E-5; + + /** + * If roots have distance less then ROOT_DELTA they are double + */ + static final double ROOT_DELTA = 1E-10; + + /** + * Rectangle cross segment + */ + public static final int CROSSING = 255; + + /** + * Unknown crossing result + */ + static final int UNKNOWN = 254; + + /** + * Solves quadratic equation + * @param eqn - the coefficients of the equation + * @param res - the roots of the equation + * @return a number of roots + */ + public static int solveQuad(double eqn[], double res[]) { + double a = eqn[2]; + double b = eqn[1]; + double c = eqn[0]; + int rc = 0; + if (a == 0.0) { + if (b == 0.0) { + return -1; + } + res[rc++] = -c / b; + } else { + double d = b * b - 4.0 * a * c; + // d < 0.0 + if (d < 0.0) { + return 0; + } + d = Math.sqrt(d); + res[rc++] = (- b + d) / (a * 2.0); + // d != 0.0 + if (d != 0.0) { + res[rc++] = (- b - d) / (a * 2.0); + } + } + return fixRoots(res, rc); + } + + /** + * Solves cubic equation + * @param eqn - the coefficients of the equation + * @param res - the roots of the equation + * @return a number of roots + */ + public static int solveCubic(double eqn[], double res[]) { + double d = eqn[3]; + if (d == 0) { + return solveQuad(eqn, res); + } + double a = eqn[2] / d; + double b = eqn[1] / d; + double c = eqn[0] / d; + int rc = 0; + + double Q = (a * a - 3.0 * b) / 9.0; + double R = (2.0 * a * a * a - 9.0 * a * b + 27.0 * c) / 54.0; + double Q3 = Q * Q * Q; + double R2 = R * R; + double n = - a / 3.0; + + if (R2 < Q3) { + double t = Math.acos(R / Math.sqrt(Q3)) / 3.0; + double p = 2.0 * Math.PI / 3.0; + double m = -2.0 * Math.sqrt(Q); + res[rc++] = m * Math.cos(t) + n; + res[rc++] = m * Math.cos(t + p) + n; + res[rc++] = m * Math.cos(t - p) + n; + } else { +// Debug.println("R2 >= Q3 (" + R2 + "/" + Q3 + ")"); + double A = Math.pow(Math.abs(R) + Math.sqrt(R2 - Q3), 1.0 / 3.0); + if (R > 0.0) { + A = -A; + } +// if (A == 0.0) { + if (-ROOT_DELTA < A && A < ROOT_DELTA) { + res[rc++] = n; + } else { + double B = Q / A; + res[rc++] = A + B + n; +// if (R2 == Q3) { + double delta = R2 - Q3; + if (-ROOT_DELTA < delta && delta < ROOT_DELTA) { + res[rc++] = - (A + B) / 2.0 + n; + } + } + + } + return fixRoots(res, rc); + } + + /** + * Excludes double roots. Roots are double if they lies enough close with each other. + * @param res - the roots + * @param rc - the roots count + * @return new roots count + */ + static int fixRoots(double res[], int rc) { + int tc = 0; + for(int i = 0; i < rc; i++) { + out: { + for(int j = i + 1; j < rc; j++) { + if (isZero(res[i] - res[j])) { + break out; + } + } + res[tc++] = res[i]; + } + } + return tc; + } + + /** + * QuadCurve class provides basic functionality to find curve crossing and calculating bounds + */ + public static class QuadCurve { + + double ax, ay, bx, by; + double Ax, Ay, Bx, By; + + public QuadCurve(double x1, double y1, double cx, double cy, double x2, double y2) { + ax = x2 - x1; + ay = y2 - y1; + bx = cx - x1; + by = cy - y1; + + Bx = bx + bx; // Bx = 2.0 * bx + Ax = ax - Bx; // Ax = ax - 2.0 * bx + + By = by + by; // By = 2.0 * by + Ay = ay - By; // Ay = ay - 2.0 * by + } + + int cross(double res[], int rc, double py1, double py2) { + int cross = 0; + + for (int i = 0; i < rc; i++) { + double t = res[i]; + + // CURVE-OUTSIDE + if (t < -DELTA || t > 1 + DELTA) { + continue; + } + // CURVE-START + if (t < DELTA) { + if (py1 < 0.0 && (bx != 0.0 ? bx : ax - bx) < 0.0) { + cross--; + } + continue; + } + // CURVE-END + if (t > 1 - DELTA) { + if (py1 < ay && (ax != bx ? ax - bx : bx) > 0.0) { + cross++; + } + continue; + } + // CURVE-INSIDE + double ry = t * (t * Ay + By); + // ry = t * t * Ay + t * By + if (ry > py2) { + double rxt = t * Ax + bx; + // rxt = 2.0 * t * Ax + Bx = 2.0 * t * Ax + 2.0 * bx + if (rxt > -DELTA && rxt < DELTA) { + continue; + } + cross += rxt > 0.0 ? 1 : -1; + } + } // for + + return cross; + } + + int solvePoint(double res[], double px) { + double eqn[] = {-px, Bx, Ax}; + return solveQuad(eqn, res); + } + + int solveExtrem(double res[]) { + int rc = 0; + if (Ax != 0.0) { + res[rc++] = - Bx / (Ax + Ax); + } + if (Ay != 0.0) { + res[rc++] = - By / (Ay + Ay); + } + return rc; + } + + int addBound(double bound[], int bc, double res[], int rc, double minX, double maxX, boolean changeId, int id) { + for(int i = 0; i < rc; i++) { + double t = res[i]; + if (t > -DELTA && t < 1 + DELTA) { + double rx = t * (t * Ax + Bx); + if (minX <= rx && rx <= maxX) { + bound[bc++] = t; + bound[bc++] = rx; + bound[bc++] = t * (t * Ay + By); + bound[bc++] = id; + if (changeId) { + id++; + } + } + } + } + return bc; + } + + } + + /** + * CubicCurve class provides basic functionality to find curve crossing and calculating bounds + */ + public static class CubicCurve { + + double ax, ay, bx, by, cx, cy; + double Ax, Ay, Bx, By, Cx, Cy; + double Ax3, Bx2; + + public CubicCurve(double x1, double y1, double cx1, double cy1, double cx2, double cy2, double x2, double y2) { + ax = x2 - x1; + ay = y2 - y1; + bx = cx1 - x1; + by = cy1 - y1; + cx = cx2 - x1; + cy = cy2 - y1; + + Cx = bx + bx + bx; // Cx = 3.0 * bx + Bx = cx + cx + cx - Cx - Cx; // Bx = 3.0 * cx - 6.0 * bx + Ax = ax - Bx - Cx; // Ax = ax - 3.0 * cx + 3.0 * bx + + Cy = by + by + by; // Cy = 3.0 * by + By = cy + cy + cy - Cy - Cy; // By = 3.0 * cy - 6.0 * by + Ay = ay - By - Cy; // Ay = ay - 3.0 * cy + 3.0 * by + + Ax3 = Ax + Ax + Ax; + Bx2 = Bx + Bx; + } + + int cross(double res[], int rc, double py1, double py2) { + int cross = 0; + for (int i = 0; i < rc; i++) { + double t = res[i]; + + // CURVE-OUTSIDE + if (t < -DELTA || t > 1 + DELTA) { + continue; + } + // CURVE-START + if (t < DELTA) { + if (py1 < 0.0 && (bx != 0.0 ? bx : (cx != bx ? cx - bx : ax - cx)) < 0.0) { + cross--; + } + continue; + } + // CURVE-END + if (t > 1 - DELTA) { + if (py1 < ay && (ax != cx ? ax - cx : (cx != bx ? cx - bx : bx)) > 0.0) { + cross++; + } + continue; + } + // CURVE-INSIDE + double ry = t * (t * (t * Ay + By) + Cy); + // ry = t * t * t * Ay + t * t * By + t * Cy + if (ry > py2) { + double rxt = t * (t * Ax3 + Bx2) + Cx; + // rxt = 3.0 * t * t * Ax + 2.0 * t * Bx + Cx + if (rxt > -DELTA && rxt < DELTA) { + rxt = t * (Ax3 + Ax3) + Bx2; + // rxt = 6.0 * t * Ax + 2.0 * Bx + if (rxt < -DELTA || rxt > DELTA) { + // Inflection point + continue; + } + rxt = ax; + } + cross += rxt > 0.0 ? 1 : -1; + } + } //for + + return cross; + } + + int solvePoint(double res[], double px) { + double eqn[] = {-px, Cx, Bx, Ax}; + return solveCubic(eqn, res); + } + + int solveExtremX(double res[]) { + double eqn[] = {Cx, Bx2, Ax3}; + return solveQuad(eqn, res); + } + + int solveExtremY(double res[]) { + double eqn[] = {Cy, By + By, Ay + Ay + Ay}; + return solveQuad(eqn, res); + } + + int addBound(double bound[], int bc, double res[], int rc, double minX, double maxX, boolean changeId, int id) { + for(int i = 0; i < rc; i++) { + double t = res[i]; + if (t > -DELTA && t < 1 + DELTA) { + double rx = t * (t * (t * Ax + Bx) + Cx); + if (minX <= rx && rx <= maxX) { + bound[bc++] = t; + bound[bc++] = rx; + bound[bc++] = t * (t * (t * Ay + By) + Cy); + bound[bc++] = id; + if (changeId) { + id++; + } + } + } + } + return bc; + } + + } + + /** + * Returns how many times ray from point (x,y) cross line. + */ + public static int crossLine(double x1, double y1, double x2, double y2, double x, double y) { + + // LEFT/RIGHT/UP/EMPTY + if ((x < x1 && x < x2) || + (x > x1 && x > x2) || + (y > y1 && y > y2) || + (x1 == x2)) + { + return 0; + } + + // DOWN + if (y < y1 && y < y2) { + } else { + // INSIDE + if ((y2 - y1) * (x - x1) / (x2 - x1) <= y - y1) { + // INSIDE-UP + return 0; + } + } + + // START + if (x == x1) { + return x1 < x2 ? 0 : -1; + } + + // END + if (x == x2) { + return x1 < x2 ? 1 : 0; + } + + // INSIDE-DOWN + return x1 < x2 ? 1 : -1; + } + + /** + * Returns how many times ray from point (x,y) cross quard curve + */ + public static int crossQuad(double x1, double y1, double cx, double cy, double x2, double y2, double x, double y) { + + // LEFT/RIGHT/UP/EMPTY + if ((x < x1 && x < cx && x < x2) || + (x > x1 && x > cx && x > x2) || + (y > y1 && y > cy && y > y2) || + (x1 == cx && cx == x2)) + { + return 0; + } + + // DOWN + if (y < y1 && y < cy && y < y2 && x != x1 && x != x2) { + if (x1 < x2) { + return x1 < x && x < x2 ? 1 : 0; + } + return x2 < x && x < x1 ? -1 : 0; + } + + // INSIDE + QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2); + double px = x - x1; + double py = y - y1; + double res[] = new double[3]; + int rc = c.solvePoint(res, px); + + return c.cross(res, rc, py, py); + } + + /** + * Returns how many times ray from point (x,y) cross cubic curve + */ + public static int crossCubic(double x1, double y1, double cx1, double cy1, double cx2, double cy2, double x2, double y2, double x, double y) { + + // LEFT/RIGHT/UP/EMPTY + if ((x < x1 && x < cx1 && x < cx2 && x < x2) || + (x > x1 && x > cx1 && x > cx2 && x > x2) || + (y > y1 && y > cy1 && y > cy2 && y > y2) || + (x1 == cx1 && cx1 == cx2 && cx2 == x2)) + { + return 0; + } + + // DOWN + if (y < y1 && y < cy1 && y < cy2 && y < y2 && x != x1 && x != x2) { + if (x1 < x2) { + return x1 < x && x < x2 ? 1 : 0; + } + return x2 < x && x < x1 ? -1 : 0; + } + + // INSIDE + CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2); + double px = x - x1; + double py = y - y1; + double res[] = new double[3]; + int rc = c.solvePoint(res, px); + return c.cross(res, rc, py, py); + } + + /** + * Returns how many times ray from point (x,y) cross path + */ + public static int crossPath(PathIterator p, double x, double y) { + int cross = 0; + double mx, my, cx, cy; + mx = my = cx = cy = 0.0; + double coords[] = new double[6]; + + while (!p.isDone()) { + switch (p.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + if (cx != mx || cy != my) { + cross += crossLine(cx, cy, mx, my, x, y); + } + mx = cx = coords[0]; + my = cy = coords[1]; + break; + case PathIterator.SEG_LINETO: + cross += crossLine(cx, cy, cx = coords[0], cy = coords[1], x, y); + break; + case PathIterator.SEG_QUADTO: + cross += crossQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3], x, y); + break; + case PathIterator.SEG_CUBICTO: + cross += crossCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4], cy = coords[5], x, y); + break; + case PathIterator.SEG_CLOSE: + if (cy != my || cx != mx) { + cross += crossLine(cx, cy, cx = mx, cy = my, x, y); + } + break; + } + p.next(); + } + if (cy != my) { + cross += crossLine(cx, cy, mx, my, x, y); + } + return cross; + } + + /** + * Returns how many times ray from point (x,y) cross shape + */ + public static int crossShape(Shape s, double x, double y) { + if (!s.getBounds2D().contains(x, y)) { + return 0; + } + return crossPath(s.getPathIterator(null), x, y); + } + + /** + * Returns true if value enough small + */ + public static boolean isZero(double val) { + return -DELTA < val && val < DELTA; + } + + /** + * Sort bound array + */ + static void sortBound(double bound[], int bc) { + for(int i = 0; i < bc - 4; i += 4) { + int k = i; + for(int j = i + 4; j < bc; j += 4) { + if (bound[k] > bound[j]) { + k = j; + } + } + if (k != i) { + double tmp = bound[i]; + bound[i] = bound[k]; + bound[k] = tmp; + tmp = bound[i + 1]; + bound[i + 1] = bound[k + 1]; + bound[k + 1] = tmp; + tmp = bound[i + 2]; + bound[i + 2] = bound[k + 2]; + bound[k + 2] = tmp; + tmp = bound[i + 3]; + bound[i + 3] = bound[k + 3]; + bound[k + 3] = tmp; + } + } + } + + /** + * Returns are bounds intersect or not intersect rectangle + */ + static int crossBound(double bound[], int bc, double py1, double py2) { + + // LEFT/RIGHT + if (bc == 0) { + return 0; + } + + // Check Y coordinate + int up = 0; + int down = 0; + for(int i = 2; i < bc; i += 4) { + if (bound[i] < py1) { + up++; + continue; + } + if (bound[i] > py2) { + down++; + continue; + } + return CROSSING; + } + + // UP + if (down == 0) { + return 0; + } + + if (up != 0) { + // bc >= 2 + sortBound(bound, bc); + boolean sign = bound[2] > py2; + for(int i = 6; i < bc; i += 4) { + boolean sign2 = bound[i] > py2; + if (sign != sign2 && bound[i + 1] != bound[i - 3]) { + return CROSSING; + } + sign = sign2; + } + } + return UNKNOWN; + } + + /** + * Returns how many times rectangle stripe cross line or the are intersect + */ + public static int intersectLine(double x1, double y1, double x2, double y2, double rx1, double ry1, double rx2, double ry2) { + + // LEFT/RIGHT/UP + if ((rx2 < x1 && rx2 < x2) || + (rx1 > x1 && rx1 > x2) || + (ry1 > y1 && ry1 > y2)) + { + return 0; + } + + // DOWN + if (ry2 < y1 && ry2 < y2) { + } else { + + // INSIDE + if (x1 == x2) { + return CROSSING; + } + + // Build bound + double bx1, bx2; + if (x1 < x2) { + bx1 = x1 < rx1 ? rx1 : x1; + bx2 = x2 < rx2 ? x2 : rx2; + } else { + bx1 = x2 < rx1 ? rx1 : x2; + bx2 = x1 < rx2 ? x1 : rx2; + } + double k = (y2 - y1) / (x2 - x1); + double by1 = k * (bx1 - x1) + y1; + double by2 = k * (bx2 - x1) + y1; + + // BOUND-UP + if (by1 < ry1 && by2 < ry1) { + return 0; + } + + // BOUND-DOWN + if (by1 > ry2 && by2 > ry2) { + } else { + return CROSSING; + } + } + + // EMPTY + if (x1 == x2) { + return 0; + } + + // CURVE-START + if (rx1 == x1) { + return x1 < x2 ? 0 : -1; + } + + // CURVE-END + if (rx1 == x2) { + return x1 < x2 ? 1 : 0; + } + + if (x1 < x2) { + return x1 < rx1 && rx1 < x2 ? 1 : 0; + } + return x2 < rx1 && rx1 < x1 ? -1 : 0; + + } + + /** + * Returns how many times rectangle stripe cross quad curve or the are intersect + */ + public static int intersectQuad(double x1, double y1, double cx, double cy, double x2, double y2, double rx1, double ry1, double rx2, double ry2) { + + // LEFT/RIGHT/UP ------------------------------------------------------ + if ((rx2 < x1 && rx2 < cx && rx2 < x2) || + (rx1 > x1 && rx1 > cx && rx1 > x2) || + (ry1 > y1 && ry1 > cy && ry1 > y2)) + { + return 0; + } + + // DOWN --------------------------------------------------------------- + if (ry2 < y1 && ry2 < cy && ry2 < y2 && rx1 != x1 && rx1 != x2) { + if (x1 < x2) { + return x1 < rx1 && rx1 < x2 ? 1 : 0; + } + return x2 < rx1 && rx1 < x1 ? -1 : 0; + } + + // INSIDE ------------------------------------------------------------- + QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2); + double px1 = rx1 - x1; + double py1 = ry1 - y1; + double px2 = rx2 - x1; + double py2 = ry2 - y1; + + double res1[] = new double[3]; + double res2[] = new double[3]; + int rc1 = c.solvePoint(res1, px1); + int rc2 = c.solvePoint(res2, px2); + + // INSIDE-LEFT/RIGHT + if (rc1 == 0 && rc2 == 0) { + return 0; + } + + // Build bound -------------------------------------------------------- + double minX = px1 - DELTA; + double maxX = px2 + DELTA; + double bound[] = new double[28]; + int bc = 0; + // Add roots + bc = c.addBound(bound, bc, res1, rc1, minX, maxX, false, 0); + bc = c.addBound(bound, bc, res2, rc2, minX, maxX, false, 1); + // Add extremal points` + rc2 = c.solveExtrem(res2); + bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 2); + // Add start and end + if (rx1 < x1 && x1 < rx2) { + bound[bc++] = 0.0; + bound[bc++] = 0.0; + bound[bc++] = 0.0; + bound[bc++] = 4; + } + if (rx1 < x2 && x2 < rx2) { + bound[bc++] = 1.0; + bound[bc++] = c.ax; + bound[bc++] = c.ay; + bound[bc++] = 5; + } + // End build bound ---------------------------------------------------- + + int cross = crossBound(bound, bc, py1, py2); + if (cross != UNKNOWN) { + return cross; + } + return c.cross(res1, rc1, py1, py2); + } + + /** + * Returns how many times rectangle stripe cross cubic curve or the are intersect + */ + public static int intersectCubic(double x1, double y1, double cx1, double cy1, double cx2, double cy2, double x2, double y2, double rx1, double ry1, double rx2, double ry2) { + + // LEFT/RIGHT/UP + if ((rx2 < x1 && rx2 < cx1 && rx2 < cx2 && rx2 < x2) || + (rx1 > x1 && rx1 > cx1 && rx1 > cx2 && rx1 > x2) || + (ry1 > y1 && ry1 > cy1 && ry1 > cy2 && ry1 > y2)) + { + return 0; + } + + // DOWN + if (ry2 < y1 && ry2 < cy1 && ry2 < cy2 && ry2 < y2 && rx1 != x1 && rx1 != x2) { + if (x1 < x2) { + return x1 < rx1 && rx1 < x2 ? 1 : 0; + } + return x2 < rx1 && rx1 < x1 ? -1 : 0; + } + + // INSIDE + CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2); + double px1 = rx1 - x1; + double py1 = ry1 - y1; + double px2 = rx2 - x1; + double py2 = ry2 - y1; + + double res1[] = new double[3]; + double res2[] = new double[3]; + int rc1 = c.solvePoint(res1, px1); + int rc2 = c.solvePoint(res2, px2); + + // LEFT/RIGHT + if (rc1 == 0 && rc2 == 0) { + return 0; + } + + double minX = px1 - DELTA; + double maxX = px2 + DELTA; + + // Build bound -------------------------------------------------------- + double bound[] = new double[40]; + int bc = 0; + // Add roots + bc = c.addBound(bound, bc, res1, rc1, minX, maxX, false, 0); + bc = c.addBound(bound, bc, res2, rc2, minX, maxX, false, 1); + // Add extrimal points + rc2 = c.solveExtremX(res2); + bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 2); + rc2 = c.solveExtremY(res2); + bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 4); + // Add start and end + if (rx1 < x1 && x1 < rx2) { + bound[bc++] = 0.0; + bound[bc++] = 0.0; + bound[bc++] = 0.0; + bound[bc++] = 6; + } + if (rx1 < x2 && x2 < rx2) { + bound[bc++] = 1.0; + bound[bc++] = c.ax; + bound[bc++] = c.ay; + bound[bc++] = 7; + } + // End build bound ---------------------------------------------------- + + int cross = crossBound(bound, bc, py1, py2); + if (cross != UNKNOWN) { + return cross; + } + return c.cross(res1, rc1, py1, py2); + } + + /** + * Returns how many times rectangle stripe cross path or the are intersect + */ + public static int intersectPath(PathIterator p, double x, double y, double w, double h) { + + int cross = 0; + int count; + double mx, my, cx, cy; + mx = my = cx = cy = 0.0; + double coords[] = new double[6]; + + double rx1 = x; + double ry1 = y; + double rx2 = x + w; + double ry2 = y + h; + + while (!p.isDone()) { + count = 0; + switch (p.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + if (cx != mx || cy != my) { + count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2); + } + mx = cx = coords[0]; + my = cy = coords[1]; + break; + case PathIterator.SEG_LINETO: + count = intersectLine(cx, cy, cx = coords[0], cy = coords[1], rx1, ry1, rx2, ry2); + break; + case PathIterator.SEG_QUADTO: + count = intersectQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3], rx1, ry1, rx2, ry2); + break; + case PathIterator.SEG_CUBICTO: + count = intersectCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4], cy = coords[5], rx1, ry1, rx2, ry2); + break; + case PathIterator.SEG_CLOSE: + if (cy != my || cx != mx) { + count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2); + } + cx = mx; + cy = my; + break; + } + if (count == CROSSING) { + return CROSSING; + } + cross += count; + p.next(); + } + if (cy != my) { + count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2); + if (count == CROSSING) { + return CROSSING; + } + cross += count; + } + return cross; + } + + /** + * Returns how many times rectangle stripe cross shape or the are intersect + */ + public static int intersectShape(Shape s, double x, double y, double w, double h) { + if (!s.getBounds2D().intersects(x, y, w, h)) { + return 0; + } + return intersectPath(s.getPathIterator(null), x, y, w, h); + } + + /** + * Returns true if cross count correspond inside location for non zero path rule + */ + public static boolean isInsideNonZero(int cross) { + return cross != 0; + } + + /** + * Returns true if cross count correspond inside location for even-odd path rule + */ + public static boolean isInsideEvenOdd(int cross) { + return (cross & 1) != 0; + } +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/gl/GLVolatileImage.java b/awt/org/apache/harmony/awt/gl/GLVolatileImage.java new file mode 100644 index 000000000..177be2309 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/GLVolatileImage.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ +package org.apache.harmony.awt.gl; + +import java.awt.image.*; + +import org.apache.harmony.awt.gl.Surface; + +public abstract class GLVolatileImage extends VolatileImage { + + public abstract Surface getImageSurface(); +} diff --git a/awt/org/apache/harmony/awt/gl/ICompositeContext.java b/awt/org/apache/harmony/awt/gl/ICompositeContext.java new file mode 100644 index 000000000..fc5631f37 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/ICompositeContext.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ +package org.apache.harmony.awt.gl; + +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +import org.apache.harmony.awt.gl.ImageSurface; +import org.apache.harmony.awt.gl.render.NativeImageBlitter; +import org.apache.harmony.awt.internal.nls.Messages; + + +/** + * This class represent implementation of the CompositeContext interface + */ +public class ICompositeContext implements CompositeContext { + Composite composite; + ColorModel srcCM, dstCM; + ImageSurface srcSurf, dstSurf; + + public ICompositeContext(Composite comp, ColorModel src, ColorModel dst){ + composite = comp; + srcCM = src; + dstCM = dst; + } + + public void dispose() { + srcSurf.dispose(); + dstSurf.dispose(); + } + + public void compose(Raster srcIn, Raster dstIn, WritableRaster dstOut) { + + if(!srcCM.isCompatibleRaster(srcIn)) { + // awt.48=The srcIn raster is incompatible with src ColorModel + throw new IllegalArgumentException(Messages.getString("awt.48")); //$NON-NLS-1$ + } + + if(!dstCM.isCompatibleRaster(dstIn)) { + // awt.49=The dstIn raster is incompatible with dst ColorModel + throw new IllegalArgumentException(Messages.getString("awt.49")); //$NON-NLS-1$ + } + + if(dstIn != dstOut){ + if(!dstCM.isCompatibleRaster(dstOut)) { + // awt.4A=The dstOut raster is incompatible with dst ColorModel + throw new IllegalArgumentException(Messages.getString("awt.4A")); //$NON-NLS-1$ + } + dstOut.setDataElements(0, 0, dstIn); + } + WritableRaster src; + if(srcIn instanceof WritableRaster){ + src = (WritableRaster) srcIn; + }else{ + src = srcIn.createCompatibleWritableRaster(); + src.setDataElements(0, 0, srcIn); + } + srcSurf = new ImageSurface(srcCM, src); + dstSurf = new ImageSurface(dstCM, dstOut); + + int w = Math.min(srcIn.getWidth(), dstOut.getWidth()); + int h = Math.min(srcIn.getHeight(), dstOut.getHeight()); + + NativeImageBlitter.getInstance().blit(0, 0, srcSurf, 0, 0, dstSurf, + w, h, composite, null, null); + + } + +} diff --git a/awt/org/apache/harmony/awt/gl/ImageSurface.java b/awt/org/apache/harmony/awt/gl/ImageSurface.java new file mode 100644 index 000000000..6368dd876 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/ImageSurface.java @@ -0,0 +1,323 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + * Created on 10.11.2005 + * + */ +package org.apache.harmony.awt.gl; + +import java.awt.color.ColorSpace; +import java.awt.image.BandedSampleModel; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.ComponentSampleModel; +import java.awt.image.DirectColorModel; +import java.awt.image.IndexColorModel; +import java.awt.image.MultiPixelPackedSampleModel; +import java.awt.image.PixelInterleavedSampleModel; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; + +import org.apache.harmony.awt.gl.color.LUTColorConverter; +import org.apache.harmony.awt.gl.image.DataBufferListener; +import org.apache.harmony.awt.internal.nls.Messages; + + +/** + * This class represent Surface for different types of Images (BufferedImage, + * OffscreenImage and so on) + */ +public class ImageSurface extends Surface implements DataBufferListener { + + boolean nativeDrawable = true; + int surfaceType; + int csType; + ColorModel cm; + WritableRaster raster; + Object data; + + boolean needToRefresh = true; + boolean dataTaken = false; + + private long cachedDataPtr; // Pointer for cached Image Data + private boolean alphaPre; // Cached Image Data alpha premultiplied + + public ImageSurface(ColorModel cm, WritableRaster raster){ + this(cm, raster, Surface.getType(cm, raster)); + } + + public ImageSurface(ColorModel cm, WritableRaster raster, int type){ + if (!cm.isCompatibleRaster(raster)) { + // awt.4D=The raster is incompatible with this ColorModel + throw new IllegalArgumentException(Messages.getString("awt.4D")); //$NON-NLS-1$ + } + this.cm = cm; + this.raster = raster; + surfaceType = type; + + data = AwtImageBackdoorAccessor.getInstance(). + getData(raster.getDataBuffer()); + ColorSpace cs = cm.getColorSpace(); + transparency = cm.getTransparency(); + width = raster.getWidth(); + height = raster.getHeight(); + + // For the moment we can build natively only images which have + // sRGB, Linear_RGB, Linear_Gray Color Space and type different + // from BufferedImage.TYPE_CUSTOM + if(cs == LUTColorConverter.sRGB_CS){ + csType = sRGB_CS; + }else if(cs == LUTColorConverter.LINEAR_RGB_CS){ + csType = Linear_RGB_CS; + }else if(cs == LUTColorConverter.LINEAR_GRAY_CS){ + csType = Linear_Gray_CS; + }else{ + csType = Custom_CS; + nativeDrawable = false; + } + + if(type == BufferedImage.TYPE_CUSTOM){ + nativeDrawable = false; + } + } + + @Override + public ColorModel getColorModel() { + return cm; + } + + @Override + public WritableRaster getRaster() { + return raster; + } + + @Override + public long getSurfaceDataPtr() { + if(surfaceDataPtr == 0L && nativeDrawable){ + createSufaceStructure(); + } + return surfaceDataPtr; + } + + @Override + public Object getData(){ + return data; + } + + @Override + public boolean isNativeDrawable(){ + return nativeDrawable; + } + + @Override + public int getSurfaceType() { + return surfaceType; + } + + /** + * Creates native Surface structure which used for native blitting + */ + private void createSufaceStructure(){ + int cmType = 0; + int numComponents = cm.getNumComponents(); + boolean hasAlpha = cm.hasAlpha(); + boolean isAlphaPre = cm.isAlphaPremultiplied(); + int transparency = cm.getTransparency(); + int bits[] = cm.getComponentSize(); + int pixelStride = cm.getPixelSize(); + int masks[] = null; + int colorMap[] = null; + int colorMapSize = 0; + int transpPixel = -1; + boolean isGrayPallete = false; + SampleModel sm = raster.getSampleModel(); + int smType = 0; + int dataType = sm.getDataType(); + int scanlineStride = 0; + int bankIndeces[] = null; + int bandOffsets[] = null; + int offset = raster.getDataBuffer().getOffset(); + + if(cm instanceof DirectColorModel){ + cmType = DCM; + DirectColorModel dcm = (DirectColorModel) cm; + masks = dcm.getMasks(); + smType = SPPSM; + SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm; + scanlineStride = sppsm.getScanlineStride(); + + }else if(cm instanceof IndexColorModel){ + cmType = ICM; + IndexColorModel icm = (IndexColorModel) cm; + colorMapSize = icm.getMapSize(); + colorMap = new int[colorMapSize]; + icm.getRGBs(colorMap); + transpPixel = icm.getTransparentPixel(); + isGrayPallete = Surface.isGrayPallete(icm); + + if(sm instanceof MultiPixelPackedSampleModel){ + smType = MPPSM; + MultiPixelPackedSampleModel mppsm = + (MultiPixelPackedSampleModel) sm; + scanlineStride = mppsm.getScanlineStride(); + }else if(sm instanceof ComponentSampleModel){ + smType = CSM; + ComponentSampleModel csm = + (ComponentSampleModel) sm; + scanlineStride = csm.getScanlineStride(); + }else{ + // awt.4D=The raster is incompatible with this ColorModel + throw new IllegalArgumentException(Messages.getString("awt.4D")); //$NON-NLS-1$ + } + + }else if(cm instanceof ComponentColorModel){ + cmType = CCM; + if(sm instanceof ComponentSampleModel){ + ComponentSampleModel csm = (ComponentSampleModel) sm; + scanlineStride = csm.getScanlineStride(); + bankIndeces = csm.getBankIndices(); + bandOffsets = csm.getBandOffsets(); + if(sm instanceof PixelInterleavedSampleModel){ + smType = PISM; + }else if(sm instanceof BandedSampleModel){ + smType = BSM; + }else{ + smType = CSM; + } + }else{ + // awt.4D=The raster is incompatible with this ColorModel + throw new IllegalArgumentException(Messages.getString("awt.4D")); //$NON-NLS-1$ + } + + }else{ + surfaceDataPtr = 0L; + return; + } + surfaceDataPtr = createSurfStruct(surfaceType, width, height, cmType, csType, smType, dataType, + numComponents, pixelStride, scanlineStride, bits, masks, colorMapSize, + colorMap, transpPixel, isGrayPallete, bankIndeces, bandOffsets, + offset, hasAlpha, isAlphaPre, transparency); + } + + @Override + public void dispose() { + if(surfaceDataPtr != 0L){ + dispose(surfaceDataPtr); + surfaceDataPtr = 0L; + } + } + + public long getCachedData(boolean alphaPre){ + if(nativeDrawable){ + if(cachedDataPtr == 0L || needToRefresh || this.alphaPre != alphaPre){ + cachedDataPtr = updateCache(getSurfaceDataPtr(), data, alphaPre); + this.alphaPre = alphaPre; + validate(); + } + } + return cachedDataPtr; + } + + private native long createSurfStruct(int surfaceType, int width, int height, + int cmType, int csType, int smType, int dataType, + int numComponents, int pixelStride, int scanlineStride, + int bits[], int masks[], int colorMapSize, int colorMap[], + int transpPixel, boolean isGrayPalette, int bankIndeces[], + int bandOffsets[], int offset, boolean hasAlpha, boolean isAlphaPre, + int transparency); + + private native void dispose(long structPtr); + + private native void setImageSize(long structPtr, int width, int height); + + private native long updateCache(long structPtr, Object data, boolean alphaPre); + + /** + * Supposes that new raster is compatible with an old one + * @param r + */ + public void setRaster(WritableRaster r) { + raster = r; + data = AwtImageBackdoorAccessor.getInstance().getData(r.getDataBuffer()); + if (surfaceDataPtr != 0) { + setImageSize(surfaceDataPtr, r.getWidth(), r.getHeight()); + } + this.width = r.getWidth(); + this.height = r.getHeight(); + } + + @Override + public long lock() { + // TODO + return 0; + } + + @Override + public void unlock() { + //TODO + } + + @Override + public Surface getImageSurface() { + return this; + } + + public void dataChanged() { + needToRefresh = true; + clearValidCaches(); + } + + public void dataTaken() { + dataTaken = true; + needToRefresh = true; + clearValidCaches(); + } + + public void dataReleased(){ + dataTaken = false; + needToRefresh = true; + clearValidCaches(); + } + + @Override + public void invalidate(){ + needToRefresh = true; + clearValidCaches(); + } + + @Override + public void validate(){ + if(!needToRefresh) { + return; + } + if(!dataTaken){ + needToRefresh = false; + AwtImageBackdoorAccessor ba = AwtImageBackdoorAccessor.getInstance(); + ba.validate(raster.getDataBuffer()); + } + + } + + @Override + public boolean invalidated(){ + return needToRefresh; + } +} diff --git a/awt/org/apache/harmony/awt/gl/MultiRectArea.java b/awt/org/apache/harmony/awt/gl/MultiRectArea.java new file mode 100644 index 000000000..c4267f3a9 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/MultiRectArea.java @@ -0,0 +1,836 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ +package org.apache.harmony.awt.gl; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import org.apache.harmony.awt.internal.nls.Messages; + +public class MultiRectArea implements Shape { + + /** + * If CHECK is true validation check active + */ + private static final boolean CHECK = false; + + boolean sorted = true; + + /** + * Rectangle buffer + */ + public int[] rect; + + /** + * Bounding box + */ + Rectangle bounds; + + /** + * Result rectangle array + */ + Rectangle[] rectangles; + + /** + * LineCash provides creating MultiRectArea line by line. Used in JavaShapeRasterizer. + */ + public static class LineCash extends MultiRectArea { + + int lineY; + int bottomCount; + int[] bottom; + + public LineCash(int size) { + super(); + bottom = new int[size]; + bottomCount = 0; + } + + public void setLine(int y) { + lineY = y; + } + + public void skipLine() { + lineY++; + bottomCount = 0; + } + + public void addLine(int[] points, int pointCount) { + int bottomIndex = 0; + int pointIndex = 0; + int rectIndex = 0; + int pointX1 = 0; + int pointX2 = 0; + int bottomX1 = 0; + int bottomX2 = 0; + boolean appendRect = false; + boolean deleteRect = false; + int lastCount = bottomCount; + + while (bottomIndex < lastCount || pointIndex < pointCount) { + + appendRect = false; + deleteRect = false; + + if (bottomIndex < lastCount) { + rectIndex = bottom[bottomIndex]; + bottomX1 = rect[rectIndex]; + bottomX2 = rect[rectIndex + 2]; + } else { + appendRect = true; + } + + if (pointIndex < pointCount) { + pointX1 = points[pointIndex]; + pointX2 = points[pointIndex + 1]; + } else { + deleteRect = true; + } + + if (!deleteRect && !appendRect) { + if (pointX1 == bottomX1 && pointX2 == bottomX2) { + rect[rectIndex + 3] = rect[rectIndex + 3] + 1; + pointIndex += 2; + bottomIndex++; + continue; + } + deleteRect = pointX2 >= bottomX1; + appendRect = pointX1 <= bottomX2; + } + + if (deleteRect) { + if (bottomIndex < bottomCount - 1) { + System.arraycopy(bottom, bottomIndex + 1, bottom, bottomIndex, bottomCount - bottomIndex - 1); + rectIndex -= 4; + } + bottomCount--; + lastCount--; + } + + if (appendRect) { + int i = rect[0]; + bottom[bottomCount++] = i; + rect = MultiRectAreaOp.checkBufSize(rect, 4); + rect[i++] = pointX1; + rect[i++] = lineY; + rect[i++] = pointX2; + rect[i++] = lineY; + pointIndex += 2; + } + } + lineY++; + + invalidate(); + } + + } + + /** + * RectCash provides simple creating MultiRectArea + */ + public static class RectCash extends MultiRectArea { + + int[] cash; + + public RectCash() { + super(); + cash = new int[MultiRectAreaOp.RECT_CAPACITY]; + cash[0] = 1; + } + + public void addRectCashed(int x1, int y1, int x2, int y2) { + addRect(x1, y1, x2, y2); + invalidate(); +/* + // Exclude from cash unnecessary rectangles + int i = 1; + while(i < cash[0]) { + if (rect[cash[i] + 3] >= y1 - 1) { + if (i > 1) { + System.arraycopy(cash, i, cash, 1, cash[0] - i); + } + break; + } + i++; + } + cash[0] -= i - 1; + + // Find in cash rectangle to concatinate + i = 1; + while(i < cash[0]) { + int index = cash[i]; + if (rect[index + 3] != y1 - 1) { + break; + } + if (rect[index] == x1 && rect[index + 2] == x2) { + rect[index + 3] += y2 - y1 + 1; + + int pos = i + 1; + while(pos < cash[0]) { + if (rect[index + 3] <= rect[cash[i] + 3]) { + System.arraycopy(cash, i + 1, cash, i, pos - i); + break; + } + i++; + } + cash[pos - 1] = index; + + invalidate(); + return; + } + i++; + } + + // Add rectangle to buffer + int index = rect[0]; + rect = MultiRectAreaOp.checkBufSize(rect, 4); + rect[index + 0] = x1; + rect[index + 1] = y1; + rect[index + 2] = x2; + rect[index + 3] = y2; + + // Add rectangle to cash + int length = cash[0]; + cash = MultiRectAreaOp.checkBufSize(cash, 1); + while(i < length) { + if (y2 <= rect[cash[i] + 3]) { + System.arraycopy(cash, i, cash, i + 1, length - i); + break; + } + i++; + } + cash[i] = index; + invalidate(); +*/ + } + + public void addRectCashed(int[] rect, int rectOff, int rectLength) { + for(int i = rectOff; i < rectOff + rectLength;) { + addRect(rect[i++], rect[i++], rect[i++], rect[i++]); +// addRectCashed(rect[i++], rect[i++], rect[i++], rect[i++]); + } + } + + } + + /** + * MultiRectArea path iterator + */ + class Iterator implements PathIterator { + + int type; + int index; + int pos; + + int[] rect; + AffineTransform t; + + Iterator(MultiRectArea mra, AffineTransform t) { + rect = new int[mra.rect[0] - 1]; + System.arraycopy(mra.rect, 1, rect, 0, rect.length); + this.t = t; + } + + public int getWindingRule() { + return WIND_NON_ZERO; + } + + public boolean isDone() { + return pos >= rect.length; + } + + public void next() { + if (index == 4) { + pos += 4; + } + index = (index + 1) % 5; + } + + public int currentSegment(double[] coords) { + if (isDone()) { + // awt.4B=Iiterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type = 0; + + switch(index) { + case 0 : + type = SEG_MOVETO; + coords[0] = rect[pos + 0]; + coords[1] = rect[pos + 1]; + break; + case 1: + type = SEG_LINETO; + coords[0] = rect[pos + 2]; + coords[1] = rect[pos + 1]; + break; + case 2: + type = SEG_LINETO; + coords[0] = rect[pos + 2]; + coords[1] = rect[pos + 3]; + break; + case 3: + type = SEG_LINETO; + coords[0] = rect[pos + 0]; + coords[1] = rect[pos + 3]; + break; + case 4: + type = SEG_CLOSE; + break; + } + + if (t != null) { + t.transform(coords, 0, coords, 0, 1); + } + return type; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + // awt.4B=Iiterator out of bounds + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + int type = 0; + + switch(index) { + case 0 : + type = SEG_MOVETO; + coords[0] = rect[pos + 0]; + coords[1] = rect[pos + 1]; + break; + case 1: + type = SEG_LINETO; + coords[0] = rect[pos + 2]; + coords[1] = rect[pos + 1]; + break; + case 2: + type = SEG_LINETO; + coords[0] = rect[pos + 2]; + coords[1] = rect[pos + 3]; + break; + case 3: + type = SEG_LINETO; + coords[0] = rect[pos + 0]; + coords[1] = rect[pos + 3]; + break; + case 4: + type = SEG_CLOSE; + break; + } + + if (t != null) { + t.transform(coords, 0, coords, 0, 1); + } + return type; + } + + } + + /** + * Constructs a new empty MultiRectArea + */ + public MultiRectArea() { + rect = MultiRectAreaOp.createBuf(0); + } + + public MultiRectArea(boolean sorted) { + this(); + this.sorted = sorted; + } + + /** + * Constructs a new MultiRectArea as a copy of another one + */ + public MultiRectArea(MultiRectArea mra) { + if (mra == null) { + rect = MultiRectAreaOp.createBuf(0); + } else { + rect = new int[mra.rect.length]; + System.arraycopy(mra.rect, 0, rect, 0, mra.rect.length); + check(this, "MultiRectArea(MRA)"); //$NON-NLS-1$ + } + } + + /** + * Constructs a new MultiRectArea consists of single rectangle + */ + public MultiRectArea(Rectangle r) { + rect = MultiRectAreaOp.createBuf(0); + if (r != null && !r.isEmpty()) { + rect[0] = 5; + rect[1] = r.x; + rect[2] = r.y; + rect[3] = r.x + r.width - 1; + rect[4] = r.y + r.height - 1; + } + check(this, "MultiRectArea(Rectangle)"); //$NON-NLS-1$ + } + + /** + * Constructs a new MultiRectArea consists of single rectangle + */ + public MultiRectArea(int x0, int y0, int x1, int y1) { + rect = MultiRectAreaOp.createBuf(0); + if (x1 >= x0 && y1 >= y0) { + rect[0] = 5; + rect[1] = x0; + rect[2] = y0; + rect[3] = x1; + rect[4] = y1; + } + check(this, "MultiRectArea(Rectangle)"); //$NON-NLS-1$ + } + + /** + * Constructs a new MultiRectArea and append rectangle from buffer + */ + public MultiRectArea(Rectangle[] buf) { + this(); + for (Rectangle element : buf) { + add(element); + } + } + + /** + * Constructs a new MultiRectArea and append rectangle from array + */ + public MultiRectArea(ArrayList buf) { + this(); + for(int i = 0; i < buf.size(); i++) { + add(buf.get(i)); + } + } + + /** + * Sort rectangle buffer + */ + void resort() { + int[] buf = new int[4]; + for(int i = 1; i < rect[0]; i += 4) { + int k = i; + int x1 = rect[k]; + int y1 = rect[k + 1]; + for(int j = i + 4; j < rect[0]; j += 4) { + int x2 = rect[j]; + int y2 = rect[j + 1]; + if (y1 > y2 || (y1 == y2 && x1 > x2)) { + x1 = x2; + y1 = y2; + k = j; + } + } + if (k != i) { + System.arraycopy(rect, i, buf, 0, 4); + System.arraycopy(rect, k, rect, i, 4); + System.arraycopy(buf, 0, rect, k, 4); + } + } + invalidate(); + } + + /** + * Tests equals with another object + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof MultiRectArea) { + MultiRectArea mra = (MultiRectArea) obj; + for(int i = 0; i < rect[0]; i++) { + if (rect[i] != mra.rect[i]) { + return false; + } + } + return true; + } + return false; + } + + /** + * Checks validation of MultiRectArea object + */ + static MultiRectArea check(MultiRectArea mra, String msg) { + if (CHECK && mra != null) { + if (MultiRectArea.checkValidation(mra.getRectangles(), mra.sorted) != -1) { + // awt.4C=Invalid MultiRectArea in method {0} + new RuntimeException(Messages.getString("awt.4C", msg)); //$NON-NLS-1$ + } + } + return mra; + } + + /** + * Checks validation of MultiRectArea object + */ + public static int checkValidation(Rectangle[] r, boolean sorted) { + + // Check width and height + for(int i = 0; i < r.length; i++) { + if (r[i].width <= 0 || r[i].height <= 0) { + return i; + } + } + + // Check order + if (sorted) { + for(int i = 1; i < r.length; i++) { + if (r[i - 1].y > r[i].y) { + return i; + } + if (r[i - 1].y == r[i].y) { + if (r[i - 1].x > r[i].x) { + return i; + } + } + } + } + + // Check override + for(int i = 0; i < r.length; i++) { + for(int j = i + 1; j < r.length; j++) { + if (r[i].intersects(r[j])) { + return i; + } + } + } + + return -1; + } + + /** + * Assigns rectangle from another buffer + */ + protected void setRect(int[] buf, boolean copy) { + if (copy) { + rect = new int[buf.length]; + System.arraycopy(buf, 0, rect, 0, buf.length); + } else { + rect = buf; + } + invalidate(); + } + + /** + * Union with another MultiRectArea object + */ + public void add(MultiRectArea mra) { + setRect(union(this, mra).rect, false); + invalidate(); + } + + /** + * Intersect with another MultiRectArea object + */ + public void intersect(MultiRectArea mra) { + setRect(intersect(this, mra).rect, false); + invalidate(); + } + + /** + * Subtract another MultiRectArea object + */ + public void substract(MultiRectArea mra) { + setRect(subtract(this, mra).rect, false); + invalidate(); + } + + /** + * Union with Rectangle object + */ + public void add(Rectangle rect) { + setRect(union(this, new MultiRectArea(rect)).rect, false); + invalidate(); + } + + /** + * Intersect with Rectangle object + */ + public void intersect(Rectangle rect) { + setRect(intersect(this, new MultiRectArea(rect)).rect, false); + invalidate(); + } + + /** + * Subtract rectangle object + */ + public void substract(Rectangle rect) { + setRect(subtract(this, new MultiRectArea(rect)).rect, false); + } + + /** + * Union two MutliRectareArea objects + */ + public static MultiRectArea intersect(MultiRectArea src1, MultiRectArea src2) { + MultiRectArea res = check(MultiRectAreaOp.Intersection.getResult(src1, src2), "intersect(MRA,MRA)"); //$NON-NLS-1$ + return res; + } + + /** + * Intersect two MultiRectArea objects + */ + public static MultiRectArea union(MultiRectArea src1, MultiRectArea src2) { + MultiRectArea res = check(new MultiRectAreaOp.Union().getResult(src1, src2), "union(MRA,MRA)"); //$NON-NLS-1$ + return res; + } + + /** + * Subtract two MultiRectArea objects + */ + public static MultiRectArea subtract(MultiRectArea src1, MultiRectArea src2) { + MultiRectArea res = check(MultiRectAreaOp.Subtraction.getResult(src1, src2), "subtract(MRA,MRA)"); //$NON-NLS-1$ + return res; + } + + /** + * Print MultiRectArea object to output stream + */ + public static void print(MultiRectArea mra, String msg) { + if (mra == null) { + System.out.println(msg + "=null"); //$NON-NLS-1$ + } else { + Rectangle[] rects = mra.getRectangles(); + System.out.println(msg + "(" + rects.length + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + for (Rectangle element : rects) { + System.out.println( + element.x + "," + //$NON-NLS-1$ + element.y + "," + //$NON-NLS-1$ + (element.x + element.width - 1) + "," + //$NON-NLS-1$ + (element.y + element.height - 1)); + } + } + } + + /** + * Translate MultiRectArea object by (x, y) + */ + public void translate(int x, int y) { + for(int i = 1; i < rect[0];) { + rect[i++] += x; + rect[i++] += y; + rect[i++] += x; + rect[i++] += y; + } + + if (bounds != null && !bounds.isEmpty()) { + bounds.translate(x, y); + } + + if (rectangles != null) { + for (Rectangle element : rectangles) { + element.translate(x, y); + } + } + } + + /** + * Add rectangle to the buffer without any checking + */ + public void addRect(int x1, int y1, int x2, int y2) { + int i = rect[0]; + rect = MultiRectAreaOp.checkBufSize(rect, 4); + rect[i++] = x1; + rect[i++] = y1; + rect[i++] = x2; + rect[i++] = y2; + } + + /** + * Tests is MultiRectArea empty + */ + public boolean isEmpty() { + return rect[0] == 1; + } + + void invalidate() { + bounds = null; + rectangles = null; + } + + /** + * Returns bounds of MultiRectArea object + */ + public Rectangle getBounds() { + if (bounds != null) { + return bounds; + } + + if (isEmpty()) { + return bounds = new Rectangle(); + } + + int x1 = rect[1]; + int y1 = rect[2]; + int x2 = rect[3]; + int y2 = rect[4]; + + for(int i = 5; i < rect[0]; i += 4) { + int rx1 = rect[i + 0]; + int ry1 = rect[i + 1]; + int rx2 = rect[i + 2]; + int ry2 = rect[i + 3]; + if (rx1 < x1) { + x1 = rx1; + } + if (rx2 > x2) { + x2 = rx2; + } + if (ry1 < y1) { + y1 = ry1; + } + if (ry2 > y2) { + y2 = ry2; + } + } + + return bounds = new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + } + + /** + * Recturn rectangle count in the buffer + */ + public int getRectCount() { + return (rect[0] - 1) / 4; + } + + /** + * Returns Rectangle array + */ + public Rectangle[] getRectangles() { + if (rectangles != null) { + return rectangles; + } + + rectangles = new Rectangle[(rect[0] - 1) / 4]; + int j = 0; + for(int i = 1; i < rect[0]; i += 4) { + rectangles[j++] = new Rectangle( + rect[i], + rect[i + 1], + rect[i + 2] - rect[i] + 1, + rect[i + 3] - rect[i + 1] + 1); + } + return rectangles; + } + + /** + * Returns Bounds2D + */ + public Rectangle2D getBounds2D() { + return getBounds(); + } + + /** + * Tests does point lie inside MultiRectArea object + */ + public boolean contains(double x, double y) { + for(int i = 1; i < rect[0]; i+= 4) { + if (rect[i] <= x && x <= rect[i + 2] && rect[i + 1] <= y && y <= rect[i + 3]) { + return true; + } + } + return false; + } + + /** + * Tests does Point2D lie inside MultiRectArea object + */ + public boolean contains(Point2D p) { + return contains(p.getX(), p.getY()); + } + + /** + * Tests does rectangle lie inside MultiRectArea object + */ + public boolean contains(double x, double y, double w, double h) { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + /** + * Tests does Rectangle2D lie inside MultiRectArea object + */ + public boolean contains(Rectangle2D r) { + throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + } + + /** + * Tests does rectangle intersect MultiRectArea object + */ + public boolean intersects(double x, double y, double w, double h) { + Rectangle r = new Rectangle(); + r.setRect(x, y, w, h); + return intersects(r); + } + + /** + * Tests does Rectangle2D intersect MultiRectArea object + */ + public boolean intersects(Rectangle2D r) { + if (r == null || r.isEmpty()) { + return false; + } + for(int i = 1; i < rect[0]; i+= 4) { + if (r.intersects(rect[i], rect[i+1], rect[i + 2]-rect[i]+1, rect[i + 3]-rect[i + 1]+1)) { + return true; + } + } + return false; + } + + /** + * Returns path iterator + */ + public PathIterator getPathIterator(AffineTransform t, double flatness) { + return new Iterator(this, t); + } + + /** + * Returns path iterator + */ + public PathIterator getPathIterator(AffineTransform t) { + return new Iterator(this, t); + } + + /** + * Returns MultiRectArea object converted to string + */ + @Override + public String toString() { + int cnt = getRectCount(); + StringBuffer sb = new StringBuffer((cnt << 5) + 128); + sb.append(getClass().getName()).append(" ["); //$NON-NLS-1$ + for(int i = 1; i < rect[0]; i += 4) { + sb.append(i > 1 ? ", [" : "[").append(rect[i]).append(", ").append(rect[i + 1]). //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + append(", ").append(rect[i + 2] - rect[i] + 1).append(", "). //$NON-NLS-1$ //$NON-NLS-2$ + append(rect[i + 3] - rect[i + 1] + 1).append("]"); //$NON-NLS-1$ + } + return sb.append("]").toString(); //$NON-NLS-1$ + } + +} + diff --git a/awt/org/apache/harmony/awt/gl/MultiRectAreaOp.java b/awt/org/apache/harmony/awt/gl/MultiRectAreaOp.java new file mode 100644 index 000000000..c75e2032a --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/MultiRectAreaOp.java @@ -0,0 +1,837 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ +package org.apache.harmony.awt.gl; + +import java.awt.Rectangle; + +public class MultiRectAreaOp { + + /** + * Rectangle buffer capacity + */ + public static final int RECT_CAPACITY = 16; + + /** + * If number of rectangle in MultiRectArea object less than MAX_SIMPLE simple algorithm applies + */ + private static final int MAX_SIMPLE = 8; + + /** + * Create buffer + */ + public static int[] createBuf(int capacity) { + if (capacity == 0) { + capacity = RECT_CAPACITY; + } + int[] buf = new int[capacity]; + buf[0] = 1; + return buf; + } + + /** + * Checks buffer size and reallocate if necessary + */ + public static int[] checkBufSize(int[] buf, int capacity) { + if (buf[0] + capacity >= buf.length) { + int length = buf[0] + (capacity > RECT_CAPACITY ? capacity : RECT_CAPACITY); + int[] tmp = new int[length]; + System.arraycopy(buf, 0, tmp, 0, buf[0]); + buf = tmp; + } + buf[0] += capacity; + return buf; + } + + /** + * Region class provides basic functionlity for MultiRectArea objects to make logical operations + */ + static class Region { + + int[] region; + int[] active; + int[] bottom; + int index; + + public Region(int[] region) { + this.region = region; + active = new int[RECT_CAPACITY]; + bottom = new int[RECT_CAPACITY]; + active[0] = 1; + bottom[0] = 1; + index = 1; + } + + void addActive(int index) { + int length = active[0]; + active = checkBufSize(active, 4); + int i = 1; + + while(i < length) { + if (region[index] < active[i]) { + // Insert + System.arraycopy(active, i, active, i + 4, length - i); + length = i; + break; + } + i += 4; + } + System.arraycopy(region, index, active, length, 4); + + } + + void findActive(int top, int bottom) { + while(index < region[0]) { + if (region[index + 1] > bottom) { // y1 > bottom + return; + } + if (region[index + 3] >= top) { // y2 >= top + addActive(index); + } + index += 4; + } + } + + void deleteActive(int bottom) { + int length = active[0]; + for(int i = 1; i < length;) { + if (active[i + 3] == bottom) { + length -= 4; + if (i < length) { + System.arraycopy(active, i + 4, active, i, length - i); + } + } else { + i += 4; + } + } + active[0] = length; + } + + void deleteActive() { + int length = active[0]; + for(int i = length - 4; i > 0; i -= 4) { + if (active[i + 1] > active[i + 3]) { + length -= 4; + if (i < length) { + System.arraycopy(active, i + 4, active, i, length - i); + } + } + } + active[0] = length; + } + + void createLevel(int[] level) { + int levelCount = 1; + int topIndex = 1; + int i = 1; + while(i < region[0]) { + + int top = region[i + 1]; + int bottom = region[i + 3] + 1; + int j = topIndex; + + addTop: { + while(j < levelCount) { + if (level[j] == top) { + break addTop; + } + if (level[j] > top) { + System.arraycopy(level, j, level, j + 1, levelCount - j); + break; + } + j++; + } + + level[j] = top; + levelCount++; + topIndex = j; + } + + addBottom: { + while(j < levelCount) { + if (level[j] == bottom) { + break addBottom; + } + if (level[j] > bottom) { + System.arraycopy(level, j, level, j + 1, levelCount - j); + break; + } + j++; + }; + + level[j] = bottom; + levelCount++; + } + + i += 4; + } + level[0] = levelCount; + } + + static void sortOrdered(int[] src1, int[] src2, int[] dst) { + int length1 = src1[0]; + int length2 = src2[0]; + int count = 1; + int i1 = 1; + int i2 = 1; + int v1 = src1[1]; + int v2 = src2[1]; + while(true) { + + LEFT: { + while(i1 < length1) { + v1 = src1[i1]; + if (v1 >= v2) { + break LEFT; + } + dst[count++] = v1; + i1++; + } + while(i2 < length2) { + dst[count++] = src2[i2++]; + } + dst[0] = count; + return; + } + + RIGHT: { + while(i2 < length2) { + v2 = src2[i2]; + if (v2 >= v1) { + break RIGHT; + } + dst[count++] = v2; + i2++; + } + while(i1 < length1) { + dst[count++] = src1[i1++]; + } + dst[0] = count; + return; + } + + if (v1 == v2) { + dst[count++] = v1; + i1++; + i2++; + if (i1 < length1) { + v1 = src1[i1]; + } + if (i2 < length2 - 1) { + v2 = src2[i2]; + } + } + } + // UNREACHABLE + } + + } + + /** + * Intersection class provides intersection of two MultiRectAre aobjects + */ + static class Intersection { + + static void intersectRegions(int[] reg1, int[] reg2, MultiRectArea.RectCash dst, int height1, int height2) { + + Region d1 = new Region(reg1); + Region d2 = new Region(reg2); + + int[] level = new int[height1 + height2]; + int[] level1 = new int[height1]; + int[] level2 = new int[height2]; + d1.createLevel(level1); + d2.createLevel(level2); + Region.sortOrdered(level1, level2, level); + + int top; + int bottom = level[1] - 1; + for(int i = 2; i < level[0]; i++) { + + top = bottom + 1; + bottom = level[i] - 1; + + d1.findActive(top, bottom); + d2.findActive(top, bottom); + + int i1 = 1; + int i2 = 1; + + while(i1 < d1.active[0] && i2 < d2.active[0]) { + + int x11 = d1.active[i1]; + int x12 = d1.active[i1 + 2]; + int x21 = d2.active[i2]; + int x22 = d2.active[i2 + 2]; + + if (x11 <= x21) { + if (x12 >= x21) { + if (x12 <= x22) { + dst.addRectCashed(x21, top, x12, bottom); + i1 += 4; + } else { + dst.addRectCashed(x21, top, x22, bottom); + i2 += 4; + } + } else { + i1 += 4; + } + } else { + if (x22 >= x11) { + if (x22 <= x12) { + dst.addRectCashed(x11, top, x22, bottom); + i2 += 4; + } else { + dst.addRectCashed(x11, top, x12, bottom); + i1 += 4; + } + } else { + i2 += 4; + } + } + } + + d1.deleteActive(bottom); + d2.deleteActive(bottom); + } + } + + static int[] simpleIntersect(MultiRectArea src1, MultiRectArea src2) { + int[] rect1 = src1.rect; + int[] rect2 = src2.rect; + int[] rect = createBuf(0); + + int k = 1; + for(int i = 1; i < rect1[0];) { + + int x11 = rect1[i++]; + int y11 = rect1[i++]; + int x12 = rect1[i++]; + int y12 = rect1[i++]; + + for(int j = 1; j < rect2[0];) { + + int x21 = rect2[j++]; + int y21 = rect2[j++]; + int x22 = rect2[j++]; + int y22 = rect2[j++]; + + if (x11 <= x22 && x12 >= x21 && + y11 <= y22 && y12 >= y21) + { + rect = checkBufSize(rect, 4); + rect[k++] = x11 > x21 ? x11 : x21; + rect[k++] = y11 > y21 ? y11 : y21; + rect[k++] = x12 > x22 ? x22 : x12; + rect[k++] = y12 > y22 ? y22 : y12; + } + } + } + + rect[0] = k; + return rect; + } + + public static MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) { + + if (src1 == null || src2 == null || src1.isEmpty() || src2.isEmpty()) { + return new MultiRectArea(); + } + + MultiRectArea.RectCash dst = new MultiRectArea.RectCash(); + + if (!src1.sorted || !src2.sorted || + src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE) + { + dst.setRect(simpleIntersect(src1, src2), false); + } else { + Rectangle bounds1 = src1.getBounds(); + Rectangle bounds2 = src2.getBounds(); + Rectangle bounds3 = bounds1.intersection(bounds2); + if (bounds3.width > 0 && bounds3.height > 0) { + intersectRegions(src1.rect, src2.rect, dst, bounds1.height + 2, bounds2.height + 2); + } + } + + return dst; + } + + } + + /** + * Union class provides union of two MultiRectAre aobjects + */ + static class Union { + + int rx1, rx2; + int top, bottom; + MultiRectArea.RectCash dst; + + boolean next(Region d, int index) { + int x1 = d.active[index]; + int x2 = d.active[index + 2]; + boolean res = false; + + if (x2 < rx1 - 1) { + res = true; + dst.addRectCashed(x1, top, x2, bottom); + } else + if (x1 > rx2 + 1) { + res = false; + dst.addRectCashed(rx1, top, rx2, bottom); + rx1 = x1; + rx2 = x2; + } else { + res = x2 <= rx2; + rx1 = Math.min(x1, rx1); + rx2 = Math.max(x2, rx2); + } + + // Top + if (d.active[index + 1] < top) { + dst.addRectCashed(x1, d.active[index + 1], x2, top - 1); + } + // Bottom + if (d.active[index + 3] > bottom) { + d.active[index + 1] = bottom + 1; + } + return res; + } + + void check(Region d, int index, boolean t) { + int x1 = d.active[index]; + int x2 = d.active[index + 2]; + // Top + if (d.active[index + 1] < top) { + dst.addRectCashed(x1, d.active[index + 1], x2, top - 1); + } + if (t) { + dst.addRectCashed(x1, top, x2, bottom); + } + // Bottom + if (d.active[index + 3] > bottom) { + d.active[index + 1] = bottom + 1; + } + } + + void unionRegions(int[] reg1, int[] reg2, int height1, int height2) { + Region d1 = new Region(reg1); + Region d2 = new Region(reg2); + + int[] level = new int[height1 + height2]; + int[] level1 = new int[height1]; + int[] level2 = new int[height2]; + d1.createLevel(level1); + d2.createLevel(level2); + Region.sortOrdered(level1, level2, level); + + bottom = level[1] - 1; + for(int i = 2; i < level[0]; i++) { + + top = bottom + 1; + bottom = level[i] - 1; + + d1.findActive(top, bottom); + d2.findActive(top, bottom); + + int i1 = 1; + int i2 = 1; + boolean res1, res2; + + if (d1.active[0] > 1) { + check(d1, 1, false); + rx1 = d1.active[1]; + rx2 = d1.active[3]; + i1 += 4; + res1 = false; + res2 = true; + } else + if (d2.active[0] > 1) { + check(d2, 1, false); + rx1 = d2.active[1]; + rx2 = d2.active[3]; + i2 += 4; + res1 = true; + res2 = false; + } else { + continue; + } + + outer: + while(true) { + + while (res1) { + if (i1 >= d1.active[0]) { + dst.addRectCashed(rx1, top, rx2, bottom); + while(i2 < d2.active[0]) { + check(d2, i2, true); + i2 += 4; + } + break outer; + } + res1 = next(d1, i1); + i1 += 4; + } + + while (res2) { + if (i2 >= d2.active[0]) { + dst.addRectCashed(rx1, top, rx2, bottom); + while(i1 < d1.active[0]) { + check(d1, i1, true); + i1 += 4; + } + break outer; + } + res2 = next(d2, i2); + i2 += 4; + } + + res1 = true; + res2 = true; + } // while + + d1.deleteActive(bottom); + d2.deleteActive(bottom); + + } + } + + static void simpleUnion(MultiRectArea src1, MultiRectArea src2, MultiRectArea dst) { + if (src1.getRectCount() < src2.getRectCount()) { + simpleUnion(src2, src1, dst); + } else { + Subtraction.simpleSubtract(src1, src2, dst); + int pos = dst.rect[0]; + int size = src2.rect[0] - 1; + dst.rect = checkBufSize(dst.rect, size); + System.arraycopy(src2.rect,1, dst.rect, pos, size); + dst.resort(); + } + } + + MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) { + + if (src1 == null || src1.isEmpty()) { + return new MultiRectArea(src2); + } + + if (src2 == null || src2.isEmpty()) { + return new MultiRectArea(src1); + } + + dst = new MultiRectArea.RectCash(); + + if (!src1.sorted || !src2.sorted || + src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE) + { + simpleUnion(src1, src2, dst); + } else { + Rectangle bounds1 = src1.getBounds(); + Rectangle bounds2 = src2.getBounds(); + Rectangle bounds3 = bounds1.intersection(bounds2); + + if (bounds3.width < 0 || bounds3.height < 0) { + if (bounds1.y + bounds1.height < bounds2.y) { + dst.setRect(addVerRegion(src1.rect, src2.rect), false); + } else + if (bounds2.y + bounds2.height < bounds1.y) { + dst.setRect(addVerRegion(src2.rect, src1.rect), false); + } else + if (bounds1.x < bounds2.x) { + dst.setRect(addHorRegion(src1.rect, src2.rect), false); + } else { + dst.setRect(addHorRegion(src2.rect, src1.rect), false); + } + } else { + unionRegions(src1.rect, src2.rect, bounds1.height + 2, bounds2.height + 2); + } + } + + return dst; + } + + int[] addVerRegion(int[] top, int[] bottom) { + int length = top[0] + bottom[0] - 1; + int[] dst = new int[length]; + dst[0] = length; + System.arraycopy(top, 1, dst, 1, top[0] - 1); + System.arraycopy(bottom, 1, dst, top[0], bottom[0] - 1); + return dst; + } + + int[] addHorRegion(int[] left, int[] right) { + int count1 = left[0]; + int count2 = right[0]; + int[] dst = new int[count1 + count2 + 1]; + int count = 1; + int index1 = 1; + int index2 = 1; + + int top1 = left[2]; + int top2 = right[2]; + int pos1, pos2; + + while(true) { + + if (index1 >= count1) { + System.arraycopy(right, index2, dst, count, count2 - index2); + count += count2 - index2; + break; + } + if (index2 >= count2) { + System.arraycopy(left, index1, dst, count, count1 - index1); + count += count1 - index1; + break; + } + + if (top1 < top2) { + pos1 = index1; + do { + index1 += 4; + } while (index1 < count1 && (top1 = left[index1 + 1]) < top2); + System.arraycopy(left, pos1, dst, count, index1 - pos1); + count += index1 - pos1; + continue; + } + + if (top1 > top2) { + pos2 = index2; + do { + index2 += 4; + } while (index2 < count2 && (top2 = right[index2 + 1]) < top1); + System.arraycopy(right, pos2, dst, count, index2 - pos2); + count += index2 - pos2; + continue; + } + + int top = top1; + pos1 = index1; + pos2 = index2; + do { + index1 += 4; + } while(index1 < count1 && (top1 = left[index1 + 1]) == top); + do { + index2 += 4; + } while(index2 < count2 && (top2 = right[index2 + 1]) == top); + + System.arraycopy(left, pos1, dst, count, index1 - pos1); + count += index1 - pos1; + System.arraycopy(right, pos2, dst, count, index2 - pos2); + count += index2 - pos2; + } + + dst[0] = count; + return dst; + } + + } + + /** + * Subtraction class provides subtraction of two MultiRectAre aobjects + */ + static class Subtraction { + + static void subtractRegions(int[] reg1, int[] reg2, MultiRectArea.RectCash dst, int height1, int height2) { + Region d1 = new Region(reg1); + Region d2 = new Region(reg2); + + int[] level = new int[height1 + height2]; + int[] level1 = new int[height1]; + int[] level2 = new int[height2]; + d1.createLevel(level1); + d2.createLevel(level2); + Region.sortOrdered(level1, level2, level); + + int top; + int bottom = level[1] - 1; + for(int i = 2; i < level[0]; i++) { + + top = bottom + 1; + bottom = level[i] - 1; + + d1.findActive(top, bottom); + if (d1.active[0] == 1) { + d2.deleteActive(bottom); + continue; + } + + d2.findActive(top, bottom); + + int i1 = 1; + int i2 = 1; + + int rx1 = 0; + int rx2 = 0; + + boolean next = true; + + while(true) { + + if (next) { + next = false; + if (i1 >= d1.active[0]) { + break; + } + // Bottom + d1.active[i1 + 1] = bottom + 1; + rx1 = d1.active[i1]; + rx2 = d1.active[i1 + 2]; + i1 += 4; + } + + if (i2 >= d2.active[0]) { + dst.addRectCashed(rx1, top, rx2, bottom); + for(int j = i1; j < d1.active[0]; j += 4) { + dst.addRectCashed(d1.active[j], top, d1.active[j + 2], bottom); + d1.active[j + 1] = bottom + 1; + } + break; + } + + int x1 = d2.active[i2]; + int x2 = d2.active[i2 + 2]; + + if (rx1 < x1) { + if (rx2 >= x1) { + if (rx2 <= x2) { + // [-----------] + // [-------------] + dst.addRectCashed(rx1, top, x1 - 1, bottom); + next = true; + } else { + // [-----------------] + // [------] + dst.addRectCashed(rx1, top, x1 - 1, bottom); + rx1 = x2 + 1; + i2 += 4; + } + } else { + // [-----] + // [----] + dst.addRectCashed(rx1, top, rx2, bottom); + next = true; + } + } else { + if (rx1 <= x2) { + if (rx2 <= x2) { + // [------] + // [-----------] + next = true; + } else { + // [------------] + // [---------] + rx1 = x2 + 1; + i2 += 4; + } + } else { + // [----] + // [-----] + i2 += 4; + } + } + + } + d1.deleteActive(); + d2.deleteActive(bottom); + } + } + + static void subtractRect(int x11, int y11, int x12, int y12, int[] rect, int index, MultiRectArea dst) { + + for(int i = index; i < rect[0]; i += 4) { + int x21 = rect[i + 0]; + int y21 = rect[i + 1]; + int x22 = rect[i + 2]; + int y22 = rect[i + 3]; + + if (x11 <= x22 && x12 >= x21 && y11 <= y22 && y12 >= y21) { + int top, bottom; + if (y11 < y21) { + subtractRect(x11, y11, x12, y21 - 1, rect, i + 4, dst); + top = y21; + } else { + top = y11; + } + if (y12 > y22) { + subtractRect(x11, y22 + 1, x12, y12, rect, i + 4, dst); + bottom = y22; + } else { + bottom = y12; + } + if (x11 < x21) { + subtractRect(x11, top, x21 - 1, bottom, rect, i + 4, dst); + } + if (x12 > x22) { + subtractRect(x22 + 1, top, x12, bottom, rect, i + 4, dst); + } + return; + } + } + dst.addRect(x11, y11, x12, y12); + } + + static void simpleSubtract(MultiRectArea src1, MultiRectArea src2, MultiRectArea dst) { + for(int i = 1; i < src1.rect[0]; i += 4) { + subtractRect( + src1.rect[i + 0], + src1.rect[i + 1], + src1.rect[i + 2], + src1.rect[i + 3], + src2.rect, + 1, + dst); + } + dst.resort(); + } + + public static MultiRectArea getResult(MultiRectArea src1, MultiRectArea src2) { + + if (src1 == null || src1.isEmpty()) { + return new MultiRectArea(); + } + + if (src2 == null || src2.isEmpty()) { + return new MultiRectArea(src1); + } + + MultiRectArea.RectCash dst = new MultiRectArea.RectCash(); + + if (!src1.sorted || !src2.sorted || + src1.getRectCount() <= MAX_SIMPLE || src2.getRectCount() <= MAX_SIMPLE) + { + simpleSubtract(src1, src2, dst); + } else { + Rectangle bounds1 = src1.getBounds(); + Rectangle bounds2 = src2.getBounds(); + Rectangle bounds3 = bounds1.intersection(bounds2); + + if (bounds3.width > 0 && bounds3.height > 0) { + subtractRegions(src1.rect, src2.rect, dst, bounds1.height + 2, bounds2.height + 2); + } else { + dst.setRect(src1.rect, true); + } + } + + return dst; + } + + } + +} diff --git a/awt/org/apache/harmony/awt/gl/Surface.java b/awt/org/apache/harmony/awt/gl/Surface.java new file mode 100644 index 000000000..8b0ae38b9 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/Surface.java @@ -0,0 +1,309 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + * Created on 10.11.2005 + * + */ +package org.apache.harmony.awt.gl; + +import java.awt.Image; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; +import java.awt.image.IndexColorModel; +import java.awt.image.MultiPixelPackedSampleModel; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.util.ArrayList; + +import org.apache.harmony.awt.gl.color.LUTColorConverter; + + +/** + * This class is super class for others types of Surfaces. + * Surface is storing data and data format description, that are using + * in blitting operations + */ +public abstract class Surface implements Transparency{ + + // Color Space Types + public static final int sRGB_CS = 1; + public static final int Linear_RGB_CS = 2; + public static final int Linear_Gray_CS = 3; + public static final int Custom_CS = 0; + + // Color Model Types + public static final int DCM = 1; // Direct Color Model + public static final int ICM = 2; // Index Color Model + public static final int CCM = 3; // Component Color Model + + // Sample Model Types + public static final int SPPSM = 1; // Single Pixel Packed Sample Model + public static final int MPPSM = 2; // Multi Pixel Packed Sample Model + public static final int CSM = 3; // Component Sample Model + public static final int PISM = 4; // Pixel Interleaved Sample Model + public static final int BSM = 5; // Banded Sample Model + + // Surface Types + private static final int ALPHA_MASK = 0xff000000; + private static final int RED_MASK = 0x00ff0000; + private static final int GREEN_MASK = 0x0000ff00; + private static final int BLUE_MASK = 0x000000ff; + private static final int RED_BGR_MASK = 0x000000ff; + private static final int GREEN_BGR_MASK = 0x0000ff00; + private static final int BLUE_BGR_MASK = 0x00ff0000; + private static final int RED_565_MASK = 0xf800; + private static final int GREEN_565_MASK = 0x07e0; + private static final int BLUE_565_MASK = 0x001f; + private static final int RED_555_MASK = 0x7c00; + private static final int GREEN_555_MASK = 0x03e0; + private static final int BLUE_555_MASK = 0x001f; + + static{ + //???AWT + /* + System.loadLibrary("gl"); //$NON-NLS-1$ + initIDs(); + */ + } + + + protected long surfaceDataPtr; // Pointer for Native Surface data + protected int transparency = OPAQUE; + protected int width; + protected int height; + + /** + * This list contains caches with the data of this surface that are valid at the moment. + * Surface should clear this list when its data is updated. + * Caches may check if they are still valid using isCacheValid method. + * When cache gets data from the surface, it should call addValidCache method of the surface. + */ + private final ArrayList validCaches = new ArrayList(); + + public abstract ColorModel getColorModel(); + public abstract WritableRaster getRaster(); + public abstract int getSurfaceType(); // Syrface type. It is equal + // BufferedImge type + /** + * Lock Native Surface data + */ + public abstract long lock(); + + /** + * Unlock Native Surface data + */ + public abstract void unlock(); + + /** + * Dispose Native Surface data + */ + public abstract void dispose(); + public abstract Surface getImageSurface(); + + public long getSurfaceDataPtr(){ + return surfaceDataPtr; + } + + public final boolean isCaheValid(Object cache) { + return validCaches.contains(cache); + } + + public final void addValidCache(Object cache) { + validCaches.add(cache); + } + + protected final void clearValidCaches() { + validCaches.clear(); + } + + /** + * Returns could or coldn't the Surface be blit by Native blitter + * @return - true if the Surface could be blit by Native blitter, + * false in other case + */ + public boolean isNativeDrawable(){ + return true; + } + + public int getTransparency() { + return transparency; + } + + public int getWidth(){ + return width; + } + + public int getHeight(){ + return height; + } + + /** + * If Surface has Raster, this method returns data array of Raster's DataBuffer + * @return - data array + */ + public Object getData(){ + return null; + } + + public boolean invalidated(){ + return true; + } + + public void validate(){} + + public void invalidate(){} + + /** + * Computation type of BufferedImage or Surface + * @param cm - ColorModel + * @param raster - WritableRaste + * @return - type of BufferedImage + */ + public static int getType(ColorModel cm, WritableRaster raster){ + int transferType = cm.getTransferType(); + boolean hasAlpha = cm.hasAlpha(); + ColorSpace cs = cm.getColorSpace(); + int csType = cs.getType(); + SampleModel sm = raster.getSampleModel(); + + if(csType == ColorSpace.TYPE_RGB){ + if(cm instanceof DirectColorModel){ + DirectColorModel dcm = (DirectColorModel) cm; + switch (transferType) { + case DataBuffer.TYPE_INT: + if (dcm.getRedMask() == RED_MASK && + dcm.getGreenMask() == GREEN_MASK && + dcm.getBlueMask() == BLUE_MASK) { + if (!hasAlpha) { + return BufferedImage.TYPE_INT_RGB; + } + if (dcm.getAlphaMask() == ALPHA_MASK) { + if (dcm.isAlphaPremultiplied()) { + return BufferedImage.TYPE_INT_ARGB_PRE; + } + return BufferedImage.TYPE_INT_ARGB; + } + return BufferedImage.TYPE_CUSTOM; + } else if (dcm.getRedMask() == RED_BGR_MASK && + dcm.getGreenMask() == GREEN_BGR_MASK && + dcm.getBlueMask() == BLUE_BGR_MASK) { + if (!hasAlpha) { + return BufferedImage.TYPE_INT_BGR; + } + } else { + return BufferedImage.TYPE_CUSTOM; + } + case DataBuffer.TYPE_USHORT: + if (dcm.getRedMask() == RED_555_MASK && + dcm.getGreenMask() == GREEN_555_MASK && + dcm.getBlueMask() == BLUE_555_MASK && !hasAlpha) { + return BufferedImage.TYPE_USHORT_555_RGB; + } else if (dcm.getRedMask() == RED_565_MASK && + dcm.getGreenMask() == GREEN_565_MASK && + dcm.getBlueMask() == BLUE_565_MASK) { + return BufferedImage.TYPE_USHORT_565_RGB; + } + default: + return BufferedImage.TYPE_CUSTOM; + } + }else if(cm instanceof IndexColorModel){ + IndexColorModel icm = (IndexColorModel) cm; + int pixelBits = icm.getPixelSize(); + if(transferType == DataBuffer.TYPE_BYTE){ + if(sm instanceof MultiPixelPackedSampleModel && !hasAlpha && + pixelBits < 5){ + return BufferedImage.TYPE_BYTE_BINARY; + }else if(pixelBits == 8){ + return BufferedImage.TYPE_BYTE_INDEXED; + } + } + return BufferedImage.TYPE_CUSTOM; + }else if(cm instanceof ComponentColorModel){ + ComponentColorModel ccm = (ComponentColorModel) cm; + if(transferType == DataBuffer.TYPE_BYTE && + sm instanceof ComponentSampleModel){ + ComponentSampleModel csm = + (ComponentSampleModel) sm; + int[] offsets = csm.getBandOffsets(); + int[] bits = ccm.getComponentSize(); + boolean isCustom = false; + for (int i = 0; i < bits.length; i++) { + if (bits[i] != 8 || + offsets[i] != offsets.length - 1 - i) { + isCustom = true; + break; + } + } + if (!isCustom) { + if (!ccm.hasAlpha()) { + return BufferedImage.TYPE_3BYTE_BGR; + } else if (ccm.isAlphaPremultiplied()) { + return BufferedImage.TYPE_4BYTE_ABGR_PRE; + } else { + return BufferedImage.TYPE_4BYTE_ABGR; + } + } + } + return BufferedImage.TYPE_CUSTOM; + } + return BufferedImage.TYPE_CUSTOM; + }else if(cs == LUTColorConverter.LINEAR_GRAY_CS){ + if(cm instanceof ComponentColorModel && + cm.getNumComponents() == 1){ + int bits[] = cm.getComponentSize(); + if(transferType == DataBuffer.TYPE_BYTE && + bits[0] == 8){ + return BufferedImage.TYPE_BYTE_GRAY; + }else if(transferType == DataBuffer.TYPE_USHORT && + bits[0] == 16){ + return BufferedImage.TYPE_USHORT_GRAY; + }else{ + return BufferedImage.TYPE_CUSTOM; + } + } + return BufferedImage.TYPE_CUSTOM; + } + return BufferedImage.TYPE_CUSTOM; + } + + public static Surface getImageSurface(Image image){ + return AwtImageBackdoorAccessor.getInstance().getImageSurface(image); + } + + @Override + protected void finalize() throws Throwable{ + dispose(); + } + + public static boolean isGrayPallete(IndexColorModel icm){ + return AwtImageBackdoorAccessor.getInstance().isGrayPallete(icm); + } + + /** + * Initialization of Native data + * + */ + //???AWT: private static native void initIDs(); +} diff --git a/awt/org/apache/harmony/awt/gl/TextRenderer.java b/awt/org/apache/harmony/awt/gl/TextRenderer.java new file mode 100644 index 000000000..f57952d23 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/TextRenderer.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl; + +import java.awt.Graphics2D; +import java.awt.font.GlyphVector; + +public abstract class TextRenderer { + + /** + * Draws string on specified Graphics at desired position. + * + * @param g specified Graphics2D object + * @param str String object to draw + * @param x start X position to draw + * @param y start Y position to draw + */ + public abstract void drawString(Graphics2D g, String str, float x, float y); + + /** + * Draws string on specified Graphics at desired position. + * + * @param g specified Graphics2D object + * @param str String object to draw + * @param x start X position to draw + * @param y start Y position to draw + */ + public void drawString(Graphics2D g, String str, int x, int y){ + drawString(g, str, (float)x, (float)y); + } + + /** + * Draws GlyphVector on specified Graphics at desired position. + * + * @param g specified Graphics2D object + * @param glyphVector GlyphVector object to draw + * @param x start X position to draw + * @param y start Y position to draw + */ + public abstract void drawGlyphVector(Graphics2D g, GlyphVector glyphVector, float x, float y); +} diff --git a/awt/org/apache/harmony/awt/gl/XORComposite.java b/awt/org/apache/harmony/awt/gl/XORComposite.java new file mode 100644 index 000000000..e27e1d3f0 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/XORComposite.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + * Created on 21.11.2005 + * + */ +package org.apache.harmony.awt.gl; + +import java.awt.Color; +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.RenderingHints; +import java.awt.image.ColorModel; + +public class XORComposite implements Composite { + + Color xorcolor; + + public XORComposite(Color xorcolor){ + this.xorcolor = xorcolor; + } + + public CompositeContext createContext(ColorModel srcCM, ColorModel dstCM, + RenderingHints hints) { + + return new ICompositeContext(this, srcCM, dstCM); + } + + public Color getXORColor(){ + return xorcolor; + } +} diff --git a/awt/org/apache/harmony/awt/gl/color/ColorConverter.java b/awt/org/apache/harmony/awt/gl/color/ColorConverter.java new file mode 100644 index 000000000..c98e11497 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/color/ColorConverter.java @@ -0,0 +1,257 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.color; + +import java.awt.color.ColorSpace; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +/** + * This class combines ColorScaler, ICC_Transform and NativeImageFormat functionality + * in the workflows for different types of input/output pixel data. + */ +public class ColorConverter { + private ColorScaler scaler = new ColorScaler(); + + public void loadScalingData(ColorSpace cs) { + scaler.loadScalingData(cs); + } + + /** + * Translates pixels, stored in source buffered image and writes the data + * to the destination image. + * @param t - ICC transform + * @param src - source image + * @param dst - destination image + */ + public void translateColor(ICC_Transform t, + BufferedImage src, BufferedImage dst) { + NativeImageFormat srcIF = NativeImageFormat.createNativeImageFormat(src); + NativeImageFormat dstIF = NativeImageFormat.createNativeImageFormat(dst); + + if (srcIF != null && dstIF != null) { + t.translateColors(srcIF, dstIF); + return; + } + + srcIF = createImageFormat(src); + dstIF = createImageFormat(dst); + + short srcChanData[] = (short[]) srcIF.getChannelData(); + short dstChanData[] = (short[]) dstIF.getChannelData(); + + ColorModel srcCM = src.getColorModel(); + int nColorChannels = srcCM.getNumColorComponents(); + scaler.loadScalingData(srcCM.getColorSpace()); // input scaling data + ColorModel dstCM = dst.getColorModel(); + + // Prepare array for alpha channel + float alpha[] = null; + boolean saveAlpha = srcCM.hasAlpha() && dstCM.hasAlpha(); + if (saveAlpha) { + alpha = new float[src.getWidth()*src.getHeight()]; + } + + WritableRaster wr = src.getRaster(); + int srcDataPos = 0, alphaPos = 0; + float normalizedVal[]; + for (int row=0, nRows = srcIF.getNumRows(); row profileHandles = new HashMap(); + + private static boolean isCMMLoaded; + + public static void addHandle(ICC_Profile key, long handle) { + profileHandles.put(key, new Long(handle)); + } + + public static void removeHandle(ICC_Profile key) { + profileHandles.remove(key); + } + + public static long getHandle(ICC_Profile key) { + return profileHandles.get(key).longValue(); + } + + /* ICC profile management */ + public static native long cmmOpenProfile(byte[] data); + public static native void cmmCloseProfile(long profileID); + public static native int cmmGetProfileSize(long profileID); + public static native void cmmGetProfile(long profileID, byte[] data); + public static native int cmmGetProfileElementSize(long profileID, int signature); + public static native void cmmGetProfileElement(long profileID, int signature, + byte[] data); + public static native void cmmSetProfileElement(long profileID, int tagSignature, + byte[] data); + + + /* ICC transforms */ + public static native long cmmCreateMultiprofileTransform( + long[] profileHandles, + int[] renderingIntents + ); + public static native void cmmDeleteTransform(long transformHandle); + public static native void cmmTranslateColors(long transformHandle, + NativeImageFormat src, + NativeImageFormat dest); + + static void loadCMM() { + if (!isCMMLoaded) { + AccessController.doPrivileged( + new PrivilegedAction() { + public Void run() { + System.loadLibrary("lcmm"); //$NON-NLS-1$ + return null; + } + } ); + isCMMLoaded = true; + } + } + + /* load native CMM library */ + static { + loadCMM(); + } +} diff --git a/awt/org/apache/harmony/awt/gl/color/NativeImageFormat.java b/awt/org/apache/harmony/awt/gl/color/NativeImageFormat.java new file mode 100644 index 000000000..9594047cd --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/color/NativeImageFormat.java @@ -0,0 +1,642 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.color; + +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.util.ArrayList; + +import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; +import org.apache.harmony.awt.internal.nls.Messages; + + +/** + * This class converts java color/sample models to the LCMS pixel formats. + * It also encapsulates all the information about the image format, which native CMM + * needs to have in order to read/write data. + * + * At present planar formats (multiple bands) are not supported + * and they are handled as a common (custom) case. + * Samples other than 1 - 7 bytes and multiple of 8 bits are + * also handled as custom (and won't be supported in the nearest future). + */ +class NativeImageFormat { + ////////////////////////////////////////////// + // LCMS Pixel types + private static final int PT_ANY = 0; // Don't check colorspace + // 1 & 2 are reserved + private static final int PT_GRAY = 3; + private static final int PT_RGB = 4; + // Skipping other since we don't use them here + /////////////////////////////////////////////// + + // Conversion of predefined BufferedImage formats to LCMS formats + private static final int INT_RGB_LCMS_FMT = + colorspaceSh(PT_RGB)| + extraSh(1)| + channelsSh(3)| + bytesSh(1)| + doswapSh(1)| + swapfirstSh(1); + + private static final int INT_ARGB_LCMS_FMT = INT_RGB_LCMS_FMT; + + private static final int INT_BGR_LCMS_FMT = + colorspaceSh(PT_RGB)| + extraSh(1)| + channelsSh(3)| + bytesSh(1); + + private static final int THREE_BYTE_BGR_LCMS_FMT = + colorspaceSh(PT_RGB)| + channelsSh(3)| + bytesSh(1)| + doswapSh(1); + + private static final int FOUR_BYTE_ABGR_LCMS_FMT = + colorspaceSh(PT_RGB)| + extraSh(1)| + channelsSh(3)| + bytesSh(1)| + doswapSh(1); + + private static final int BYTE_GRAY_LCMS_FMT = + colorspaceSh(PT_GRAY)| + channelsSh(1)| + bytesSh(1); + + private static final int USHORT_GRAY_LCMS_FMT = + colorspaceSh(PT_GRAY)| + channelsSh(1)| + bytesSh(2); + + // LCMS format packed into 32 bit value. For description + // of this format refer to LCMS documentation. + private int cmmFormat = 0; + + // Dimensions + private int rows = 0; + private int cols = 0; + + // Scanline may contain some padding in the end + private int scanlineStride = -1; + + private Object imageData; + // It's possible to have offset from the beginning of the array + private int dataOffset; + + // Has the image alpha channel? If has - here its band band offset goes + private int alphaOffset = -1; + + // initializes proper field IDs + private static native void initIDs(); + + static { + NativeCMM.loadCMM(); + initIDs(); + } + + //////////////////////////////////// + // LCMS image format encoders + //////////////////////////////////// + private static int colorspaceSh(int s) { + return (s << 16); + } + + private static int swapfirstSh(int s) { + return (s << 14); + } + + private static int flavorSh(int s) { + return (s << 13); + } + + private static int planarSh(int s) { + return (s << 12); + } + + private static int endianSh(int s) { + return (s << 11); + } + + private static int doswapSh(int s) { + return (s << 10); + } + + private static int extraSh(int s) { + return (s << 7); + } + + private static int channelsSh(int s) { + return (s << 3); + } + + private static int bytesSh(int s) { + return s; + } + //////////////////////////////////// + // End of LCMS image format encoders + //////////////////////////////////// + + // Accessors + Object getChannelData() { + return imageData; + } + + int getNumCols() { + return cols; + } + + int getNumRows() { + return rows; + } + + // Constructors + public NativeImageFormat() { + } + + /** + * Simple image layout for common case with + * not optimized workflow. + * + * For hifi colorspaces with 5+ color channels imgData + * should be byte array. + * + * For common colorspaces with up to 4 color channels it + * should be short array. + * + * Alpha channel is handled by caller, not by CMS. + * + * Color channels are in their natural order (not BGR but RGB). + * + * @param imgData - array of byte or short + * @param nChannels - number of channels + * @param nRows - number of scanlines in the image + * @param nCols - number of pixels in one row of the image + */ + public NativeImageFormat(Object imgData, int nChannels, int nRows, int nCols) { + if (imgData instanceof short[]) { + cmmFormat |= bytesSh(2); + } + else if (imgData instanceof byte[]) { + cmmFormat |= bytesSh(1); + } + else + // awt.47=First argument should be byte or short array + throw new IllegalArgumentException(Messages.getString("awt.47")); //$NON-NLS-1$ + + cmmFormat |= channelsSh(nChannels); + + rows = nRows; + cols = nCols; + + imageData = imgData; + + dataOffset = 0; + } + + /** + * Deduces image format from the buffered image type + * or color and sample models. + * @param bi - image + * @return image format object + */ + public static NativeImageFormat createNativeImageFormat(BufferedImage bi) { + NativeImageFormat fmt = new NativeImageFormat(); + + switch (bi.getType()) { + case BufferedImage.TYPE_INT_RGB: { + fmt.cmmFormat = INT_RGB_LCMS_FMT; + break; + } + + case BufferedImage.TYPE_INT_ARGB: + case BufferedImage.TYPE_INT_ARGB_PRE: { + fmt.cmmFormat = INT_ARGB_LCMS_FMT; + fmt.alphaOffset = 3; + break; + } + + case BufferedImage.TYPE_INT_BGR: { + fmt.cmmFormat = INT_BGR_LCMS_FMT; + break; + } + + case BufferedImage.TYPE_3BYTE_BGR: { + fmt.cmmFormat = THREE_BYTE_BGR_LCMS_FMT; + break; + } + + case BufferedImage.TYPE_4BYTE_ABGR_PRE: + case BufferedImage.TYPE_4BYTE_ABGR: { + fmt.cmmFormat = FOUR_BYTE_ABGR_LCMS_FMT; + fmt.alphaOffset = 0; + break; + } + + case BufferedImage.TYPE_BYTE_GRAY: { + fmt.cmmFormat = BYTE_GRAY_LCMS_FMT; + break; + } + + case BufferedImage.TYPE_USHORT_GRAY: { + fmt.cmmFormat = USHORT_GRAY_LCMS_FMT; + break; + } + + case BufferedImage.TYPE_BYTE_BINARY: + case BufferedImage.TYPE_USHORT_565_RGB: + case BufferedImage.TYPE_USHORT_555_RGB: + case BufferedImage.TYPE_BYTE_INDEXED: { + // A bunch of unsupported formats + return null; + } + + default: + break; // Try to look at sample model and color model + } + + + if (fmt.cmmFormat == 0) { + ColorModel cm = bi.getColorModel(); + SampleModel sm = bi.getSampleModel(); + + if (sm instanceof ComponentSampleModel) { + ComponentSampleModel csm = (ComponentSampleModel) sm; + fmt.cmmFormat = getFormatFromComponentModel(csm, cm.hasAlpha()); + fmt.scanlineStride = calculateScanlineStrideCSM(csm, bi.getRaster()); + } else if (sm instanceof SinglePixelPackedSampleModel) { + SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm; + fmt.cmmFormat = getFormatFromSPPSampleModel(sppsm, cm.hasAlpha()); + fmt.scanlineStride = calculateScanlineStrideSPPSM(sppsm, bi.getRaster()); + } + + if (cm.hasAlpha()) + fmt.alphaOffset = calculateAlphaOffset(sm, bi.getRaster()); + } + + if (fmt.cmmFormat == 0) + return null; + + if (!fmt.setImageData(bi.getRaster().getDataBuffer())) { + return null; + } + + fmt.rows = bi.getHeight(); + fmt.cols = bi.getWidth(); + + fmt.dataOffset = bi.getRaster().getDataBuffer().getOffset(); + + return fmt; + } + + /** + * Deduces image format from the raster sample model. + * @param r - raster + * @return image format object + */ + public static NativeImageFormat createNativeImageFormat(Raster r) { + NativeImageFormat fmt = new NativeImageFormat(); + SampleModel sm = r.getSampleModel(); + + // Assume that there's no alpha + if (sm instanceof ComponentSampleModel) { + ComponentSampleModel csm = (ComponentSampleModel) sm; + fmt.cmmFormat = getFormatFromComponentModel(csm, false); + fmt.scanlineStride = calculateScanlineStrideCSM(csm, r); + } else if (sm instanceof SinglePixelPackedSampleModel) { + SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm; + fmt.cmmFormat = getFormatFromSPPSampleModel(sppsm, false); + fmt.scanlineStride = calculateScanlineStrideSPPSM(sppsm, r); + } + + if (fmt.cmmFormat == 0) + return null; + + fmt.cols = r.getWidth(); + fmt.rows = r.getHeight(); + fmt.dataOffset = r.getDataBuffer().getOffset(); + + if (!fmt.setImageData(r.getDataBuffer())) + return null; + + return fmt; + } + + /** + * Obtains LCMS format from the component sample model + * @param sm - sample model + * @param hasAlpha - true if there's an alpha channel + * @return LCMS format + */ + private static int getFormatFromComponentModel(ComponentSampleModel sm, boolean hasAlpha) { + // Multiple data arrays (banks) not supported + int bankIndex = sm.getBankIndices()[0]; + for (int i=1; i < sm.getNumBands(); i++) { + if (sm.getBankIndices()[i] != bankIndex) { + return 0; + } + } + + int channels = hasAlpha ? sm.getNumBands()-1 : sm.getNumBands(); + int extra = hasAlpha ? 1 : 0; + int bytes = 1; + switch (sm.getDataType()) { + case DataBuffer.TYPE_BYTE: + bytes = 1; break; + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + bytes = 2; break; + case DataBuffer.TYPE_INT: + bytes = 4; break; + case DataBuffer.TYPE_DOUBLE: + bytes = 0; break; + default: + return 0; // Unsupported data type + } + + int doSwap = 0; + int swapFirst = 0; + boolean knownFormat = false; + + int i; + + // "RGBA" + for (i=0; i < sm.getNumBands(); i++) { + if (sm.getBandOffsets()[i] != i) break; + } + if (i == sm.getNumBands()) { // Ok, it is it + doSwap = 0; + swapFirst = 0; + knownFormat = true; + } + + // "ARGB" + if (!knownFormat) { + for (i=0; i < sm.getNumBands()-1; i++) { + if (sm.getBandOffsets()[i] != i+1) break; + } + if (sm.getBandOffsets()[i] == 0) i++; + if (i == sm.getNumBands()) { // Ok, it is it + doSwap = 0; + swapFirst = 1; + knownFormat = true; + } + } + + // "BGRA" + if (!knownFormat) { + for (i=0; i < sm.getNumBands()-1; i++) { + if (sm.getBandOffsets()[i] != sm.getNumBands() - 2 - i) break; + } + if (sm.getBandOffsets()[i] == sm.getNumBands()-1) i++; + if (i == sm.getNumBands()) { // Ok, it is it + doSwap = 1; + swapFirst = 1; + knownFormat = true; + } + } + + // "ABGR" + if (!knownFormat) { + for (i=0; i < sm.getNumBands(); i++) { + if (sm.getBandOffsets()[i] != sm.getNumBands() - 1 - i) break; + } + if (i == sm.getNumBands()) { // Ok, it is it + doSwap = 1; + swapFirst = 0; + knownFormat = true; + } + } + + // XXX - Planar formats are not supported yet + if (!knownFormat) + return 0; + + return + channelsSh(channels) | + bytesSh(bytes) | + extraSh(extra) | + doswapSh(doSwap) | + swapfirstSh(swapFirst); + } + + /** + * Obtains LCMS format from the single pixel packed sample model + * @param sm - sample model + * @param hasAlpha - true if there's an alpha channel + * @return LCMS format + */ + private static int getFormatFromSPPSampleModel(SinglePixelPackedSampleModel sm, + boolean hasAlpha) { + // Can we extract bytes? + int mask = sm.getBitMasks()[0] >>> sm.getBitOffsets()[0]; + if (!(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF)) + return 0; + + // All masks are same? + for (int i = 1; i < sm.getNumBands(); i++) { + if ((sm.getBitMasks()[i] >>> sm.getBitOffsets()[i]) != mask) + return 0; + } + + int pixelSize = 0; + // Check if data type is supported + if (sm.getDataType() == DataBuffer.TYPE_USHORT) + pixelSize = 2; + else if (sm.getDataType() == DataBuffer.TYPE_INT) + pixelSize = 4; + else + return 0; + + + int bytes = 0; + switch (mask) { + case 0xFF: + bytes = 1; + break; + case 0xFFFF: + bytes = 2; + break; + case 0xFFFFFFFF: + bytes = 4; + break; + default: return 0; + } + + + int channels = hasAlpha ? sm.getNumBands()-1 : sm.getNumBands(); + int extra = hasAlpha ? 1 : 0; + extra += pixelSize/bytes - sm.getNumBands(); // Unused bytes? + + // Form an ArrayList containing offset for each band + ArrayList offsetsLst = new ArrayList(); + for (int k=0; k < sm.getNumBands(); k++) { + offsetsLst.add(new Integer(sm.getBitOffsets()[k]/(bytes*8))); + } + + // Add offsets for unused space + for (int i=0; i + * + * xlfd format: + * -Foundry-Family-Weight-Slant-Width-Style-PixelSize-PointSize-ResX-ResY-Spacing-AvgWidth-Registry-Encoding + * @param xlfd String parameter in xlfd format + */ + public static String[] parseXLFD(String xlfd){ + int fieldsCount = 14; + String fieldsDelim = "-"; //$NON-NLS-1$ + String[] res = new String[fieldsCount]; + if (!xlfd.startsWith(fieldsDelim)){ + return null; + } + + xlfd = xlfd.substring(1); + int i=0; + int pos; + for (i=0; i < fieldsCount-1; i++){ + pos = xlfd.indexOf(fieldsDelim); + if (pos != -1){ + res[i] = xlfd.substring(0, pos); + xlfd = xlfd.substring(pos + 1); + } else { + return null; + } + } + pos = xlfd.indexOf(fieldsDelim); + + // check if no fields left + if(pos != -1){ + return null; + } + res[fieldsCount-1] = xlfd; + + return res; + } + + public int getFaceIndex(String faceName){ + + for (int i = 0; i < faces.length; i++) { + if(faces[i].equals(faceName)){ + return i; + } + } + return -1; + } + + public String[] getAllFamilies(){ + if (allFamilies == null){ + allFamilies = new String[]{"sans-serif", "serif", "monospace"}; + } + return allFamilies; + } + + public Font[] getAllFonts(){ + Font[] fonts = new Font[faces.length]; + for (int i =0; i < fonts.length;i++){ + fonts[i] = new Font(faces[i], Font.PLAIN, 1); + } + return fonts; + } + + public FontPeer createPhysicalFontPeer(String name, int style, int size) { + AndroidFont peer; + int familyIndex = getFamilyIndex(name); + if (familyIndex != -1){ + // !! we use family names from the list with cached families because + // they are differ from the family names in xlfd structure, in xlfd + // family names mostly in lower case. + peer = new AndroidFont(getFamily(familyIndex), style, size); + peer.setFamily(getFamily(familyIndex)); + return peer; + } + int faceIndex = getFaceIndex(name); + if (faceIndex != -1){ + + peer = new AndroidFont(name, style, size); + return peer; + } + + return null; + } + + public FontPeer createDefaultFont(int style, int size) { + Log.i("DEFAULT FONT", Integer.toString(style)); + return new AndroidFont(DEFAULT_NAME, style, size); + } + +} diff --git a/awt/org/apache/harmony/awt/gl/font/AndroidFontProperty.java b/awt/org/apache/harmony/awt/gl/font/AndroidFontProperty.java new file mode 100644 index 000000000..0cfdc43fc --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/AndroidFontProperty.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + * + */ +package org.apache.harmony.awt.gl.font; + +/** + * Android FontProperty implementation, applicable for Linux formats of + * font property files. + */ +public class AndroidFontProperty extends FontProperty { + + /** xlfd string that is applicable for Linux font.properties */ + String xlfd; + + /** logical name of the font corresponding to this FontProperty */ + String logicalName; + + /** style name of the font corresponding to this FontProperty */ + String styleName; + + public AndroidFontProperty(String _logicalName, String _styleName, String _fileName, String _name, String _xlfd, int _style, int[] exclusionRange, String _encoding){ + this.logicalName = _logicalName; + this.styleName = _styleName; + this.name = _name; + this.encoding = _encoding; + this.exclRange = exclusionRange; + this.fileName = _fileName; + this.xlfd = _xlfd; + this.style = _style; + } + + /** + * Returns logical name of the font corresponding to this FontProperty. + */ + public String getLogicalName(){ + return logicalName; + } + + /** + * Returns style name of the font corresponding to this FontProperty. + */ + public String getStyleName(){ + return styleName; + } + + /** + * Returns xlfd string of this FontProperty. + */ + public String getXLFD(){ + return xlfd; + } + + public String toString(){ + return new String(this.getClass().getName() + + "[name=" + name + //$NON-NLS-1$ + ",fileName="+ fileName + //$NON-NLS-1$ + ",Charset=" + encoding + //$NON-NLS-1$ + ",exclRange=" + exclRange + //$NON-NLS-1$ + ",xlfd=" + xlfd + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + + } + +} diff --git a/awt/org/apache/harmony/awt/gl/font/AndroidGlyphVector.java b/awt/org/apache/harmony/awt/gl/font/AndroidGlyphVector.java new file mode 100644 index 000000000..4ce5aed64 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/AndroidGlyphVector.java @@ -0,0 +1,219 @@ +package org.apache.harmony.awt.gl.font; + +import com.android.internal.awt.AndroidGraphics2D; + +import java.awt.Font; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphJustificationInfo; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import android.util.Log; +import android.graphics.Path; + +public class AndroidGlyphVector extends GlyphVector { + + // array of chars defined in constructor + public char[] charVector; + + // array of Glyph objects, that describe information about glyphs + public Glyph[] vector; + + // array of default positions of glyphs in GlyphVector + // without applying GlyphVector's transform + float[] defaultPositions; + + // array of logical positions of glyphs in GlyphVector + + float[] logicalPositions; + + // array of visual (real) positions of glyphs in GlyphVector + public float[] visualPositions; + + // FontRenderContext for this vector. + protected FontRenderContext vectorFRC; + + // layout flags mask + protected int layoutFlags = 0; + + // array of cached glyph outlines + protected Shape[] gvShapes; + + FontPeerImpl peer; + + // font corresponding to the GlyphVector + Font font; + + // ascent of the font + float ascent; + + // height of the font + float height; + + // leading of the font + float leading; + + // descent of the font + float descent; + + // transform of the GlyphVector + AffineTransform transform; + + @SuppressWarnings("deprecation") + public AndroidGlyphVector(char[] chars, FontRenderContext frc, Font fnt, + int flags) { + int len = chars.length; + this.font = fnt; + LineMetricsImpl lmImpl = (LineMetricsImpl)fnt.getLineMetrics(String.valueOf(chars), frc); + this.ascent = lmImpl.getAscent(); + this.height = lmImpl.getHeight(); + this.leading = lmImpl.getLeading(); + this.descent = lmImpl.getDescent(); + this.charVector = chars; + this.vectorFRC = frc; + } + + public AndroidGlyphVector(char[] chars, FontRenderContext frc, Font fnt) { + this(chars, frc, fnt, 0); + } + + public AndroidGlyphVector(String str, FontRenderContext frc, Font fnt) { + this(str.toCharArray(), frc, fnt, 0); + } + + public AndroidGlyphVector(String str, FontRenderContext frc, Font fnt, int flags) { + this(str.toCharArray(), frc, fnt, flags); + } + + @Override + public boolean equals(GlyphVector glyphVector) { + return false; + } + + public char[] getGlyphs() { + return this.charVector; + } + + @Override + public Font getFont() { + return this.font; + } + + @Override + public FontRenderContext getFontRenderContext() { + return this.vectorFRC; + } + + @Override + public int getGlyphCode(int glyphIndex) { + return charVector[glyphIndex]; + } + + @Override + public int[] getGlyphCodes(int beginGlyphIndex, int numEntries, + int[] codeReturn) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public Shape getGlyphLogicalBounds(int glyphIndex) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public GlyphMetrics getGlyphMetrics(int glyphIndex) { + throw new RuntimeException("Not implemented!"); + } + + public Path getAndroidGlyphOutline(int glyphIndex) { + AndroidGraphics2D g = AndroidGraphics2D.getInstance(); + Path path = new Path(); + char tmp[] = new char[1]; + tmp[0] = charVector[glyphIndex]; + ((AndroidGraphics2D)g).getAndroidPaint().getTextPath(new String(tmp), 0, 1, 0, 0, path); + return path; + } + + @Override + public Shape getGlyphOutline(int glyphIndex) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public Point2D getGlyphPosition(int glyphIndex) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public float[] getGlyphPositions(int beginGlyphIndex, int numEntries, + float[] positionReturn) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public AffineTransform getGlyphTransform(int glyphIndex) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public Shape getGlyphVisualBounds(int glyphIndex) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public Rectangle2D getLogicalBounds() { + throw new RuntimeException("Not implemented!"); + } + + @Override + public int getNumGlyphs() { + return charVector.length; + } + + @Override + public Shape getOutline(float x, float y) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public Shape getOutline() { + throw new RuntimeException("Not implemented!"); + } + + public Path getAndroidOutline() { + AndroidGraphics2D g = AndroidGraphics2D.getInstance(); + Path path = new Path(); + ((AndroidGraphics2D)g).getAndroidPaint().getTextPath(new String(charVector), 0, charVector.length, 0, 0, path); + return path; + } + + @Override + public Rectangle2D getVisualBounds() { + throw new RuntimeException("Not implemented!"); + } + + @Override + public void performDefaultLayout() { + throw new RuntimeException("Not implemented!"); + } + + @Override + public void setGlyphPosition(int glyphIndex, Point2D newPos) { + throw new RuntimeException("Not implemented!"); + } + + @Override + public void setGlyphTransform(int glyphIndex, AffineTransform trans) { + throw new RuntimeException("Not implemented!"); + } + +} diff --git a/awt/org/apache/harmony/awt/gl/font/AndroidLineMetrics.java b/awt/org/apache/harmony/awt/gl/font/AndroidLineMetrics.java new file mode 100644 index 000000000..f37be6d4f --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/AndroidLineMetrics.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.font; + +import java.awt.font.FontRenderContext; +import org.apache.harmony.awt.gl.font.LineMetricsImpl; + + +/** + * + * Linux implementation of LineMetrics class + */ +public class AndroidLineMetrics extends LineMetricsImpl { + + /** + * Constructor + */ + public AndroidLineMetrics( AndroidFont fnt, + FontRenderContext frc, + String str){ + numChars = str.length(); + baseLineIndex = 0; + + ascent = fnt.ascent; // Ascent of the font + descent = -fnt.descent; // Descent of the font + leading = fnt.leading; // External leading + + height = ascent + descent + leading; // Height of the font ( == (ascent + descent + leading)) + underlineThickness = 0.0f; + underlineOffset = 0.0f; + strikethroughThickness = 0.0f; + strikethroughOffset = 0.0f; + maxCharWidth = 0.0f; + + // TODO: Find out pixel metrics + /* + * positive metrics rounded to the smallest int that is bigger than value + * negative metrics rounded to the smallest int that is lesser than value + * thicknesses rounded to int ((int)round(value + 0.5)) + * + */ + + lAscent = (int)Math.ceil(fnt.ascent);// // Ascent of the font + lDescent = -(int)Math.ceil(fnt.descent);// Descent of the font + lLeading = (int)Math.ceil(leading); // External leading + + lHeight = lAscent + lDescent + lLeading; // Height of the font ( == (ascent + descent + leading)) + + lUnderlineThickness = Math.round(underlineThickness);//(int)metrics[11]; + + if (underlineOffset >= 0){ + lUnderlineOffset = (int)Math.ceil(underlineOffset); + } else { + lUnderlineOffset = (int)Math.floor(underlineOffset); + } + + lStrikethroughThickness = Math.round(strikethroughThickness); //(int)metrics[13]; + + if (strikethroughOffset >= 0){ + lStrikethroughOffset = (int)Math.ceil(strikethroughOffset); + } else { + lStrikethroughOffset = (int)Math.floor(strikethroughOffset); + } + + lMaxCharWidth = (int)Math.ceil(maxCharWidth); //(int)metrics[15]; + units_per_EM = 0; + + } + + public float[] getBaselineOffsets() { + // TODO: implement baseline offsets for TrueType fonts + if (baselineOffsets == null){ + float[] baselineData = null; + + // Temporary workaround: + // Commented out native data initialization, since it can + // cause failures with opening files in multithreaded applications. + // + // TODO: support work with truetype data in multithreaded + // applications. + + // If font TrueType data is taken from BASE table +// if ((this.font.getFontHandle() != 0) && (font.getFontType() == FontManager.FONT_TYPE_TT)){ +// baselineData = LinuxNativeFont.getBaselineOffsetsNative(font.getFontHandle(), font.getSize(), ascent, descent, units_per_EM); +// } +// + baseLineIndex = 0; + baselineOffsets = new float[]{0, (-ascent+descent)/2, -ascent}; + } + + return baselineOffsets; + } + + public int getBaselineIndex() { + if (baselineOffsets == null){ + // get offsets and set correct index + getBaselineOffsets(); + } + return baseLineIndex; + } + +} diff --git a/awt/org/apache/harmony/awt/gl/font/BasicMetrics.java b/awt/org/apache/harmony/awt/gl/font/BasicMetrics.java new file mode 100644 index 000000000..c0fb390f2 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/BasicMetrics.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + */ + +package org.apache.harmony.awt.gl.font; + +import java.awt.font.LineMetrics; +import java.awt.font.GraphicAttribute; +import java.awt.*; + +/** + * Date: May 14, 2005 + * Time: 7:44:13 PM + * + * This class incapsulates text metrics specific for the text layout or + * for the separate text segment. Text segment is a text run with the constant direction + * and attributes like font, decorations, etc. BasicMetrics is also used to store + * calculated text metrics like advance, ascent or descent. this class is very similar to + * LineMetrics, but provides some additional info, constructors and is more transparent. + */ +public class BasicMetrics { + int baseLineIndex; + + float ascent; // Ascent of the font + float descent; // Descent of the font + float leading; // External leading + float advance; + + float italicAngle; + float superScriptOffset; + + float underlineOffset; + float underlineThickness; + + float strikethroughOffset; + float strikethroughThickness; + + /** + * Constructs BasicMetrics from LineMetrics and font + * @param lm + * @param font + */ + BasicMetrics(LineMetrics lm, Font font) { + ascent = lm.getAscent(); + descent = lm.getDescent(); + leading = lm.getLeading(); + + underlineOffset = lm.getUnderlineOffset(); + underlineThickness = lm.getUnderlineThickness(); + + strikethroughOffset = lm.getStrikethroughOffset(); + strikethroughThickness = lm.getStrikethroughThickness(); + + baseLineIndex = lm.getBaselineIndex(); + + italicAngle = font.getItalicAngle(); + superScriptOffset = (float) font.getTransform().getTranslateY(); + } + + /** + * Constructs BasicMetrics from GraphicAttribute. + * It gets ascent and descent from the graphic attribute and + * computes reasonable defaults for other metrics. + * @param ga - graphic attribute + */ + BasicMetrics(GraphicAttribute ga) { + ascent = ga.getAscent(); + descent = ga.getDescent(); + leading = 2; + + baseLineIndex = ga.getAlignment(); + + italicAngle = 0; + superScriptOffset = 0; + + underlineOffset = Math.max(descent/2, 1); + + // Just suggested, should be cap_stem_width or something like that + underlineThickness = Math.max(ascent/13, 1); + + strikethroughOffset = -ascent/2; // Something like middle of the line + strikethroughThickness = underlineThickness; + } + + /** + * Copies metrics from the TextMetricsCalculator object. + * @param tmc - TextMetricsCalculator object + */ + BasicMetrics(TextMetricsCalculator tmc) { + ascent = tmc.ascent; + descent = tmc.descent; + leading = tmc.leading; + advance = tmc.advance; + baseLineIndex = tmc.baselineIndex; + } + + public float getAscent() { + return ascent; + } + + public float getDescent() { + return descent; + } + + public float getLeading() { + return leading; + } + + public float getAdvance() { + return advance; + } + + public int getBaseLineIndex() { + return baseLineIndex; + } +} diff --git a/awt/org/apache/harmony/awt/gl/font/CaretManager.java b/awt/org/apache/harmony/awt/gl/font/CaretManager.java new file mode 100644 index 000000000..b18bdd53a --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/CaretManager.java @@ -0,0 +1,530 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Jun 14, 2005 + */ + +package org.apache.harmony.awt.gl.font; + +import java.awt.font.TextHitInfo; +import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.*; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * This class provides functionality for creating caret and highlight shapes + * (bidirectional text is also supported, but, unfortunately, not tested yet). + */ +public class CaretManager { + private TextRunBreaker breaker; + + public CaretManager(TextRunBreaker breaker) { + this.breaker = breaker; + } + + /** + * Checks if TextHitInfo is not out of the text range and throws the + * IllegalArgumentException if it is. + * @param info - text hit info + */ + private void checkHit(TextHitInfo info) { + int idx = info.getInsertionIndex(); + + if (idx < 0 || idx > breaker.getCharCount()) { + // awt.42=TextHitInfo out of range + throw new IllegalArgumentException(Messages.getString("awt.42")); //$NON-NLS-1$ + } + } + + /** + * Calculates and returns visual position from the text hit info. + * @param hitInfo - text hit info + * @return visual index + */ + private int getVisualFromHitInfo(TextHitInfo hitInfo) { + final int idx = hitInfo.getCharIndex(); + + if (idx >= 0 && idx < breaker.getCharCount()) { + int visual = breaker.getVisualFromLogical(idx); + // We take next character for (LTR char + TRAILING info) and (RTL + LEADING) + if (hitInfo.isLeadingEdge() ^ ((breaker.getLevel(idx) & 0x1) == 0x0)) { + visual++; + } + return visual; + } else if (idx < 0) { + return breaker.isLTR() ? 0: breaker.getCharCount(); + } else { + return breaker.isLTR() ? breaker.getCharCount() : 0; + } + } + + /** + * Calculates text hit info from the visual position + * @param visual - visual position + * @return text hit info + */ + private TextHitInfo getHitInfoFromVisual(int visual) { + final boolean first = visual == 0; + + if (!(first || visual == breaker.getCharCount())) { + int logical = breaker.getLogicalFromVisual(visual); + return (breaker.getLevel(logical) & 0x1) == 0x0 ? + TextHitInfo.leading(logical) : // LTR + TextHitInfo.trailing(logical); // RTL + } else if (first) { + return breaker.isLTR() ? + TextHitInfo.trailing(-1) : + TextHitInfo.leading(breaker.getCharCount()); + } else { // Last + return breaker.isLTR() ? + TextHitInfo.leading(breaker.getCharCount()) : + TextHitInfo.trailing(-1); + } + } + + /** + * Creates caret info. Required for the getCaretInfo + * methods of the TextLayout + * @param hitInfo - specifies caret position + * @return caret info, see TextLayout.getCaretInfo documentation + */ + public float[] getCaretInfo(TextHitInfo hitInfo) { + checkHit(hitInfo); + float res[] = new float[2]; + + int visual = getVisualFromHitInfo(hitInfo); + float advance, angle; + TextRunSegment seg; + + if (visual < breaker.getCharCount()) { + int logIdx = breaker.getLogicalFromVisual(visual); + int segmentIdx = breaker.logical2segment[logIdx]; + seg = breaker.runSegments.get(segmentIdx); + advance = seg.x + seg.getAdvanceDelta(seg.getStart(), logIdx); + angle = seg.metrics.italicAngle; + + } else { // Last character + int logIdx = breaker.getLogicalFromVisual(visual-1); + int segmentIdx = breaker.logical2segment[logIdx]; + seg = breaker.runSegments.get(segmentIdx); + advance = seg.x + seg.getAdvanceDelta(seg.getStart(), logIdx+1); + } + + angle = seg.metrics.italicAngle; + + res[0] = advance; + res[1] = angle; + + return res; + } + + /** + * Returns the next position to the right from the current caret position + * @param hitInfo - current position + * @return next position to the right + */ + public TextHitInfo getNextRightHit(TextHitInfo hitInfo) { + checkHit(hitInfo); + int visual = getVisualFromHitInfo(hitInfo); + + if (visual == breaker.getCharCount()) { + return null; + } + + TextHitInfo newInfo; + + while(visual <= breaker.getCharCount()) { + visual++; + newInfo = getHitInfoFromVisual(visual); + + if (newInfo.getCharIndex() >= breaker.logical2segment.length) { + return newInfo; + } + + if (hitInfo.getCharIndex() >= 0) { // Don't check for leftmost info + if ( + breaker.logical2segment[newInfo.getCharIndex()] != + breaker.logical2segment[hitInfo.getCharIndex()] + ) { + return newInfo; // We crossed segment boundary + } + } + + TextRunSegment seg = breaker.runSegments.get(breaker.logical2segment[newInfo + .getCharIndex()]); + if (!seg.charHasZeroAdvance(newInfo.getCharIndex())) { + return newInfo; + } + } + + return null; + } + + /** + * Returns the next position to the left from the current caret position + * @param hitInfo - current position + * @return next position to the left + */ + public TextHitInfo getNextLeftHit(TextHitInfo hitInfo) { + checkHit(hitInfo); + int visual = getVisualFromHitInfo(hitInfo); + + if (visual == 0) { + return null; + } + + TextHitInfo newInfo; + + while(visual >= 0) { + visual--; + newInfo = getHitInfoFromVisual(visual); + + if (newInfo.getCharIndex() < 0) { + return newInfo; + } + + // Don't check for rightmost info + if (hitInfo.getCharIndex() < breaker.logical2segment.length) { + if ( + breaker.logical2segment[newInfo.getCharIndex()] != + breaker.logical2segment[hitInfo.getCharIndex()] + ) { + return newInfo; // We crossed segment boundary + } + } + + TextRunSegment seg = breaker.runSegments.get(breaker.logical2segment[newInfo + .getCharIndex()]); + if (!seg.charHasZeroAdvance(newInfo.getCharIndex())) { + return newInfo; + } + } + + return null; + } + + /** + * For each visual caret position there are two hits. For the simple LTR text one is + * a trailing of the previous char and another is the leading of the next char. This + * method returns the opposite hit for the given hit. + * @param hitInfo - given hit + * @return opposite hit + */ + public TextHitInfo getVisualOtherHit(TextHitInfo hitInfo) { + checkHit(hitInfo); + + int idx = hitInfo.getCharIndex(); + + int resIdx; + boolean resIsLeading; + + if (idx >= 0 && idx < breaker.getCharCount()) { // Hit info in the middle + int visual = breaker.getVisualFromLogical(idx); + + // Char is LTR + LEADING info + if (((breaker.getLevel(idx) & 0x1) == 0x0) ^ hitInfo.isLeadingEdge()) { + visual++; + if (visual == breaker.getCharCount()) { + if (breaker.isLTR()) { + resIdx = breaker.getCharCount(); + resIsLeading = true; + } else { + resIdx = -1; + resIsLeading = false; + } + } else { + resIdx = breaker.getLogicalFromVisual(visual); + if ((breaker.getLevel(resIdx) & 0x1) == 0x0) { + resIsLeading = true; + } else { + resIsLeading = false; + } + } + } else { + visual--; + if (visual == -1) { + if (breaker.isLTR()) { + resIdx = -1; + resIsLeading = false; + } else { + resIdx = breaker.getCharCount(); + resIsLeading = true; + } + } else { + resIdx = breaker.getLogicalFromVisual(visual); + if ((breaker.getLevel(resIdx) & 0x1) == 0x0) { + resIsLeading = false; + } else { + resIsLeading = true; + } + } + } + } else if (idx < 0) { // before "start" + if (breaker.isLTR()) { + resIdx = breaker.getLogicalFromVisual(0); + resIsLeading = (breaker.getLevel(resIdx) & 0x1) == 0x0; // LTR char? + } else { + resIdx = breaker.getLogicalFromVisual(breaker.getCharCount() - 1); + resIsLeading = (breaker.getLevel(resIdx) & 0x1) != 0x0; // RTL char? + } + } else { // idx == breaker.getCharCount() + if (breaker.isLTR()) { + resIdx = breaker.getLogicalFromVisual(breaker.getCharCount() - 1); + resIsLeading = (breaker.getLevel(resIdx) & 0x1) != 0x0; // LTR char? + } else { + resIdx = breaker.getLogicalFromVisual(0); + resIsLeading = (breaker.getLevel(resIdx) & 0x1) == 0x0; // RTL char? + } + } + + return resIsLeading ? TextHitInfo.leading(resIdx) : TextHitInfo.trailing(resIdx); + } + + public Line2D getCaretShape(TextHitInfo hitInfo, TextLayout layout) { + return getCaretShape(hitInfo, layout, true, false, null); + } + + /** + * Creates a caret shape. + * @param hitInfo - hit where to place a caret + * @param layout - text layout + * @param useItalic - unused for now, was used to create + * slanted carets for italic text + * @param useBounds - true if the cared should fit into the provided bounds + * @param bounds - bounds for the caret + * @return caret shape + */ + public Line2D getCaretShape( + TextHitInfo hitInfo, TextLayout layout, + boolean useItalic, boolean useBounds, Rectangle2D bounds + ) { + checkHit(hitInfo); + + float x1, x2, y1, y2; + + int charIdx = hitInfo.getCharIndex(); + + if (charIdx >= 0 && charIdx < breaker.getCharCount()) { + TextRunSegment segment = breaker.runSegments.get(breaker.logical2segment[charIdx]); + y1 = segment.metrics.descent; + y2 = - segment.metrics.ascent - segment.metrics.leading; + + x1 = x2 = segment.getCharPosition(charIdx) + (hitInfo.isLeadingEdge() ? + 0 : segment.getCharAdvance(charIdx)); + // Decided that straight cursor looks better even for italic fonts, + // especially combined with highlighting + /* + // Not graphics, need to check italic angle and baseline + if (layout.getBaseline() >= 0) { + if (segment.metrics.italicAngle != 0 && useItalic) { + x1 -= segment.metrics.italicAngle * segment.metrics.descent; + x2 += segment.metrics.italicAngle * + (segment.metrics.ascent + segment.metrics.leading); + + float baselineOffset = + layout.getBaselineOffsets()[layout.getBaseline()]; + y1 += baselineOffset; + y2 += baselineOffset; + } + } + */ + } else { + y1 = layout.getDescent(); + y2 = - layout.getAscent() - layout.getLeading(); + x1 = x2 = ((breaker.getBaseLevel() & 0x1) == 0 ^ charIdx < 0) ? + layout.getAdvance() : 0; + } + + if (useBounds) { + y1 = (float) bounds.getMaxY(); + y2 = (float) bounds.getMinY(); + + if (x2 > bounds.getMaxX()) { + x1 = x2 = (float) bounds.getMaxX(); + } + if (x1 < bounds.getMinX()) { + x1 = x2 = (float) bounds.getMinX(); + } + } + + return new Line2D.Float(x1, y1, x2, y2); + } + + /** + * Creates caret shapes for the specified offset. On the boundaries where + * the text is changing its direction this method may return two shapes + * for the strong and the weak carets, in other cases it would return one. + * @param offset - offset in the text. + * @param bounds - bounds to fit the carets into + * @param policy - caret policy + * @param layout - text layout + * @return one or two caret shapes + */ + public Shape[] getCaretShapes( + int offset, Rectangle2D bounds, + TextLayout.CaretPolicy policy, TextLayout layout + ) { + TextHitInfo hit1 = TextHitInfo.afterOffset(offset); + TextHitInfo hit2 = getVisualOtherHit(hit1); + + Shape caret1 = getCaretShape(hit1, layout); + + if (getVisualFromHitInfo(hit1) == getVisualFromHitInfo(hit2)) { + return new Shape[] {caret1, null}; + } + Shape caret2 = getCaretShape(hit2, layout); + + TextHitInfo strongHit = policy.getStrongCaret(hit1, hit2, layout); + return strongHit.equals(hit1) ? + new Shape[] {caret1, caret2} : + new Shape[] {caret2, caret1}; + } + + /** + * Connects two carets to produce a highlight shape. + * @param caret1 - 1st caret + * @param caret2 - 2nd caret + * @return highlight shape + */ + GeneralPath connectCarets(Line2D caret1, Line2D caret2) { + GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO); + path.moveTo((float) caret1.getX1(), (float) caret1.getY1()); + path.lineTo((float) caret2.getX1(), (float) caret2.getY1()); + path.lineTo((float) caret2.getX2(), (float) caret2.getY2()); + path.lineTo((float) caret1.getX2(), (float) caret1.getY2()); + + path.closePath(); + + return path; + } + + /** + * Creates a highlight shape from given two hits. This shape + * will always be visually contiguous + * @param hit1 - 1st hit + * @param hit2 - 2nd hit + * @param bounds - bounds to fit the shape into + * @param layout - text layout + * @return highlight shape + */ + public Shape getVisualHighlightShape( + TextHitInfo hit1, TextHitInfo hit2, + Rectangle2D bounds, TextLayout layout + ) { + checkHit(hit1); + checkHit(hit2); + + Line2D caret1 = getCaretShape(hit1, layout, false, true, bounds); + Line2D caret2 = getCaretShape(hit2, layout, false, true, bounds); + + return connectCarets(caret1, caret2); + } + + /** + * Suppose that the user visually selected a block of text which has + * several different levels (mixed RTL and LTR), so, in the logical + * representation of the text this selection may be not contigous. + * This methods returns a set of logical ranges for the arbitrary + * visual selection represented by two hits. + * @param hit1 - 1st hit + * @param hit2 - 2nd hit + * @return logical ranges for the selection + */ + public int[] getLogicalRangesForVisualSelection(TextHitInfo hit1, TextHitInfo hit2) { + checkHit(hit1); + checkHit(hit2); + + int visual1 = getVisualFromHitInfo(hit1); + int visual2 = getVisualFromHitInfo(hit2); + + if (visual1 > visual2) { + int tmp = visual2; + visual2 = visual1; + visual1 = tmp; + } + + // Max level is 255, so we don't need more than 512 entries + int results[] = new int[512]; + + int prevLogical, logical, runStart, numRuns = 0; + + logical = runStart = prevLogical = breaker.getLogicalFromVisual(visual1); + + // Get all the runs. We use the fact that direction is constant in all runs. + for (int i=visual1+1; i<=visual2; i++) { + logical = breaker.getLogicalFromVisual(i); + int diff = logical-prevLogical; + + // Start of the next run encountered + if (diff > 1 || diff < -1) { + results[(numRuns)*2] = Math.min(runStart, prevLogical); + results[(numRuns)*2 + 1] = Math.max(runStart, prevLogical); + numRuns++; + runStart = logical; + } + + prevLogical = logical; + } + + // The last unsaved run + results[(numRuns)*2] = Math.min(runStart, logical); + results[(numRuns)*2 + 1] = Math.max(runStart, logical); + numRuns++; + + int retval[] = new int[numRuns*2]; + System.arraycopy(results, 0, retval, 0, numRuns*2); + return retval; + } + + /** + * Creates a highlight shape from given two endpoints in the logical + * representation. This shape is not always visually contiguous + * @param firstEndpoint - 1st logical endpoint + * @param secondEndpoint - 2nd logical endpoint + * @param bounds - bounds to fit the shape into + * @param layout - text layout + * @return highlight shape + */ + public Shape getLogicalHighlightShape( + int firstEndpoint, int secondEndpoint, + Rectangle2D bounds, TextLayout layout + ) { + GeneralPath res = new GeneralPath(); + + for (int i=firstEndpoint; i<=secondEndpoint; i++) { + int endRun = breaker.getLevelRunLimit(i, secondEndpoint); + TextHitInfo hit1 = TextHitInfo.leading(i); + TextHitInfo hit2 = TextHitInfo.trailing(endRun-1); + + Line2D caret1 = getCaretShape(hit1, layout, false, true, bounds); + Line2D caret2 = getCaretShape(hit2, layout, false, true, bounds); + + res.append(connectCarets(caret1, caret2), false); + + i = endRun; + } + + return res; + } +} diff --git a/awt/org/apache/harmony/awt/gl/font/CommonGlyphVector.java b/awt/org/apache/harmony/awt/gl/font/CommonGlyphVector.java new file mode 100644 index 000000000..4040a60d9 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/CommonGlyphVector.java @@ -0,0 +1,954 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.font; + +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphJustificationInfo; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * GlyphVector implementation + */ +public class CommonGlyphVector extends GlyphVector { + + // array of transforms of glyphs in GlyphVector + protected AffineTransform[] glsTransforms; + + // array of chars defined in constructor + public char[] charVector; + + // array of Glyph objects, that describe information about glyphs + public Glyph[] vector; + + // array of default positions of glyphs in GlyphVector + // without applying GlyphVector's transform + float[] defaultPositions; + + // array of logical positions of glyphs in GlyphVector + + float[] logicalPositions; + + // array of visual (real) positions of glyphs in GlyphVector + public float[] visualPositions; + + // FontRenderContext for this vector. + protected FontRenderContext vectorFRC; + + // layout flags mask + protected int layoutFlags = 0; + + // array of cached glyph outlines + protected Shape[] gvShapes; + + FontPeerImpl peer; + + // font corresponding to the GlyphVector + Font font; + + // ascent of the font + float ascent; + + // height of the font + float height; + + // leading of the font + float leading; + + // descent of the font + float descent; + + // transform of the GlyphVector + AffineTransform transform; + + /** + * Creates new CommonGlyphVector object from the specified parameters. + * + * @param chars an array of chars + * @param frc FontRenderContext object + * @param fnt Font object + * @param flags layout flags + */ + @SuppressWarnings("deprecation") + public CommonGlyphVector(char[] chars, FontRenderContext frc, Font fnt, + int flags) { + int len = chars.length; + + this.font = fnt; + this.transform = fnt.getTransform(); + this.peer = (FontPeerImpl) fnt.getPeer(); + + gvShapes = new Shape[len]; + + // !! As pointed in API documentation for the + // getGlyphPosisitions(int index,int numEntries, float[] positionReturn) + // and getGlyphPosition(int index) methods, if the index is equals to + // the number of glyphs the position after the last glyph must be + // returned, thus there are n+1 positions and last (n+1) position + // points to the end of GlyphVector. + + logicalPositions = new float[(len+1)<<1]; + visualPositions = new float[(len+1)<<1]; + defaultPositions = new float[(len+1)<<1]; + + glsTransforms = new AffineTransform[len]; + + this.charVector = chars; + this.vectorFRC = frc; + //LineMetricsImpl lmImpl = (LineMetricsImpl)peer.getLineMetrics(); + + LineMetricsImpl lmImpl = (LineMetricsImpl)fnt.getLineMetrics(String.valueOf(chars), frc); + + this.ascent = lmImpl.getAscent(); + this.height = lmImpl.getHeight(); + this.leading = lmImpl.getLeading(); + this.descent = lmImpl.getDescent(); + this.layoutFlags = flags; + + if ((flags & Font.LAYOUT_RIGHT_TO_LEFT) != 0){ + char vector[] = new char[len]; + for(int i=0; i < len; i++){ + vector[i] = chars[len-i-1]; + } + this.vector = peer.getGlyphs(vector); + + } else { + this.vector = peer.getGlyphs(chars); + } + + this.glsTransforms = new AffineTransform[len]; + + setDefaultPositions(); + performDefaultLayout(); + } + + /** + * Creates new CommonGlyphVector object from the specified parameters. + * Layout flags set to default. + * + * @param chars an array of chars + * @param frc FontRenderContext object + * @param fnt Font object + */ + public CommonGlyphVector(char[] chars, FontRenderContext frc, Font fnt) { + this(chars, frc, fnt, 0); + } + + /** + * Creates new CommonGlyphVector object from the specified parameters. + * Layout flags set to default. + * + * @param str specified string + * @param frc FontRenderContext object + * @param fnt Font object + */ + public CommonGlyphVector(String str, FontRenderContext frc, Font fnt) { + this(str.toCharArray(), frc, fnt, 0); + } + + /** + * Creates new CommonGlyphVector object from the specified parameters. + * + * @param str specified string + * @param frc FontRenderContext object + * @param fnt Font object + * @param flags layout flags + */ + public CommonGlyphVector(String str, FontRenderContext frc, Font fnt, int flags) { + this(str.toCharArray(), frc, fnt, flags); + } + + /** + * Set array of logical positions of the glyphs to + * default with their default advances and height. + */ + void setDefaultPositions(){ + int len = getNumGlyphs(); + + // First [x,y] is set into [0,0] position + // for this reason start index is 1 + for (int i=1; i <= len; i++ ){ + int idx = i << 1; + float advanceX = vector[i-1].getGlyphPointMetrics().getAdvanceX(); + float advanceY = vector[i-1].getGlyphPointMetrics().getAdvanceY(); + + defaultPositions[idx] = defaultPositions[idx-2] + advanceX; + defaultPositions[idx+1] = defaultPositions[idx-1] + advanceY; + + } + transform.transform(defaultPositions, 0, logicalPositions, 0, getNumGlyphs()+1); + + } + + /** + * Returnes the pixel bounds of this GlyphVector rendered at the + * specified x,y location with the given FontRenderContext. + * + * @param frc a FontRenderContext that is used + * @param x specified x coordinate value + * @param y specified y coordinate value + * @return a Rectangle that bounds pixels of this GlyphVector + */ + @Override + public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) { + + double xM, yM, xm, ym; + + double minX = 0; + double minY = 0; + double maxX = 0; + double maxY = 0; + + for (int i = 0; i < this.getNumGlyphs(); i++) { + Rectangle glyphBounds = this.getGlyphPixelBounds(i, frc, 0, 0); + xm = glyphBounds.getMinX(); + ym = glyphBounds.getMinY(); + xM = glyphBounds.getMaxX(); + yM = glyphBounds.getMaxY(); + + if (i == 0) { + minX = xm; + minY = ym; + maxX = xM; + maxY = yM; + } + + if (minX > xm) { + minX = xm; + } + if (minY > ym) { + minY = ym; + } + if (maxX < xM) { + maxX = xM; + } + if (maxY < yM) { + maxY = yM; + } + } + return new Rectangle((int)(minX + x), (int)(minY + y), (int)(maxX - minX), (int)(maxY - minY)); + + } + + /** + * Returns the visual bounds of this GlyphVector. + * The visual bounds is the bounds of the total outline of + * this GlyphVector. + * @return a Rectangle2D that id the visual bounds of this GlyphVector + */ + @Override + public Rectangle2D getVisualBounds() { + float xM, yM, xm, ym; + float minX = 0; + float minY = 0; + float maxX = 0; + float maxY = 0; + boolean firstIteration = true; + + for (int i = 0; i < this.getNumGlyphs(); i++) { + Rectangle2D bounds = this.getGlyphVisualBounds(i).getBounds2D(); + if (bounds.getWidth() == 0){ + continue; + } + xm = (float)bounds.getX(); + ym = (float)bounds.getY(); + + xM = (float)(xm + bounds.getWidth()); + + yM = ym + (float) bounds.getHeight(); + + if (firstIteration) { + minX = xm; + minY = ym; + maxX = xM; + maxY = yM; + firstIteration = false; + } else { + if (minX > xm) { + minX = xm; + } + if (minY > ym) { + minY = ym; + } + if (maxX < xM) { + maxX = xM; + } + if (maxY < yM) { + maxY = yM; + } + + } + } + + return (this.getNumGlyphs() != 0) ? new Rectangle2D.Float(minX, minY, + (maxX - minX), (maxY - minY)) : null; + } + + /** + * Sets new position to the specified glyph. + */ + @Override + public void setGlyphPosition(int glyphIndex, Point2D newPos) { + if ((glyphIndex > vector.length) || (glyphIndex < 0)) { + // awt.43=glyphIndex is out of vector's limits + throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + float x = (float)newPos.getX(); + float y = (float)newPos.getY(); + int index = glyphIndex << 1; + + if ((x != visualPositions[index]) || (y != visualPositions[index + 1])){ + visualPositions[index] = x; + visualPositions[index+1] = y; + layoutFlags = layoutFlags | FLAG_HAS_POSITION_ADJUSTMENTS; + } + + } + + /** + * Returns the position of the specified glyph relative to the origin of + * this GlyphVector + * @return a Point2D that the origin of the glyph with specified index + */ + @Override + public Point2D getGlyphPosition(int glyphIndex) { + if ((glyphIndex > vector.length) || (glyphIndex < 0)) { + // awt.43=glyphIndex is out of vector's limits + throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + int index = glyphIndex << 1; + Point2D pos = new Point2D.Float(visualPositions[index], visualPositions[index+1]); + + // For last position we don't have to transform !! + if(glyphIndex==vector.length){ + return pos; + } + + AffineTransform at = getGlyphTransform(glyphIndex); + if ((at == null) || (at.isIdentity())){ + return pos; + } + + pos.setLocation(pos.getX() + at.getTranslateX(), pos.getY() + at.getTranslateY()); + + return pos; + } + + /** + * Sets new transform to the specified glyph. + * + * @param glyphIndex specified index of the glyph + * @param trans AffineTransform of the glyph with specified index + */ + @Override + public void setGlyphTransform(int glyphIndex, AffineTransform trans) { + if ((glyphIndex >= vector.length) || (glyphIndex < 0)) { + // awt.43=glyphIndex is out of vector's limits + throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + + if ((trans == null) || (trans.isIdentity())) { + glsTransforms[glyphIndex] = null; + } else { + glsTransforms[glyphIndex] = new AffineTransform(trans); + layoutFlags = layoutFlags | FLAG_HAS_TRANSFORMS; + } + } + + /** + * Returns the affine transform of the specified glyph. + * + * @param glyphIndex specified index of the glyph + * @return an AffineTransform of the glyph with specified index + */ + @Override + public AffineTransform getGlyphTransform(int glyphIndex) { + if ((glyphIndex >= this.vector.length) || (glyphIndex < 0)) { + // awt.43=glyphIndex is out of vector's limits + throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + return this.glsTransforms[glyphIndex]; + } + + /** + * Returns the metrics of the specified glyph. + * + * @param glyphIndex specified index of the glyph + */ + @Override + public GlyphMetrics getGlyphMetrics(int glyphIndex) { + + if ((glyphIndex < 0) || ((glyphIndex) >= this.getNumGlyphs())) { + // awt.43=glyphIndex is out of vector's limits + throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + // TODO: is there a sence in GlyphMetrics + // if certain glyph or Font has a transform?? + return this.vector[glyphIndex].getGlyphMetrics(); + } + + /** + * Returns a justification information for the glyph with specified glyph + * index. + * @param glyphIndex index of a glyph which GlyphJustificationInfo is to be + * received + * @return a GlyphJustificationInfo object that contains glyph justification + * properties of the specified glyph + */ + @Override + public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) { + // TODO : Find out the source of Justification info + if (true) { + throw new RuntimeException("Method is not implemented"); //$NON-NLS-1$ + } + return null; + } + + /** + * Returns the FontRenderContext parameter of this GlyphVector. + */ + @Override + public FontRenderContext getFontRenderContext() { + return this.vectorFRC; + } + + /** + * Returns the visual bounds of the specified glyph. + * + * @param glyphIndex specified index of the glyph + */ + @Override + public Shape getGlyphVisualBounds(int glyphIndex) { + if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { + // awt.43=glyphIndex is out of vector's limits + throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + + int idx = glyphIndex << 1; + + AffineTransform fontTransform = this.transform; + double xOffs = fontTransform.getTranslateX(); + double yOffs = fontTransform.getTranslateY(); + + if (vector[glyphIndex].getWidth() == 0){ + return new Rectangle2D.Float((float)xOffs, (float)yOffs, 0, 0); + } + + AffineTransform at = AffineTransform.getTranslateInstance(xOffs, yOffs); + AffineTransform glyphTransform = getGlyphTransform(glyphIndex); + + if (transform.isIdentity() && ((glyphTransform == null) || glyphTransform.isIdentity())){ + Rectangle2D blackBox = vector[glyphIndex].getGlyphMetrics().getBounds2D(); + at.translate(visualPositions[idx], visualPositions[idx+1]); + return(at.createTransformedShape(blackBox)); + } + + GeneralPath shape = (GeneralPath)this.getGlyphOutline(glyphIndex); + shape.transform(at); + return shape.getBounds2D(); + } + + /** + * Returnes the pixel bounds of the specified glyph within GlyphVector + * rendered at the specified x,y location. + * + * @param glyphIndex index of the glyph + * @param frc a FontRenderContext that is used + * @param x specified x coordinate value + * @param y specified y coordinate value + * @return a Rectangle that bounds pixels of the specified glyph + */ + @Override + public Rectangle getGlyphPixelBounds(int glyphIndex, FontRenderContext frc, + float x, float y) { + // TODO : need to be implemented with FontRenderContext + if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { + // awt.43=glyphIndex is out of vector's limits + throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + + int idx = glyphIndex << 1; + + if (vector[glyphIndex].getWidth() == 0){ + AffineTransform fontTransform = this.transform; + double xOffs = x + visualPositions[idx] + fontTransform.getTranslateX(); + double yOffs = y + visualPositions[idx+1] + fontTransform.getTranslateY(); + return new Rectangle((int)xOffs, (int)yOffs, 0, 0); + } + + GeneralPath shape = (GeneralPath)this.getGlyphOutline(glyphIndex); + + AffineTransform at = AffineTransform.getTranslateInstance(x, y); + + if (frc != null){ + at.concatenate(frc.getTransform()); + } + + shape.transform(at); + + Rectangle bounds = shape.getBounds(); + return new Rectangle((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth()-1, (int)bounds.getHeight()-1); + } + + /** + * Returns a Shape that encloses specified glyph. + * + * @param glyphIndex specified index of the glyph + */ + @Override + public Shape getGlyphOutline(int glyphIndex) { + if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { + // awt.43=glyphIndex is out of vector's limits + throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + + if (gvShapes[glyphIndex] == null) { + gvShapes[glyphIndex] = vector[glyphIndex].getShape(); + } + + GeneralPath gp = (GeneralPath)((GeneralPath)gvShapes[glyphIndex]).clone(); + + /* Applying GlyphVector font transform */ + AffineTransform at = (AffineTransform)this.transform.clone(); + + /* Applying Glyph transform */ + AffineTransform glyphAT = getGlyphTransform(glyphIndex); + if (glyphAT != null){ + at.preConcatenate(glyphAT); + } + + int idx = glyphIndex << 1; + + gp.transform(at); + gp.transform(AffineTransform.getTranslateInstance(visualPositions[idx], visualPositions[idx+1])); + return gp; + } + + + /** + * Returns a Shape that is the outline representation of this GlyphVector + * rendered at the specified x,y coordinates. + * + * @param x specified x coordinate value + * @param y specified y coordinate value + * @return a Shape object that is the outline of this GlyphVector + * at the specified coordinates. + */ + @Override + public Shape getOutline(float x, float y) { + GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD); + for (int i = 0; i < this.vector.length; i++) { + GeneralPath outline = (GeneralPath)getGlyphOutline(i); + + /* Applying translation to actual visual bounds */ + outline.transform(AffineTransform.getTranslateInstance(x, y)); + gp.append(outline, false); + } + + return gp; + } + + /** + * Returns a Shape that is the outline representation of this GlyphVector. + * + * @return a Shape object that is the outline of this GlyphVector + */ + @Override + public Shape getOutline() { + return this.getOutline(0, 0); + } + + /** + * Returns an array of glyphcodes for the specified glyphs. + * + * @param beginGlyphIndex the start index + * @param numEntries the number of glyph codes to get + * @param codeReturn the array that receives glyph codes' values + * @return an array that receives glyph codes' values + */ + @Override + public int[] getGlyphCodes(int beginGlyphIndex, int numEntries, + int[] codeReturn) { + + if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > this.getNumGlyphs())) { + // awt.44=beginGlyphIndex is out of vector's range + throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$ + } + + if (numEntries < 0) { + // awt.45=numEntries is out of vector's range + throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ + } + + if (codeReturn == null) { + codeReturn = new int[numEntries]; + } + + for (int i = beginGlyphIndex; i < beginGlyphIndex + numEntries; i++) { + codeReturn[i-beginGlyphIndex] = this.vector[i].getGlyphCode(); + } + + return codeReturn; + } + + /** + * Returns an array of numEntries character indices for the specified glyphs. + * + * @param beginGlyphIndex the start index + * @param numEntries the number of glyph codes to get + * @param codeReturn the array that receives glyph codes' values + * @return an array that receives glyph char indices + */ + @Override + public int[] getGlyphCharIndices(int beginGlyphIndex, int numEntries, + int[] codeReturn) { + if ((beginGlyphIndex < 0) || (beginGlyphIndex >= this.getNumGlyphs())) { + // awt.44=beginGlyphIndex is out of vector's range + throw new IllegalArgumentException(Messages.getString("awt.44")); //$NON-NLS-1$ + } + + if ((numEntries < 0) + || ((numEntries + beginGlyphIndex) > this.getNumGlyphs())) { + // awt.45=numEntries is out of vector's range + throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ + } + + if (codeReturn == null) { + codeReturn = new int[numEntries]; + } + + for (int i = 0; i < numEntries; i++) { + codeReturn[i] = this.getGlyphCharIndex(i + beginGlyphIndex); + } + return codeReturn; + } + + /** + * Returns an array of numEntries glyphs positions from beginGlyphIndex + * glyph in Glyph Vector. + * + * @param beginGlyphIndex the start index + * @param numEntries the number of glyph codes to get + * @param positionReturn the array that receives glyphs' positions + * @return an array of floats that receives glyph char indices + */ + @Override + public float[] getGlyphPositions(int beginGlyphIndex, int numEntries, + float[] positionReturn) { + + int len = (this.getNumGlyphs()+1) << 1; + beginGlyphIndex *= 2; + numEntries *= 2; + + if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > len)) { + // awt.44=beginGlyphIndex is out of vector's range + throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$ + } + + if (numEntries < 0) { + // awt.45=numEntries is out of vector's range + throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ + } + + if (positionReturn == null) { + positionReturn = new float[numEntries]; + } + + System.arraycopy(visualPositions, beginGlyphIndex, positionReturn, 0, numEntries); + + return positionReturn; + } + + /** + * Set numEntries elements of the visualPositions array from beginGlyphIndex + * of numEntries glyphs positions from beginGlyphIndex glyph in Glyph Vector. + * + * @param beginGlyphIndex the start index + * @param numEntries the number of glyph codes to get + * @param setPositions the array of positions to set + */ + public void setGlyphPositions(int beginGlyphIndex, int numEntries, + float[] setPositions) { + + int len = (this.getNumGlyphs()+1) << 1; + beginGlyphIndex *= 2; + numEntries *= 2; + + if ((beginGlyphIndex < 0) || ((numEntries + beginGlyphIndex) > len)) { + // awt.44=beginGlyphIndex is out of vector's range + throw new IndexOutOfBoundsException(Messages.getString("awt.44")); //$NON-NLS-1$ + } + + if (numEntries < 0) { + // awt.45=numEntries is out of vector's range + throw new IllegalArgumentException(Messages.getString("awt.45")); //$NON-NLS-1$ + } + + System.arraycopy(setPositions, 0, visualPositions, beginGlyphIndex, numEntries); + layoutFlags = layoutFlags & FLAG_HAS_POSITION_ADJUSTMENTS; + + } + + /** + * Set elements of the visualPositions array. + * + * @param setPositions the array of positions to set + */ + public void setGlyphPositions(float[] setPositions) { + + int len = (this.getNumGlyphs()+1) << 1; + if (len != setPositions.length){ + // awt.46=length of setPositions array differs from the length of positions array + throw new IllegalArgumentException(Messages.getString("awt.46")); //$NON-NLS-1$ + } + + System.arraycopy(setPositions, 0, visualPositions, 0, len); + layoutFlags = layoutFlags & FLAG_HAS_POSITION_ADJUSTMENTS; + + } + + + /** + * Returns glyph code of the specified glyph. + * + * @param glyphIndex specified index of the glyph + */ + @Override + public int getGlyphCode(int glyphIndex) { + if (glyphIndex >= this.vector.length || glyphIndex < 0) { + // awt.43=glyphIndex is out of vector's limits + throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + return this.vector[glyphIndex].getGlyphCode(); + } + + /** + * Returns character index of the specified glyph. + * + * @param glyphIndex specified index of the glyph + */ + @Override + public int getGlyphCharIndex(int glyphIndex) { + + if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { + // awt.43=glyphIndex is out of vector's limits + throw new IllegalArgumentException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + + if ((this.layoutFlags & Font.LAYOUT_RIGHT_TO_LEFT) != 0) { + return this.charVector.length - glyphIndex - 1; + } + + return glyphIndex; + } + + /** + * Returns a character value of the specified glyph. + * + * @param glyphIndex specified index of the glyph + */ + public char getGlyphChar(int glyphIndex) { + + if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())) { + // awt.43=glyphIndex is out of vector's limits + throw new IllegalArgumentException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + return this.charVector[glyphIndex]; + } + + /** + * Assigns default positions to each glyph in this GlyphVector. + */ + @Override + public void performDefaultLayout() { + + System.arraycopy(logicalPositions, 0, visualPositions, 0, logicalPositions.length); + + // Set position changes flag to zero + clearLayoutFlags(GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS); + } + + /** + * Returns the number of glyphs in this Glyph Vector + */ + @Override + public int getNumGlyphs() { + return vector.length; + } + + /** + * Returns the logical bounds of this GlyphVector + */ + @Override + public Rectangle2D getLogicalBounds(){ + // XXX: for transforms where an angle between basis vectors is not 90 degrees + // Rectanlge2D class doesn't fit as Logical bounds. For this reason we use + // only non-transformed bounds!! + + float x = visualPositions[0]; + float width = visualPositions[visualPositions.length-2]; + + double scaleY = transform.getScaleY(); + + Rectangle2D bounds = new Rectangle2D.Float(x, (float)((-this.ascent-this.leading)*scaleY), width, (float)(this.height*scaleY)); + return bounds; + } + + + /** + * Checks whether given GlyphVector equals to this GlyphVector. + * @param glyphVector GlyphVector object to compare + */ + @Override + public boolean equals(GlyphVector glyphVector){ + if (glyphVector == this){ + return true; + } + + if (glyphVector != null) { + + if (!(glyphVector.getFontRenderContext().equals(this.vectorFRC) && + glyphVector.getFont().equals(this.font))){ + return false; + } + + try { + boolean eq = true; + for (int i = 0; i < getNumGlyphs(); i++) { + + int idx = i*2; + eq = (((CommonGlyphVector)glyphVector).visualPositions[idx] == this.visualPositions[idx]) && + (((CommonGlyphVector)glyphVector).visualPositions[idx+1] == this.visualPositions[idx+1]) && + (glyphVector.getGlyphCharIndex(i) == this.getGlyphCharIndex(i)); + + if (eq){ + AffineTransform trans = glyphVector.getGlyphTransform(i); + if (trans == null){ + eq = (this.glsTransforms[i] == null); + }else{ + eq = this.glsTransforms[i].equals(trans); + } + } + + if (!eq){ + return false; + } + } + + return eq; + } catch (ClassCastException e) { + } + } + + return false; + } + + + /** + * Returns flags describing the state of the GlyphVector. + */ + @Override + public int getLayoutFlags() { + return layoutFlags; + } + + /** + * Returns char with the specified index. + * + * @param index specified index of the char + * + */ + public char getChar(int index) { + return this.charVector[index]; + + } + + /** + * Clear desired flags in layout flags describing the state. + * + * @param clearFlags flags mask to clear + */ + + private void clearLayoutFlags(int clearFlags){ + layoutFlags &= ~clearFlags; + } + + /** + * Returns the logical bounds of the specified glyph within this CommonGlyphVector. + * + * @param glyphIndex index of the glyph to get it's logical bounds + * @return logical bounds of the specified glyph + */ + @Override + public Shape getGlyphLogicalBounds(int glyphIndex){ + if ((glyphIndex < 0) || (glyphIndex >= this.getNumGlyphs())){ + // awt.43=glyphIndex is out of vector's limits + throw new IndexOutOfBoundsException(Messages.getString("awt.43")); //$NON-NLS-1$ + } + Glyph glyph = this.vector[glyphIndex]; + + float x0 = visualPositions[glyphIndex*2]; + float y0 = visualPositions[glyphIndex*2+1]; + float advanceX = glyph.getGlyphPointMetrics().getAdvanceX(); + + GeneralPath gp = new GeneralPath(); + gp.moveTo(0, -ascent - leading); + gp.lineTo(advanceX ,-ascent - leading); + gp.lineTo(advanceX, descent); + gp.lineTo(0, descent); + gp.lineTo(0, -ascent - leading); + gp.closePath(); + + /* Applying GlyphVector font transform */ + AffineTransform at = (AffineTransform)this.transform.clone(); + + /* Applying Glyph transform */ + AffineTransform glyphTransform = getGlyphTransform(glyphIndex); + if (glyphTransform != null){ + at.concatenate(glyphTransform); + } + + /* Applying translation to actual visual bounds */ + at.preConcatenate(AffineTransform.getTranslateInstance(x0, y0)); + gp.transform(at); + return gp; + } + + /** + * Returns the Font parameter of this GlyphVector + */ + @Override + public Font getFont(){ + return this.font; + } + + +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/gl/font/CompositeFont.java b/awt/org/apache/harmony/awt/gl/font/CompositeFont.java new file mode 100644 index 000000000..70cb3341b --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/CompositeFont.java @@ -0,0 +1,486 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.font; + +import java.awt.font.FontRenderContext; +import java.awt.font.LineMetrics; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; + +import org.apache.harmony.awt.gl.font.FontPeerImpl; +import org.apache.harmony.awt.gl.font.FontProperty; + +/** + * CompositeFont class is the implementation of logical font classes. + * Every logical font consists of several physical fonts that described + * in font.properties file according to the face name of this logical font. + */ +public class CompositeFont extends FontPeerImpl{ + + // a number of physical fonts that CompositeFont consist of + int numFonts; + + // font family name + String family; + + // font face name + String face; + + String[] fontNames; + + // an array of font properties applicable to this CompositeFont + FontProperty[] fontProperties; + + // an array of font peers applicable to this CompositeFont + public FontPeerImpl[] fPhysicalFonts; + + // missing glyph code field + int missingGlyphCode = -1; + + // line metrics of this font + LineMetricsImpl nlm = null; + + // cached num glyphs parameter of this font that is the sum of num glyphs of + // font peers composing this font + int cachedNumGlyphs = -1; + /** + * Creates CompositeFont object that is corresponding to the specified logical + * family name. + * + * @param familyName logical family name CompositeFont is to be created from + * @param faceName logical face name CompositeFont is to be created from + * @param _style style of the CompositeFont to be created + * @param _size size of the CompositeFont to be created + * @param fProperties an array of FontProperties describing physical fonts - + * parts of logical font + * @param physFonts an array of physical font peers related to the CompositeFont + * to be created + */ + public CompositeFont(String familyName, String faceName, int _style, int _size, FontProperty[] fProperties, FontPeerImpl[] physFonts){ + this.size = _size; + this.name = faceName; + this.family = familyName; + this.style = _style; + this.face = faceName; + this.psName = faceName; + this.fontProperties = fProperties;// !! Supposed that fProperties parameter != null + fPhysicalFonts = physFonts; + numFonts = fPhysicalFonts.length; + setDefaultLineMetrics("", null); //$NON-NLS-1$ + this.uniformLM = false; + } + + /** + * Returns the index of the FontPeer in array of physical fonts that is applicable + * for the given character. This font has to have the highest priority among fonts + * that can display this character and don't have exclusion range covering + * specified character. If there is no desired fonts -1 is returned. + * + * @param chr specified character + * @return index of the font from the array of physical fonts that will be used + * during processing of the specified character. + */ + public int getCharFontIndex(char chr){ + for (int i = 0; i < numFonts; i++){ + if (fontProperties[i].isCharExcluded(chr)){ + continue; + } + if (fPhysicalFonts[i].canDisplay(chr)){ + return i; + } + } + + return -1; + } + + /** + * Returns the index of the FontPeer in array of physical fonts that is applicable + * for the given character. This font has to have the highest priority among fonts + * that can display this character and don't have exclusion range covering + * specified character. If there is no desired fonts default value is returned. + * + * @param chr specified character + * @param defaultValue default index that is returned if the necessary font couldn't be found. + * @return index of the font from the array of physical fonts that will be used + * during processing of the specified character. + */ + public int getCharFontIndex(char chr, int defaultValue){ + for (int i = 0; i < numFonts; i++){ + if (fontProperties[i].isCharExcluded(chr)){ + continue; + } + if (fPhysicalFonts[i].canDisplay(chr)){ + return i; + } + } + + return defaultValue; + } + + /** + * Returns true if one of the physical fonts composing this font CompositeFont + * can display specified character. + * + * @param chr specified character + */ + @Override + public boolean canDisplay(char chr){ + return (getCharFontIndex(chr) != -1); + } + + /** + * Returns logical ascent (in pixels) + */ + @Override + public int getAscent(){ + return nlm.getLogicalAscent(); + } + + /** + * Returns LineMetrics instance scaled according to the specified transform. + * + * @param str specified String + * @param frc specified FontRenderContext + * @param at specified AffineTransform + */ + @Override + public LineMetrics getLineMetrics(String str, FontRenderContext frc , AffineTransform at){ + LineMetricsImpl lm = (LineMetricsImpl)(this.nlm.clone()); + lm.setNumChars(str.length()); + + if ((at != null) && (!at.isIdentity())){ + lm.scale((float)at.getScaleX(), (float)at.getScaleY()); + } + + return lm; + } + + /** + * Returns cached LineMetrics instance for the null string or creates it if + * it wasn't cached yet. + */ + @Override + public LineMetrics getLineMetrics(){ + if (nlm == null){ + setDefaultLineMetrics("", null); //$NON-NLS-1$ + } + + return this.nlm; + } + + /** + * Creates LineMetrics instance and set cached LineMetrics field to it. + * Created LineMetrics has maximum values of the idividual metrics of all + * composing physical fonts. If there is only one physical font - it's + * LineMetrics object is returned. + * + * @param str specified String + * @param frc specified FontRenderContext + */ + private void setDefaultLineMetrics(String str, FontRenderContext frc){ + LineMetrics lm = fPhysicalFonts[0].getLineMetrics(str, frc, null); + float maxCharWidth = (float)fPhysicalFonts[0].getMaxCharBounds(frc).getWidth(); + + if (numFonts == 1) { + this.nlm = (LineMetricsImpl)lm; + return; + } + + float[] baselineOffsets = lm.getBaselineOffsets(); + int numChars = str.length(); + + // XXX: default value - common for all Fonts + int baseLineIndex = lm.getBaselineIndex(); + + float maxUnderlineThickness = lm.getUnderlineThickness(); + float maxUnderlineOffset = lm.getUnderlineOffset(); + float maxStrikethroughThickness = lm.getStrikethroughThickness(); + float minStrikethroughOffset = lm.getStrikethroughOffset(); + float maxLeading = lm.getLeading(); // External leading + float maxHeight = lm.getHeight(); // Height of the font ( == (ascent + descent + leading)) + float maxAscent = lm.getAscent(); // Ascent of the font + float maxDescent = lm.getDescent(); // Descent of the font + + for (int i = 1; i < numFonts; i++){ + lm = fPhysicalFonts[i].getLineMetrics(str, frc, null); + if (maxUnderlineThickness < lm.getUnderlineThickness()){ + maxUnderlineThickness = lm.getUnderlineThickness(); + } + + if (maxUnderlineOffset < lm.getUnderlineOffset()){ + maxUnderlineOffset = lm.getUnderlineOffset(); + } + + if (maxStrikethroughThickness < lm.getStrikethroughThickness()){ + maxStrikethroughThickness = lm.getStrikethroughThickness(); + } + + if (minStrikethroughOffset > lm.getStrikethroughOffset()){ + minStrikethroughOffset = lm.getStrikethroughOffset(); + } + + if (maxLeading < lm.getLeading()){ + maxLeading = lm.getLeading(); + } + + if (maxAscent < lm.getAscent()){ + maxAscent = lm.getAscent(); + } + + if (maxDescent < lm.getDescent()){ + maxDescent = lm.getDescent(); + } + + float width = (float)fPhysicalFonts[i].getMaxCharBounds(frc).getWidth(); + if(maxCharWidth < width){ + maxCharWidth = width; + } + for (int j =0; j < baselineOffsets.length; j++){ + float[] offsets = lm.getBaselineOffsets(); + if (baselineOffsets[j] > offsets[j]){ + baselineOffsets[j] = offsets[j]; + } + } + + } + maxHeight = maxAscent + maxDescent + maxLeading; + + this.nlm = new LineMetricsImpl( + numChars, + baseLineIndex, + baselineOffsets, + maxUnderlineThickness, + maxUnderlineOffset, + maxStrikethroughThickness, + minStrikethroughOffset, + maxLeading, + maxHeight, + maxAscent, + maxDescent, + maxCharWidth); + + } + + /** + * Returns the number of glyphs in this CompositeFont object. + */ + @Override + public int getNumGlyphs(){ + if (this.cachedNumGlyphs == -1){ + + this.cachedNumGlyphs = 0; + + for (int i = 0; i < numFonts; i++){ + this.cachedNumGlyphs += fPhysicalFonts[i].getNumGlyphs(); + } + } + + return this.cachedNumGlyphs; + } + + /** + * Returns the italic angle of this object. + */ + @Override + public float getItalicAngle(){ + // !! only first physical font used to get this value + return fPhysicalFonts[0].getItalicAngle(); + } + + /** + * Returns rectangle that bounds the specified string in terms of composite line metrics. + * + * @param chars an array of chars + * @param start the initial offset in array of chars + * @param end the end offset in array of chars + * @param frc specified FontRenderContext + */ + public Rectangle2D getStringBounds(char[] chars, int start, int end, FontRenderContext frc){ + + LineMetrics lm = getLineMetrics(); + float minY = -lm.getAscent(); + float minX = 0; + float height = lm.getHeight(); + float width = 0; + + for (int i = start; i < end; i++){ + width += charWidth(chars[i]); + } + + Rectangle2D rect2D = new Rectangle2D.Float(minX, minY, width, height); + return rect2D; + + } + + /** + * Returns maximum rectangle that encloses all maximum char bounds of + * physical fonts composing this CompositeFont. + * + * @param frc specified FontRenderContext + */ + @Override + public Rectangle2D getMaxCharBounds(FontRenderContext frc){ + + Rectangle2D rect2D = fPhysicalFonts[0].getMaxCharBounds(frc); + float minY = (float)rect2D.getY(); + float maxWidth = (float)rect2D.getWidth(); + float maxHeight = (float)rect2D.getHeight(); + if (numFonts == 1){ + return rect2D; + } + + for (int i = 1; i < numFonts; i++){ + if (fPhysicalFonts[i] != null){ + rect2D = fPhysicalFonts[i].getMaxCharBounds(frc); + float y = (float)rect2D.getY(); + float mWidth = (float)rect2D.getWidth(); + float mHeight = (float)rect2D.getHeight(); + if (y < minY){ + minY = y; + } + if (mWidth > maxWidth){ + maxHeight = mWidth; + } + + if (mHeight > maxHeight){ + maxHeight = mHeight; + } + } + } + + rect2D = new Rectangle2D.Float(0, minY, maxWidth, maxHeight); + + return rect2D; + } + + /** + * Returns font name. + */ + @Override + public String getFontName(){ + return face; + } + + /** + * Returns font postscript name. + */ + @Override + public String getPSName(){ + return psName; + } + + /** + * Returns font family name. + */ + @Override + public String getFamily(){ + return family; + } + + /** + * Returns the code of the missing glyph. + */ + @Override + public int getMissingGlyphCode(){ + // !! only first physical font used to get this value + return fPhysicalFonts[0].getMissingGlyphCode(); + } + + /** + * Returns Glyph object corresponding to the specified character. + * + * @param ch specified char + */ + @Override + public Glyph getGlyph(char ch){ + for (int i = 0; i < numFonts; i++){ + if (fontProperties[i].isCharExcluded(ch)){ + continue; + } + + /* Control symbols considered to be supported by the font peer */ + if ((ch < 0x20) || fPhysicalFonts[i].canDisplay(ch)){ + return fPhysicalFonts[i].getGlyph(ch); + } + } + return getDefaultGlyph(); + } + + /** + * Returns width of the char with specified index. + * + * @param ind specified index of the character + */ + @Override + public int charWidth(int ind){ + return charWidth((char)ind); + } + + /** + * Returns width of the specified char. + * + * @param c specified character + */ + @Override + public int charWidth(char c){ + Glyph gl = this.getGlyph(c); + return (int)gl.getGlyphPointMetrics().getAdvanceX(); + } + + /** + * Returns debug information about this class. + */ + @Override + public String toString(){ + return new String(this.getClass().getName() + + "[name=" + this.name + //$NON-NLS-1$ + ",style="+ this.style + //$NON-NLS-1$ + ",fps=" + this.fontProperties + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Returns Glyph object corresponding to the default glyph. + */ + @Override + public Glyph getDefaultGlyph(){ + // !! only first physical font used to get this value + return fPhysicalFonts[0].getDefaultGlyph(); + } + + /** + * Returns FontExtraMetrics object with extra metrics + * related to this CompositeFont. + */ + @Override + public FontExtraMetrics getExtraMetrics(){ + // Returns FontExtraMetrics instanse of the first physical + // Font from the array of fonts. + return fPhysicalFonts[0].getExtraMetrics(); + } + + /** + * Disposes CompositeFont object's resources. + */ + @Override + public void dispose() { + // Nothing to dispose + } +} diff --git a/awt/org/apache/harmony/awt/gl/font/FontExtraMetrics.java b/awt/org/apache/harmony/awt/gl/font/FontExtraMetrics.java new file mode 100644 index 000000000..047ba6d10 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/FontExtraMetrics.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + * + */ +package org.apache.harmony.awt.gl.font; + +/** + * Extra font metrics: sub/superscripts sizes, offsets, average char width. + */ +public class FontExtraMetrics { + + /* !! Subscript/superscript metrics are undefined for Type1. As a possible + * solution we can use values for Type1, that are proportionate to TrueType + * ones: + * SubscriptSizeX == 0.7 * fontSize + * SubscriptSizeY == 0.65 * fontSize + * SubscriptOffsetX == 0; + * SubscriptOffsetY == 0.15 * fontSize; + * SuperscriptSizeX == 0.7 * fontSize + * SuperscriptSizeY == 0.65 * fontSize + * SuperscriptOffsetX == 0; + * SuperscriptOffsetY == 0.45 * fontSize + * + */ + + /* + * The average width of characters in the font. + */ + private float lAverageCharWidth; + + /* + * Horizontal size for subscripts. + */ + private float lSubscriptSizeX; + + /* + * Vertical size for subscripts. + */ + private float lSubscriptSizeY; + + /* + * Horizontal offset for subscripts, the offset from the character origin + * to the origin of the subscript character. + */ + private float lSubscriptOffsetX; + + /* + * Vertical offset for subscripts, the offset from the character origin + * to the origin of the subscript character. + */ + private float lSubscriptOffsetY; + + /* + * Horizontal size for superscripts. + */ + private float lSuperscriptSizeX; + + /* + * Vertical size for superscripts. + */ + private float lSuperscriptSizeY; + + /* + * Horizontal offset for superscripts, the offset from the character + * base line to the base line of the superscript character. + */ + private float lSuperscriptOffsetX; + + /* + * Vertical offset for superscripts, the offset from the character + * base line to the base line of the superscript character. + */ + private float lSuperscriptOffsetY; + + public FontExtraMetrics(){ + // default constructor + } + + public FontExtraMetrics(float[] metrics){ + lAverageCharWidth = metrics[0]; + lSubscriptSizeX = metrics[1]; + lSubscriptSizeY = metrics[2]; + lSubscriptOffsetX = metrics[3]; + lSubscriptOffsetY = metrics[4]; + lSuperscriptSizeX = metrics[5]; + lSuperscriptSizeY = metrics[6]; + lSuperscriptOffsetX = metrics[7]; + lSuperscriptOffsetY = metrics[8]; + } + + public float getAverageCharWidth(){ + return lAverageCharWidth; + } + + public float getSubscriptSizeX(){ + return lSubscriptSizeX; + } + + public float getSubscriptSizeY(){ + return lSubscriptSizeY; + } + + public float getSubscriptOffsetX(){ + return lSubscriptOffsetX; + } + + public float getSubscriptOffsetY(){ + return lSubscriptOffsetY; + } + + public float getSuperscriptSizeX(){ + return lSuperscriptSizeX; + } + + public float getSuperscriptSizeY(){ + return lSuperscriptSizeY; + } + + public float getSuperscriptOffsetX(){ + return lSuperscriptOffsetX; + } + + public float getSuperscriptOffsetY(){ + return lSuperscriptOffsetY; + } + + +} diff --git a/awt/org/apache/harmony/awt/gl/font/FontFinder.java b/awt/org/apache/harmony/awt/gl/font/FontFinder.java new file mode 100644 index 000000000..09bcf5c96 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/FontFinder.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Jul 12, 2005 + */ + +package org.apache.harmony.awt.gl.font; + +import java.awt.Font; +import java.awt.GraphicsEnvironment; +import java.util.List; +import java.util.Map; + +/** + * This class chooses the default font for the given text. + * If it finds the character which current font is unable to display + * it starts the next font run and looks for the font which is able to + * display the current character. It also caches the font mappings + * (index in the array containing all fonts) for the characters, + * using that fact that scripts are mainly contiguous in the UTF-16 encoding + * and there's a high probability that the upper byte will be the same for the + * next character as for the previous. This allows to save the space used for the cache. + */ +public class FontFinder { + private static final float DEFAULT_FONT_SIZE = 12; + + private static final Font fonts[] = + GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + + private static final int NUM_BLOCKS = 256; + private static final int BLOCK_SIZE = 256; + private static final int INDEX_MASK = 0xFF; + private static final int BLOCK_SHIFT = 8; + + // Maps characters into the fonts array + private static final int blocks[][] = new int[NUM_BLOCKS][]; + + /** + * Finds the font which is able to display the given character + * and saves the font mapping for this character + * @param c - character + * @return font + */ + static Font findFontForChar(char c) { + int blockNum = c >> BLOCK_SHIFT; + int index = c & INDEX_MASK; + + if (blocks[blockNum] == null) { + blocks[blockNum] = new int[BLOCK_SIZE]; + } + + if (blocks[blockNum][index] == 0) { + blocks[blockNum][index] = 1; + + for (int i=0; i runStarts, + Map fonts) { + Font prevFont = null; + Font currFont; + for (int i = runStart; i < runLimit; i++) { + currFont = findFontForChar(text[i]); + if (currFont != prevFont) { + prevFont = currFont; + Integer idx = new Integer(i); + fonts.put(idx, currFont); + if (i != runStart) { + runStarts.add(idx); + } + } + } + } +} diff --git a/awt/org/apache/harmony/awt/gl/font/FontManager.java b/awt/org/apache/harmony/awt/gl/font/FontManager.java new file mode 100644 index 000000000..8354e2593 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/FontManager.java @@ -0,0 +1,819 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.font; + +import java.awt.Font; +import java.awt.peer.FontPeer; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Properties; +import java.util.Vector; + +import org.apache.harmony.awt.gl.CommonGraphics2DFactory; +import org.apache.harmony.luni.util.NotImplementedException; + + +public abstract class FontManager { + + //???AWT + boolean NOT_IMP = false; + + /** + * array of font families names + */ + public String[] allFamilies; + + public static final String DEFAULT_NAME = "Default"; /* Default font name */ //$NON-NLS-1$ + public static final String DIALOG_NAME = "Dialog"; /* Dialog font name */ //$NON-NLS-1$ + + /** + * Set of constants applicable to the TrueType 'name' table. + */ + public static final byte FAMILY_NAME_ID = 1; /* Family name identifier */ + public static final byte FONT_NAME_ID = 4; /* Full font name identifier */ + public static final byte POSTSCRIPT_NAME_ID = 6; /* PostScript name identifier */ + public static final short ENGLISH_LANGID = 0x0409; /* English (United States)language identifier */ + + /** + * Set of constants describing font type. + */ + public static final byte FONT_TYPE_TT = 4; /* TrueType type (TRUETYPE_FONTTYPE) */ + public static final byte FONT_TYPE_T1 = 2; /* Type1 type (DEVICE_FONTTYPE) */ + public static final byte FONT_TYPE_UNDEF = 0; /* Undefined type */ + + // logical family types (indices in FontManager.LOGICAL_FONT_NAMES) + static final int DIALOG = 3; // FF_SWISS + static final int SANSSERIF = 1; // FF_SWISS + static final int DIALOGINPUT = 4; // FF_MODERN + static final int MONOSPACED = 2; // FF_MODERN + static final int SERIF = 0; // FF_ROMAN + + + /** + * FontProperty related constants. + */ + public static final String PLATFORM_FONT_NAME = "PlatformFontName"; //$NON-NLS-1$ + public static final String LOGICAL_FONT_NAME = "LogicalFontName"; //$NON-NLS-1$ + public static final String COMPONENT_INDEX = "ComponentIndex"; //$NON-NLS-1$ + public static final String STYLE_INDEX = "StyleIndex"; //$NON-NLS-1$ + + public static final String[] FONT_MAPPING_KEYS = { + "LogicalFontName.StyleName.ComponentIndex", "LogicalFontName.ComponentIndex" //$NON-NLS-1$ //$NON-NLS-2$ + }; + + public static final String FONT_CHARACTER_ENCODING = "fontcharset.LogicalFontName.ComponentIndex"; //$NON-NLS-1$ + + public static final String EXCLUSION_RANGES = "exclusion.LogicalFontName.ComponentIndex"; //$NON-NLS-1$ + + public static final String FONT_FILE_NAME = "filename.PlatformFontName"; //$NON-NLS-1$ + + /** + * Available logical font families names. + */ + public static final String[] LOGICAL_FONT_FAMILIES = { + "Serif", "SansSerif", "Monospaced", "Dialog", "DialogInput" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + }; + + /** + * Available logical font names. + */ + public static final String[] LOGICAL_FONT_NAMES = { + "serif", "serif.plain", "serif.bold", "serif.italic", "serif.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "sansserif", "sansserif.plain", "sansserif.bold", "sansserif.italic", "sansserif.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "monospaced", "monospaced.plain", "monospaced.bold", "monospaced.italic", "monospaced.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "dialog", "dialog.plain", "dialog.bold", "dialog.italic", "dialog.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "dialoginput", "dialoginput.plain", "dialoginput.bold", "dialoginput.italic", "dialoginput.bolditalic" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + }; + + /** + * Available logical font face names. + */ + public static final String[] LOGICAL_FONT_FACES = { + "Serif", "Serif.plain", "Serif.bold", "Serif.italic", "Serif.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "Sansserif", "Sansserif.plain", "Sansserif.bold", "Sansserif.italic", "Sansserif.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "Monospaced", "Monospaced.plain", "Monospaced.bold", "Monospaced.italic", "Monospaced.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "Dialog", "Dialog.plain", "Dialog.bold", "Dialog.italic", "Dialog.bolditalic", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "Dialoginput", "Dialoginput.plain", "Dialoginput.bold", "Dialoginput.italic", "Dialoginput.bolditalic" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + }; + + /** + * Set of font style names. + * Font.getStyle() corresponds to indexes in STYLE_NAMES array. + */ + public static final String[] STYLE_NAMES = { + "plain", "bold", "italic", "bolditalic" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + }; + + /** + * Logical font styles names table where font styles names used + * as the key and the value is the index of this style name. + */ + private static final Hashtable style_keys = new Hashtable(4); + + /** + * Initialize font styles keys table. + */ + static { + for (int i = 0; i < STYLE_NAMES.length; i++){ + style_keys.put(STYLE_NAMES[i], Integer.valueOf(i)); + } + } + + /** + * Return font style from the logical style name. + * + * @param lName style name of the logical face + */ + public static int getLogicalStyle(String lName){ + Integer value = style_keys.get(lName); + return value != null ? value.intValue(): -1; + } + + /** + * Set of possible "os" property values. + */ + public static final String[] OS_VALUES = { + "NT", "98", "2000", "Me", "XP", // For Windows //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "Redhat", "Turbo", "SuSE" // For Linux //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + }; + + /** + * Set of possible font.property file names. + * Language, Country, Encoding, OS, Version should be replaced with + * the values from current configuration. + */ + public static final String[] FP_FILE_NAMES = { + "/lib/font.properties.Language_Country_Encoding.OSVersion", //$NON-NLS-1$ + "/lib/font.properties.Language_Country_Encoding.OS", //$NON-NLS-1$ + "/lib/font.properties.Language_Country_Encoding.Version", //$NON-NLS-1$ + "/lib/font.properties.Language_Country_Encoding", //$NON-NLS-1$ + "/lib/font.properties.Language_Country.OSVersion", //$NON-NLS-1$ + "/lib/font.properties.Language_Country.OS", //$NON-NLS-1$ + "/lib/font.properties.Language_Country.Version", //$NON-NLS-1$ + "/lib/font.properties.Language_Country", //$NON-NLS-1$ + "/lib/font.properties.Language_Encoding.OSVersion", //$NON-NLS-1$ + "/lib/font.properties.Language_Encoding.OS", //$NON-NLS-1$ + "/lib/font.properties.Language_Encoding.Version", //$NON-NLS-1$ + "/lib/font.properties.Language_Encoding", //$NON-NLS-1$ + "/lib/font.properties.Language.OSVersion", //$NON-NLS-1$ + "/lib/font.properties.Language.OS", //$NON-NLS-1$ + "/lib/font.properties.Language.Version", //$NON-NLS-1$ + "/lib/font.properties.Language", //$NON-NLS-1$ + "/lib/font.properties.Encoding.OSVersion", //$NON-NLS-1$ + "/lib/font.properties.Encoding.OS", //$NON-NLS-1$ + "/lib/font.properties.Encoding.Version", //$NON-NLS-1$ + "/lib/font.properties.Encoding", //$NON-NLS-1$ + "/lib/font.properties.OSVersion", //$NON-NLS-1$ + "/lib/font.properties.OS", //$NON-NLS-1$ + "/lib/font.properties.Version", //$NON-NLS-1$ + "/lib/font.properties" //$NON-NLS-1$ + }; + + /** + * Table with all available font properties corresponding + * to the current system configuration. + */ + public Hashtable> fProperties = new Hashtable>(); + + public FontManager(){ + allFamilies = getAllFamilies(); + /* + * Creating and registering shutdown hook to free resources + * before object is destroyed. + */ + //???AWT + //DisposeNativeHook shutdownHook = new DisposeNativeHook(); + //Runtime.getRuntime().addShutdownHook(shutdownHook); + } + + /** + * Maximum number of unreferenced font peers to keep. + */ + public static final int EMPTY_FONTS_CAPACITY = 10; + + /** + * Locale - Language ID hash table. + */ + Hashtable tableLCID = new Hashtable(); + + /** + * Hash table that contains FontPeers instances. + */ + public Hashtable fontsTable = new Hashtable(); + + /** + * ReferenceQueue for HashMapReference objects to check + * if they were collected by garbage collector. + */ + public ReferenceQueue queue = new ReferenceQueue(); + + /** + * Singleton instance + */ + public final static FontManager inst = CommonGraphics2DFactory.inst.getFontManager(); + + /** + * Gets singleton instance of FontManager + * + * @return instance of FontManager implementation + */ + public static FontManager getInstance() { + return inst; + } + + /** + * Returns platform-dependent Font peer created from the specified + * Font object from the table with cached FontPeers instances. + * + * Note, this method checks whether FontPeer with specified parameters + * exists in the table with cached FontPeers' instances. If there is no needed + * instance - it is created and cached. + * + * @param fontName name of the font + * @param _fontStyle style of the font + * @param size font size + * + * @return platform dependent FontPeer implementation created from + * the specified parameters + */ + public FontPeer getFontPeer(String fontName, int _fontStyle, int size) { + updateFontsTable(); + + FontPeer peer = null; + String key; + String name; + int fontStyle = _fontStyle; + + int logicalIndex = getLogicalFaceIndex(fontName); + + if (logicalIndex != -1){ + name = getLogicalFaceFromFont(fontStyle, logicalIndex); + fontStyle = getStyleFromLogicalFace(name); + key = name.concat(String.valueOf(size)); + } else { + name = fontName; + key = name.concat(String.valueOf(fontStyle)). + concat(String.valueOf(size)); + } + + HashMapReference hmr = fontsTable.get(key); + if (hmr != null) { + peer = hmr.get(); + } + + if (peer == null) { + peer = createFontPeer(name, fontStyle, size, logicalIndex); + if (peer == null){ + peer = getFontPeer(DIALOG_NAME, fontStyle, size); + } + fontsTable.put(key, new HashMapReference(key, peer, queue)); + } + + return peer; + } + + /** + * Returns instance of font peer (logical or physical) according to the + * specified parameters. + * + * @param name font face name + * @param style style of the font + * @param size size of the font + * @param logicalIndex index of the logical face name in LOGICAL_FONT_FACES + * array or -1 if desired font peer is not logical. + */ + private FontPeer createFontPeer(String name, int style, int size, int logicalIndex){ + FontPeer peer; + if (logicalIndex != -1){ + peer = createLogicalFontPeer(name, style, size); + }else { + peer = createPhysicalFontPeer(name, style, size); + } + + return peer; + } + + /** + * Returns family name for logical face names as a parameter. + * + * @param faceName logical font face name + */ + public String getFamilyFromLogicalFace(String faceName){ + int pos = faceName.indexOf("."); //$NON-NLS-1$ + if (pos == -1){ + return faceName; + } + + return faceName.substring(0, pos); + } + + /** + * Returns new logical font peer for the parameters specified using font + * properties. + * + * @param faceName face name of the logical font + * @param style style of the font + * @param size font size + * + */ + private FontPeer createLogicalFontPeer(String faceName, int style, int size){ + String family = getFamilyFromLogicalFace(faceName); + FontProperty[] fps = getFontProperties(family.toLowerCase() + "." + style); //$NON-NLS-1$ + if (fps != null){ + int numFonts = fps.length; + FontPeerImpl[] physicalFonts = new FontPeerImpl[numFonts]; + for (int i = 0; i < numFonts; i++){ + FontProperty fp = fps[i]; + + String name = fp.getName(); + int fpStyle = fp.getStyle(); + String key = name.concat(String.valueOf(fpStyle)). + concat(String.valueOf(size)); + + HashMapReference hmr = fontsTable.get(key); + if (hmr != null) { + physicalFonts[i] = (FontPeerImpl)hmr.get(); + } + + if (physicalFonts[i] == null){ + physicalFonts[i] = (FontPeerImpl)createPhysicalFontPeer(name, fpStyle, size); + fontsTable.put(key, new HashMapReference(key, physicalFonts[i], queue)); + } + + if (physicalFonts[i] == null){ + physicalFonts[i] = (FontPeerImpl)getDefaultFont(style, size); + } + } + return new CompositeFont(family, faceName, style, size, fps, physicalFonts); + } + + // if there is no property for this logical font - default font is to be + // created + FontPeerImpl peer = (FontPeerImpl)getDefaultFont(style, size); + + return peer; + } + + /** + * Returns new physical font peer for the parameters specified using font properties + * This method must be overridden by subclasses implementations. + * + * @param faceName face name or family name of the font + * @param style style of the font + * @param size font size + * + */ + public abstract FontPeer createPhysicalFontPeer(String name, int style, int size); + + /** + * Returns default font peer class with "Default" name that is usually + * used when font with specified font names and style doesn't exsist + * on a system. + * + * @param style style of the font + * @param size size of the font + */ + public FontPeer getDefaultFont(int style, int size){ + updateFontsTable(); + + FontPeer peer = null; + String key = DEFAULT_NAME.concat(String.valueOf(style)). + concat(String.valueOf(size)); + + HashMapReference hmr = fontsTable.get(key); + if (hmr != null) { + peer = hmr.get(); + } + + if (peer == null) { + peer = createDefaultFont(style, size); + + ((FontPeerImpl)peer).setFamily(DEFAULT_NAME); + ((FontPeerImpl)peer).setPSName(DEFAULT_NAME); + ((FontPeerImpl)peer).setFontName(DEFAULT_NAME); + + fontsTable.put(key, new HashMapReference(key, peer, queue)); + } + + return peer; + } + + /** + * + * Returns new default font peer with "Default" name for the parameters + * specified. This method must be overridden by subclasses implementations. + * + * @param style style of the font + * @param size size of the font + */ + public abstract FontPeer createDefaultFont(int style, int size); + + /** + * Returns face name of the logical font, which is the result + * of specified font style and face style union. + * + * @param fontStyle specified style of the font + * @param logicalIndex index of the specified face from the + * LOGICAL_FONT_FACES array + * @return resulting face name + */ + public String getLogicalFaceFromFont(int fontStyle, int logicalIndex){ + int style = 0; + String name = LOGICAL_FONT_FACES[logicalIndex]; + int pos = name.indexOf("."); //$NON-NLS-1$ + + if (pos == -1){ + return createLogicalFace(name, fontStyle); + } + + String styleName = name.substring(pos+1); + name = name.substring(0, pos); + + // appending font style to the face style + style = fontStyle | getLogicalStyle(styleName); + + return createLogicalFace(name, style); + } + + /** + * Function returns style value from logical face name. + * + * @param name face name + * @return font style + */ + public int getStyleFromLogicalFace(String name){ + int style; + int pos = name.indexOf("."); //$NON-NLS-1$ + + if (pos == -1){ + return Font.PLAIN; + } + + String styleName = name.substring(pos+1); + + style = getLogicalStyle(styleName); + + return style; + } + + /** + * Returns logical face name corresponding to the logical + * family name and style of the font. + * + * @param family font family + * @param styleIndex index of the style name from the STYLE_NAMES array + */ + public String createLogicalFace(String family, int styleIndex){ + return family + "." + STYLE_NAMES[styleIndex]; //$NON-NLS-1$ + } + + /** + * Return language Id from LCID hash corresponding to the specified locale + * + * @param l specified locale + */ + public Short getLCID(Locale l){ + if (this.tableLCID.size() == 0){ + initLCIDTable(); + } + + return tableLCID.get(l.toString()); + } + + /** + * Platform-dependent LCID table init. + */ + public abstract void initLCIDTable(); + + /** + * Freeing native resources. This hook is used to avoid + * sudden application exit and to free resources created in native code. + */ + private class DisposeNativeHook extends Thread { + + @Override + public void run() { + try{ + /* Disposing native font peer's resources */ + Enumeration kEnum = fontsTable.keys(); + + while(kEnum.hasMoreElements()){ + Object key = kEnum.nextElement(); + HashMapReference hmr = fontsTable.remove(key); + FontPeerImpl delPeer = (FontPeerImpl)hmr.get(); + + if ((delPeer != null) && (delPeer.getClass() != CompositeFont.class)){ + // there's nothing to dispose in CompositeFont objects + delPeer.dispose(); + } + } + } catch (Throwable t){ + throw new RuntimeException(t); + } + } + } + + /** + * Returns File object, created in a directory + * according to the System, where JVM is being ran. + * + * In Linux case we use ".fonts" directory (for fontconfig purpose), + * where font file from the stream will be stored, hence in LinuxFontManager this + * method is overridden. + * In Windows case we use Windows temp directory (default implementation) + * + */ + public File getTempFontFile()throws IOException{ + //???AWT + /* + File fontFile = File.createTempFile("jFont", ".ttf"); //$NON-NLS-1$ //$NON-NLS-2$ + fontFile.deleteOnExit(); + + return fontFile; + */ + if(NOT_IMP) + throw new NotImplementedException("getTempFontFile not Implemented"); + return null; + } + + /** + * Returns File object with font properties. It's name obtained using current + * system configuration properties and locale settings. If no appropriate + * file is found method returns null. + */ + public static File getFontPropertyFile(){ + File file = null; + + String javaHome = System.getProperty("java.home"); //$NON-NLS-1$ + Locale l = Locale.getDefault(); + String language = l.getLanguage(); + String country = l.getCountry(); + String fileEncoding = System.getProperty("file.encoding"); //$NON-NLS-1$ + + String os = System.getProperty("os.name"); //$NON-NLS-1$ + + int i = 0; + + // OS names from system properties don't match + // OS identifiers used in font.property files + for (; i < OS_VALUES.length; i++){ + if (os.endsWith(OS_VALUES[i])){ + os = OS_VALUES[i]; + break; + } + } + + if (i == OS_VALUES.length){ + os = null; + } + + String version = System.getProperty("os.version"); //$NON-NLS-1$ + String pathname; + + for (i = 0; i < FP_FILE_NAMES.length; i++){ + pathname = FP_FILE_NAMES[i]; + if (os != null){ + pathname = pathname.replaceFirst("OS", os); //$NON-NLS-1$ + } + + pathname = javaHome + pathname; + + pathname = pathname.replaceAll("Language", language). //$NON-NLS-1$ + replaceAll("Country", country). //$NON-NLS-1$ + replaceAll("Encoding", fileEncoding). //$NON-NLS-1$ + replaceAll("Version", version); //$NON-NLS-1$ + + file = new File(pathname); + + if (file.exists()){ + break; + } + } + + return file.exists() ? file : null; + } + + /** + * Returns an array of integer range values + * if the parameter exclusionString has format: + * Range + * Range [, exclusionString] + * + * Range: + * Char-Char + * + * Char: + * HexDigit HexDigit HexDigit HexDigit + * + * Method returns null if the specified string is null. + * + * @param exclusionString string parameter in specified format + */ + public static int[] parseIntervals(String exclusionString){ + int[] results = null; + + if (exclusionString == null){ + return null; + } + + String[] intervals = exclusionString.split(","); //$NON-NLS-1$ + + if (intervals != null){ + int num = intervals.length; + if (num > 0){ + results = new int[intervals.length << 1]; + for (int i = 0; i < intervals.length; i++){ + String ranges[] = intervals[i].split("-"); //$NON-NLS-1$ + results[i*2] = Integer.parseInt(ranges[0], 16); + results[i*2+1] = Integer.parseInt(ranges[1], 16); + + } + } + } + return results; + } + + /** + * Returns Properties from the properties file or null if + * there is an error with FileInputStream processing. + * + * @param file File object containing properties + */ + public static Properties getProperties(File file){ + Properties props = null; + FileInputStream fis = null; + try{ + fis = new FileInputStream(file); + props = new Properties(); + props.load(fis); + } catch (Exception e){ + System.out.println(e); + } + return props; + } + + /** + * Returns an array of FontProperties from the properties file + * with the specified property name "logical face.style". E.g. + * "dialog.2" corresponds to the font family Dialog with bold style. + * + * @param fpName key of the font properties in the properties set + */ + public FontProperty[] getFontProperties(String fpName){ + Vector props = fProperties.get(fpName); + + if (props == null){ + return null; + } + + int size = props.size(); + + if (size == 0){ + return null; + } + + FontProperty[] fps = new FontProperty[size]; + for (int i=0; i < fps.length; i++){ + fps[i] = props.elementAt(i); + } + return fps; + } + + /** + * Returns index of the font name in array of font names or -1 if + * this font is not logical. + * + * @param fontName specified font name + */ + public static int getLogicalFaceIndex(String fontName){ + for (int i=0; i { + + /** + * The key for Hashtable. + */ + private final String key; + + /** + * Creates a new soft reference with the key specified and + * adding this reference in the reference queue specified. + * + * @param key the key in Hashtable + * @param value object that corresponds to the key + * @param queue reference queue where reference is to be added + */ + public HashMapReference(final String key, final FontPeer value, + final ReferenceQueue queue) { + super(value, queue); + this.key = key; + } + + /** + * Returns the key that corresponds to the SoftReference instance + * + * @return the key in Hashtable with cached references + */ + public Object getKey() { + return key; + } + } + + /** + * Removes keys from the Hashtable with font peers which corresponding + * HashMapReference objects were garbage collected. + */ + private void updateFontsTable() { + HashMapReference r; + //???AWT + //while ((r = (HashMapReference)queue.poll()) != null) { + // fontsTable.remove(r.getKey()); + //} + } + +} + + diff --git a/awt/org/apache/harmony/awt/gl/font/FontMetricsImpl.java b/awt/org/apache/harmony/awt/gl/font/FontMetricsImpl.java new file mode 100644 index 000000000..778331787 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/FontMetricsImpl.java @@ -0,0 +1,282 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.font; + +import com.android.internal.awt.AndroidGraphics2D; + +import java.awt.Font; +import java.awt.FontMetrics; +//import java.awt.Paint; +import java.awt.geom.AffineTransform; + +import android.graphics.Paint; + +/** + * FontMetrics implementation + */ + +public class FontMetricsImpl extends FontMetrics { + + private static final long serialVersionUID = 844695615201925138L; + + // ascent of the font + private int ascent; + + // descent of the font + private int descent; + + // leading of the font + private int leading; + + // maximum ascent of the font + private int maxAscent; + + // maximum descent of the font + private int maxDescent; + + // maximum advance of the font + private int maxAdvance; + + // array of char advance widths + private int[] widths = new int[256]; + + // font peer corresponding to this FontPeerImpl + private transient FontPeerImpl peer; + + // X scale parameter of the font transform + private float scaleX = 1; + + public AndroidGraphics2D mSg; + + private Font mFn; + + // Y scale parameter of the font transform + private float scaleY = 1; + + /** + * Creates new FontMericsImpl object described by the specified Font. + * + * @param fnt + * the specified Font object + */ + public FontMetricsImpl(Font fnt) { + super(fnt); + this.mFn = fnt; + + mSg = AndroidGraphics2D.getInstance(); + Paint p = mSg.getAndroidPaint(); + + this.ascent = (int)-p.ascent(); + this.descent = (int)p.descent(); + this.leading = p.getFontMetricsInt().leading; + + AffineTransform at = fnt.getTransform(); + if (!at.isIdentity()) { + scaleX = (float) at.getScaleX(); + scaleY = (float) at.getScaleY(); + } + + /* + * metrics[5] - strikethrough thickness

    + * -metrics[6] - strikethrough offset

    + * metrics[7] - maximum char width

    + * metrics[8] - ascent in pixels

    + * metrics[9] - descent in pixles

    + * metrics[10] - external leading in pixels

    + * metrics[11] - underline thickness in pixels

    + * -metrics[12] - underline offset in pixels

    + * metrics[13] - strikethrough thickness in pixels

    + * -metrics[14] - strikethrough offset in pixels

    + * metrics[15] - maximum char width in pixels

    + + * @param _baselineData an array of 3 elements with baseline offsets metrics

    + * _baselineData[0] - roman baseline offset

    + * _baselineData[1] - center baseline offset

    + * _baselineData[2] - hanging baseline offset

    + */ + } + + + /** + * Initialize the array of the first 256 chars' advance widths of the Font + * describing this FontMetricsImpl object. + */ + private void initWidths() { + + this.widths = new int[256]; + for (int chr = 0; chr < 256; chr++) { + widths[chr] = (int) (getFontPeer().charWidth((char) chr) * scaleX); + } + + } + + /** + * Returns the ascent of the Font describing this FontMetricsImpl object. + */ + @Override + public int getAscent() { + return this.ascent; + } + + /** + * Returns the descent of the Font describing this FontMetricsImpl object. + */ + @Override + public int getDescent() { + return this.descent; + } + + /** + * Returns the leading of the Font describing this FontMetricsImpl object. + */ + @Override + public int getLeading() { + return this.leading; + } + + /** + * Returns the advance width of the specified char of the Font describing + * this FontMetricsImpl object. + * + * @param ch + * the char which width is to be returned + * @return the advance width of the specified char of the Font describing + * this FontMetricsImpl object + */ + @Override + public int charWidth(int ch) { + if (ch < 256) { + return widths[ch]; + } + + return getFontPeer().charWidth((char) ch); + } + + /** + * Returns the advance width of the specified char of the Font describing + * this FontMetricsImpl object. + * + * @param ch + * the char which width is to be returned + * @return the advance width of the specified char of the Font describing + * this FontMetricsImpl object + */ + @Override + public int charWidth(char ch) { + if (ch < 256) { + return widths[ch]; + } + return (int) (getFontPeer().charWidth(ch) * scaleX); + } + + /** + * Returns the maximum advance of the Font describing this FontMetricsImpl + * object. + */ + @Override + public int getMaxAdvance() { + return this.maxAdvance; + } + + /** + * Returns the maximum ascent of the Font describing this FontMetricsImpl + * object. + */ + @Override + public int getMaxAscent() { + return this.maxAscent; + } + + /** + * Returns the maximum descent of the Font describing this FontMetricsImpl + * object. + */ + @SuppressWarnings("deprecation") + @Deprecated + @Override + public int getMaxDecent() { + return this.maxDescent; + } + + /** + * Returns the maximum descent of the Font describing this FontMetricsImpl + * object. + */ + @Override + public int getMaxDescent() { + return this.maxDescent; + } + + /** + * Returns the advance widths of the first 256 characters in the Font + * describing this FontMetricsImpl object. + */ + @Override + public int[] getWidths() { + return this.widths; + } + + /** + * Returns the total advance width of the specified string in the metrics of + * the Font describing this FontMetricsImpl object. + * + * @param str + * the String which width is to be measured + * @return the total advance width of the specified string in the metrics of + * the Font describing this FontMetricsImpl object + */ + @Override + public int stringWidth(String str) { + + int width = 0; + char chr; + + for (int i = 0; i < str.length(); i++) { + chr = str.charAt(i); + width += charWidth(chr); + } + return width; + + /* + * float res = 0; int ln = str.length(); char[] c = new char[ln]; float[] f = + * new float[ln]; str.getChars(0, ln, c, 0); mSg.getPaint().getTextWidths(c, 0, + * ln, f); + * + * for(int i = 0; i < f.length; i++) { res += f[i]; } return (int)res; + */ + } + + /** + * Returns FontPeer implementation of the Font describing this + * FontMetricsImpl object. + * + * @return a FontPeer object, that is the platform dependent FontPeer + * implementation for the Font describing this FontMetricsImpl + * object. + */ + @SuppressWarnings("deprecation") + public FontPeerImpl getFontPeer() { + if (peer == null) { + peer = (FontPeerImpl) font.getPeer(); + } + return peer; + } +} diff --git a/awt/org/apache/harmony/awt/gl/font/FontPeerImpl.java b/awt/org/apache/harmony/awt/gl/font/FontPeerImpl.java new file mode 100644 index 000000000..14ff99761 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/FontPeerImpl.java @@ -0,0 +1,499 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.font; + + +import com.android.internal.awt.AndroidGraphics2D; +import com.android.internal.awt.AndroidGraphicsFactory; + +import java.awt.Graphics2D; +import java.awt.Toolkit; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.peer.FontPeer; + +import java.awt.font.FontRenderContext; +import java.awt.font.LineMetrics; +import java.util.ArrayList; +import java.util.Locale; + +import org.apache.harmony.awt.internal.nls.Messages; + +import android.graphics.Paint; + +/** + * Abstract class for platform dependent peer implementation of the Font class. + */ +public abstract class FontPeerImpl implements FontPeer{ + + // ascent of this font peer (in pixels) + int ascent; + + // descent of this font peer (in pixels) + int descent; + + // leading of this font peer (in pixels) + int leading; + + // logical maximum advance of this font peer (in pixels) + int maxAdvance; + + // the height of this font peer + float height; + + // the style of this font peer + int style; + + // the point size of this font peer (in pixels) + int size; + + // the logical hight of this font peer (in pixels) + int logicalHeight; + + // the name of this font peer + String name; + + // family name of this font peer + String fontFamilyName; + + // the Face name of this font peer + String faceName; + + // bounds rectanlge of the largest character in this font peer + Rectangle2D maxCharBounds; + + // italic angle value of this font peer + float italicAngle = 0.0f; + + // the number of glyphs supported by this font peer + int numGlyphs = 0; + + // native font handle + long pFont; + + // cached line metrics object + LineMetricsImpl nlm; + + // the postscript name of this font peer + String psName = null; + + /** + * Default glyph index, that is used, when the desired glyph + * is unsupported in this Font. + */ + public char defaultChar = (char)0xFFFF; + + /** + * Uniform LineMetrics flag, that is false for CompositeFont. + * Default value is true. + */ + boolean uniformLM = true; + + /** + * Flag of the type of this Font that is indicate is the Font + * has TrueType or Type1 type. Default value is FONT_TYPE_UNDEF. + */ + int fontType = FontManager.FONT_TYPE_UNDEF; + + /** + * Flag if this Font was created from stream, + * this parameter used in finilize method. + */ + private boolean createdFromStream = false; + + // temorary Font file name, if this FontPeerImpl was created from InputStream + private String tempFontFileName = null; + + // cached FontExtraMetrics object related to this font peer + FontExtraMetrics extraMetrix = null; + + public abstract FontExtraMetrics getExtraMetrics(); + + /** + * Returns LineMetrics object with specified parameters + * @param str specified String + * @param frc specified render context + * @param at specified affine transform + * @return + */ + public abstract LineMetrics getLineMetrics(String str, FontRenderContext frc, AffineTransform at); + + /** + * Returns postscript name of the font. + */ + public abstract String getPSName(); + + //private Graphics2D g = ((AndroidGraphicsFactory)Toolkit.getDefaultToolkit().getGraphicsFactory()).getGraphics2D(); + //private Graphics2D g = AndroidGraphics2D.getInstance(); + + /** + * Set postscript name of the font to the specified parameter. + */ + public void setPSName(String name){ + this.psName = name; + } + + /** + * Returns code of the missing glyph. + */ + public abstract int getMissingGlyphCode(); + + /** + * Returns Glyph representation of the given char. + * @param ch specified char + */ + public abstract Glyph getGlyph(char ch); + + /** + * Disposes nesessary resources. + */ + public abstract void dispose(); + + /** + * Returns Glyph represeting missing char. + */ + public abstract Glyph getDefaultGlyph(); + + /** + * Returns true if this FontPeerImpl can display the specified char + */ + public abstract boolean canDisplay(char c); + + /** + * Returns family name of the font in specified locale settings. + * @param l specified Locale + */ + public String getFamily(Locale l){ + return this.getFamily(); + } + + /** + * Sets family name of the font in specified locale settings. + */ + public void setFamily(String familyName){ + this.fontFamilyName = familyName; + } + + /** + * Returns face name of the font in specified locale settings. + * @param l specified Locale + */ + public String getFontName(Locale l){ + return this.getFontName(); + } + + /** + * Sets font name of the font in specified locale settings. + */ + public void setFontName(String fontName){ + this.faceName = fontName; + } + + /** + * Returns true, if this font peer was created from InputStream, false otherwise. + * In case of creating fonts from InputStream some font peer implementations + * may need to free temporary resources. + */ + public boolean isCreatedFromStream(){ + return this.createdFromStream; + } + + /** + * Sets createdFromStream flag to the specified parameter. + * If parameter is true it means font peer was created from InputStream. + * + * @param value true, if font peer was created from InputStream + */ + public void setCreatedFromStream(boolean value){ + this.createdFromStream = value; + } + + /** + * Returns font file name of this font. + */ + public String getTempFontFileName(){ + return this.tempFontFileName; + } + + /** + * Sets font file name of this font to the specified one. + * @param value String representing font file name + */ + public void setFontFileName(String value){ + this.tempFontFileName = value; + } + + /** + * Returns the advance width of the specified char of this FontPeerImpl. + * Note, if glyph is absent in the font's glyphset - returned value + * is the advance of the deafualt glyph. For escape-chars returned + * width value is 0. + * + * @param ch the char which width is to be returned + * @return the advance width of the specified char of this FontPeerImpl + */ + public int charWidth(char ch) { + Paint p; + AndroidGraphics2D g = AndroidGraphics2D.getInstance(); + if(g == null) { + throw new RuntimeException("AndroidGraphics2D not instantiated!"); + } + p = ((AndroidGraphics2D)g).getAndroidPaint(); + char[] ca = {ch}; + float[] fa = new float[1]; + p.getTextWidths(ca, 0, 1, fa); + return (int)fa[0]; + } + + /** + * Returns the advance width of the specified char of this FontPeerImpl. + * + * @param ind the char which width is to be returned + * @return the advance width of the specified char of this FontPeerImpl + */ + public int charWidth(int ind) { + return charWidth((char)ind); + } + + /** + * Returns an array of Glyphs that represent characters from the specified + * Unicode range. + * + * @param uFirst start position in Unicode range + * @param uLast end position in Unicode range + * @return + */ + public Glyph[] getGlyphs(char uFirst, char uLast) { + + char i = uFirst; + int len = uLast - uFirst; + ArrayList lst = new ArrayList(len); + + if (size < 0) { + // awt.09=min range bound value is greater than max range bound + throw new IllegalArgumentException(Messages.getString("awt.09")); //$NON-NLS-1$ + } + + while (i < uLast) { + lst.add(this.getGlyph(i)); + } + + return (Glyph[]) lst.toArray(); + } + + /** + * Returns an array of Glyphs representing given array of chars. + * + * @param chars specified array of chars + */ + public Glyph[] getGlyphs(char[] chars) { + if (chars == null){ + return null; + } + + Glyph[] result = new Glyph[chars.length]; + + for (int i = 0; i < chars.length; i++) { + result[i] = this.getGlyph(chars[i]); + } + return result; + } + + /** + * Returns an array of Glyphs representing given string. + * + * @param str specified string + */ + public Glyph[] getGlyphs(String str) { + char[] chars = str.toCharArray(); + return this.getGlyphs(chars); + } + + /** + * Returns family name of this FontPeerImpl. + */ + public String getFamily() { + return fontFamilyName; + } + + /** + * Returns face name of this FontPeerImpl. + */ + public String getFontName() { + if (this.fontType == FontManager.FONT_TYPE_T1){ + return this.fontFamilyName; + } + + return faceName; + } + + /** + * Returns height of this font peer in pixels. + */ + public int getLogicalHeight() { + return logicalHeight; + } + + /** + * Sets height of this font peer in pixels to the given value. + * + * @param newHeight new height in pixels value + */ + public void setLogicalHeight(int newHeight) { + logicalHeight = newHeight; + } + + /** + * Returns font size. + */ + public int getSize() { + return size; + } + + /** + * Returns font style. + */ + public int getStyle() { + return style; + } + + /** + * Returns font name. + */ + public String getName() { + return name; + } + + /** + * Returns the bounds of the largest char in this FontPeerImpl in + * specified render context. + * + * @param frc specified FontRenderContext + */ + public Rectangle2D getMaxCharBounds(FontRenderContext frc) { + return maxCharBounds; + } + + /** + * Returns the number of glyphs in this FontPeerImpl. + */ + public int getNumGlyphs() { + return numGlyphs; + } + + /** + * Returns tangens of the italic angle of this FontPeerImpl. + * If the FontPeerImpl has TrueType font type, italic angle value can be + * calculated as (CharSlopeRun / CharSlopeRise) in terms of GDI. + */ + public float getItalicAngle() { + return italicAngle; + } + + /** + * Returns height of this font peer. + */ + public float getHeight(){ + return height; + } + + /** + * Returns cached LineMetrics object of this font peer. + */ + public LineMetrics getLineMetrics(){ + return nlm; + } + + /** + * Returns native font handle of this font peer. + */ + public long getFontHandle(){ + return pFont; + } + + /** + * Returns ascent of this font peer. + */ + public int getAscent(){ + Paint p; + AndroidGraphics2D g = AndroidGraphics2D.getInstance(); + if(g == null) { + throw new RuntimeException("AndroidGraphics2D not instantiated!"); + } + p = ((AndroidGraphics2D)g).getAndroidPaint(); + return (int)p.ascent(); + //return ascent; + } + + /** + * Returns descent of this font peer. + */ + public int getDescent(){ + return descent; + } + + /** + * Returns leading of this font peer. + */ + public int getLeading(){ + return leading; + } + + /** + * Returns true if this font peer has uniform line metrics. + */ + public boolean hasUniformLineMetrics(){ + return uniformLM; + } + + /** + * Returns type of this font. + * + * @return one of constant font type values. + */ + public int getFontType(){ + return fontType; + } + + /** + * Sets new font type to the font object. + * + * @param newType new type value + */ + public void setFontType(int newType){ + if (newType == FontManager.FONT_TYPE_T1 || newType == FontManager.FONT_TYPE_TT){ + fontType = newType; + } + } + + /** + * Sets new font type to the font object. + * + * @param newType new type value + */ + @Override + protected void finalize() throws Throwable { + super.finalize(); + + dispose(); + } + +} diff --git a/awt/org/apache/harmony/awt/gl/font/FontProperty.java b/awt/org/apache/harmony/awt/gl/font/FontProperty.java new file mode 100644 index 000000000..4eb7cbb0d --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/FontProperty.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ + +package org.apache.harmony.awt.gl.font; + + +/** + * Class containing font property information. This information can be found + * in font.property files. See API documentation, logical fonts description part. + * + */ +public class FontProperty { + + // font file name + String fileName = null; + + // name of the encoding to be used + String encoding = null; + + // array of exclusion ranges (pairs of low and high unicode exclusion bounds) + int[] exclRange = null; + + // font face name + String name = null; + + // font style + int style = -1; + + /** + * Returns font style of this font property. + */ + public int getStyle(){ + return this.style; + } + + /** + * Returns font name of this font property. + */ + public String getName(){ + return this.name; + } + + /** + * Returns encoding used in this font property. + */ + public String getEncoding(){ + return this.encoding; + } + + /** + * Returns an array of exclusion ranges. This array contain pairs of + * low and high bounds of the intervals of characters to ignore in + * total Unicode characters range. + */ + public int[] getExclusionRange(){ + return this.exclRange; + } + + /** + * Returns file name of the font that is described by this font property. + */ + public String getFileName(){ + return this.fileName; + } + + /** + * Returns true if specified character covered by exclusion ranges of this + * font property, false otherwise. + * + * @param ch specified char to check + */ + public boolean isCharExcluded(char ch){ + if (exclRange == null ){ + return false; + } + + for (int i = 0; i < exclRange.length;){ + int lb = exclRange[i++]; + int hb = exclRange[i++]; + + if (ch >= lb && ch <= hb){ + return true; + } + } + + return false; + } +} diff --git a/awt/org/apache/harmony/awt/gl/font/Glyph.java b/awt/org/apache/harmony/awt/gl/font/Glyph.java new file mode 100644 index 000000000..44b8809d8 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/Glyph.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.font; + +import java.awt.Shape; +import java.awt.font.GlyphJustificationInfo; +import java.awt.font.GlyphMetrics; +import java.awt.image.BufferedImage; + +public abstract class Glyph{ + + // character of the glyph + char glChar; + + // precise glyph metrics + GlyphMetrics glMetrics; + + // glyph metrics in pixels + GlyphMetrics glPointMetrics; + + // glyph code of this Glyph + int glCode; + + // justification info of this glyph + GlyphJustificationInfo glJustInfo; + + // native font handle of the font corresponding to this glyph + long pFont; + + // size of the font corresponding to this glyph + int fontSize; + + // bitmap representation of the glyph + byte[] bitmap = null; + + // Buffered image representation of the glyph + BufferedImage image; + + // shape that representing the outline of this glyph + Shape glOutline = null; + + /** + * image bitmap parameters + */ + + // top side bearing + public int bmp_top = 0; + + // left side bearing + public int bmp_left = 0; + + // number of bytes in row + public int bmp_pitch; + + // number of rows + public int bmp_rows; + + // width of the row + public int bmp_width; + + /** + * Retruns handle to Native Font object + */ + public long getPFont(){ + return this.pFont; + } + + /** + * Retruns char value of this glyph object + */ + public char getChar(){ + return glChar; + } + + /** + * Retruns precise width of this glyph object + */ + public int getWidth(){ + return Math.round((float)glMetrics.getBounds2D().getWidth()); + } + + /** + * Retruns precise height of this glyph object + */ + public int getHeight(){ + return Math.round((float)glMetrics.getBounds2D().getHeight()); + } + + /** + * Retruns glyph code of this glyph object + */ + public int getGlyphCode(){ + return glCode; + } + + /** + * Retruns GlyphMetrics of this glyph object with precise metrics. + */ + public GlyphMetrics getGlyphMetrics(){ + return glMetrics; + } + + /** + * Retruns GlyphMetrics of this glyph object in pixels. + */ + public GlyphMetrics getGlyphPointMetrics(){ + return glPointMetrics; + } + + /** + * Retruns GlyphJustificationInfo of this glyph object + */ + public GlyphJustificationInfo getGlyphJustificationInfo(){ + return glJustInfo; + } + + /** + * Sets JustificationInfo of this glyph object + * + * @param newJustInfo GlyphJustificationInfo object to set to the Glyph object + */ + public void setGlyphJustificationInfo(GlyphJustificationInfo newJustInfo){ + this.glJustInfo = newJustInfo; + } + + /** + * Returns an int array of 3 elements, so-called ABC structure that contains + * the width of the character: + * 1st element = left side bearing of the glyph + * 2nd element = width of the glyph + * 3d element = right side bearing of the glyph + */ + public int[] getABC(){ + int[] abc = new int[3]; + abc[0] = (int)glMetrics.getLSB(); + abc[1] = (int)glMetrics.getBounds2D().getWidth(); + abc[2] = (int)glMetrics.getRSB(); + + return abc; + } + + /** + * Sets BufferedImage representation of this glyph to the specified parameter. + * + * @param newImage new BufferedImage object to be set as BufferedImage + * representation. + */ + public void setImage(BufferedImage newImage){ + this.image = newImage; + } + + /** + * Returns true if this Glyph and specified object are equal. + */ + @Override + public boolean equals(Object obj){ + if (obj == this) { + return true; + } + + if (obj != null) { + try { + Glyph gl = (Glyph)obj; + + return ((this.getChar() == gl.getChar()) + && (this.getGlyphMetrics().equals(gl.getGlyphMetrics())) + && (this.getGlyphCode() == gl.getGlyphCode())); + } catch (ClassCastException e) { + } + } + + return false; + } + + /** + * Returns height of the glyph in points. + */ + public int getPointHeight(){ + return (int)glPointMetrics.getBounds2D().getHeight(); + } + + /** + * Returns width of the glyph in points. + */ + public int getPointWidth(){ + return (int)glPointMetrics.getBounds2D().getWidth(); + } + + public Shape getShape(){ + if (glOutline == null){ + glOutline = initOutline(this.glChar); + } + return glOutline; + } + + /** + * Sets BufferedImage representation of this glyph. + */ + public BufferedImage getImage(){ + //!! Implementation classes must override this method + return null; + } + + /** + * Returns array of bytes, representing image of this glyph + */ + public abstract byte[] getBitmap(); + + /** + * Returns shape that represents outline of the specified character. + * + * @param c specified character + */ + public abstract Shape initOutline(char c); + +} + + diff --git a/awt/org/apache/harmony/awt/gl/font/LineMetricsImpl.java b/awt/org/apache/harmony/awt/gl/font/LineMetricsImpl.java new file mode 100644 index 000000000..370146d91 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/LineMetricsImpl.java @@ -0,0 +1,412 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.font; + +import java.awt.font.LineMetrics; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * + * LineMetrics implementation class. + */ + +public class LineMetricsImpl extends LineMetrics implements Cloneable{ + + // array of baseline offsets + float[] baselineOffsets; + + // the number of characters to measure + int numChars; + + // baseline index of the font corresponding to this line metrics + int baseLineIndex; + + // underline thickness + float underlineThickness; + + // underline offset + float underlineOffset; + + // strikethrough thickness + float strikethroughThickness; + + // strikethrough offset + float strikethroughOffset; + + // External leading + float leading; + + // Height of the font ( == (ascent+descent+leading)) + float height; + + // Ascent of the font + float ascent; + + // Descent of the font + float descent; + + // Width of the widest char in the font + float maxCharWidth; + + // underline thickness (in pixels) + int lUnderlineThickness; + + // underline offset (in pixels) + int lUnderlineOffset; + + // strikethrough thickness (in pixels) + int lStrikethroughThickness; + + // strikethrough offset (in pixels) + int lStrikethroughOffset; + + // External leading (in pixels) + int lLeading; + + // Height of the font ( == (ascent+descent+leading)) (in pixels) + int lHeight; + + // Ascent of the font (in pixels) + int lAscent; + + // Descent of the font (in pixels) + int lDescent; + + // Width of the widest char in the font (in pixels) + int lMaxCharWidth; + + // units per EM square in font value + int units_per_EM = 0; + + /** + * Creates LineMetricsImpl object from specified parameters. If baseline data parameter + * is null than {0, (-ascent+descent)/2, -ascent} values are used for baseline offsets. + * + * @param len a number of characters + * @param metrics an array of 16 elements with metrics values that can be + * initialized in native code.

    + * metrics[0] - ascent

    + * metrics[1] - descent

    + * metrics[2] - external leading

    + * metrics[3] - underline thickness

    + * -metrics[4] - underline offset

    + * metrics[5] - strikethrough thickness

    + * -metrics[6] - strikethrough offset

    + * metrics[7] - maximum char width

    + * metrics[8] - ascent in pixels

    + * metrics[9] - descent in pixles

    + * metrics[10] - external leading in pixels

    + * metrics[11] - underline thickness in pixels

    + * -metrics[12] - underline offset in pixels

    + * metrics[13] - strikethrough thickness in pixels

    + * -metrics[14] - strikethrough offset in pixels

    + * metrics[15] - maximum char width in pixels

    + + * @param _baselineData an array of 3 elements with baseline offsets metrics

    + * _baselineData[0] - roman baseline offset

    + * _baselineData[1] - center baseline offset

    + * _baselineData[2] - hanging baseline offset

    + */ + public LineMetricsImpl(int len, float[] metrics, float[] _baselineData){ + numChars = len; + + ascent = metrics[0]; // Ascent of the font + descent = metrics[1]; // Descent of the font + leading = metrics[2]; // External leading + height = metrics[0] + metrics[1] + metrics[2]; // Height of the font ( == (ascent + descent + leading)) + } + + /** + * Creates LineMetricsImpl object from specified parameters. If baseline data parameter + * is null than {0, (-ascent+descent)/2, -ascent} values are used for baseline offsets. + * + * @param _numChars number of chars + * @param _baseLineIndex index of the baseline offset + * @param _baselineOffsets an array of baseline offsets + * @param _underlineThickness underline thickness + * @param _underlineOffset underline offset + * @param _strikethroughThickness strikethrough thickness + * @param _strikethroughOffset strinkethrough offset + * @param _leading leading of the font + * @param _height font height + * @param _ascent ascent of the font + * @param _descent descent of the font + * @param _maxCharWidth max char width + */ + public LineMetricsImpl(int _numChars, int _baseLineIndex, + float[] _baselineOffsets, float _underlineThickness, + float _underlineOffset, float _strikethroughThickness, + float _strikethroughOffset, float _leading, float _height, + float _ascent, float _descent, float _maxCharWidth) { + + numChars = _numChars; + baseLineIndex = _baseLineIndex; + underlineThickness = _underlineThickness; + underlineOffset = _underlineOffset; + strikethroughThickness = _strikethroughThickness; + strikethroughOffset = _strikethroughOffset; + leading = _leading; + height = _height; + ascent = _ascent; + descent = _descent; + baselineOffsets = _baselineOffsets; + lUnderlineThickness = (int) underlineThickness; + lUnderlineOffset = (int) underlineOffset; + lStrikethroughThickness = (int) strikethroughThickness; + lStrikethroughOffset = (int) strikethroughOffset; + lLeading = (int) leading; + lHeight = (int) height; + lAscent = (int) ascent; + lDescent = (int) descent; + maxCharWidth = _maxCharWidth; + } + + public LineMetricsImpl(){ + + } + + /** + * All metrics are scaled according to scaleX and scaleY values. + * This function helps to recompute metrics according to the scale factors + * of desired AffineTransform. + * + * @param scaleX scale X factor + * @param scaleY scale Y factor + */ + public void scale(float scaleX, float scaleY){ + float absScaleX = Math.abs(scaleX); + float absScaleY = Math.abs(scaleY); + + underlineThickness *= absScaleY; + underlineOffset *= scaleY; + strikethroughThickness *= absScaleY; + strikethroughOffset *= scaleY; + leading *= absScaleY; + height *= absScaleY; + ascent *= absScaleY; + descent *= absScaleY; + + if(baselineOffsets == null) { + getBaselineOffsets(); + } + + for (int i=0; i< baselineOffsets.length; i++){ + baselineOffsets[i] *= scaleY; + } + + lUnderlineThickness *= absScaleY; + lUnderlineOffset *= scaleY; + lStrikethroughThickness *= absScaleY; + lStrikethroughOffset *= scaleY; + lLeading *= absScaleY; + lHeight *= absScaleY; + lAscent *= absScaleY; + lDescent *= absScaleY; + maxCharWidth *= absScaleX; + + } + + + /** + * Returns offset of the baseline. + */ + @Override + public float[] getBaselineOffsets() { + // XXX: at the moment there only horizontal metrics are taken into + // account. If there is no baseline information in TrueType font + // file default values used: {0, -ascent, (-ascent+descent)/2} + + return baselineOffsets; + } + + /** + * Returns a number of chars in specified text + */ + @Override + public int getNumChars() { + return numChars; + } + + /** + * Returns index of the baseline, one of predefined constants. + */ + @Override + public int getBaselineIndex() { + // Baseline index is the deafult baseline index value + // taken from the TrueType table "BASE". + return baseLineIndex; + } + + /** + * Returns thickness of the Underline. + */ + @Override + public float getUnderlineThickness() { + return underlineThickness; + } + + /** + * Returns offset of the Underline. + */ + @Override + public float getUnderlineOffset() { + return underlineOffset; + } + + /** + * Returns thickness of the Strikethrough line. + */ + @Override + public float getStrikethroughThickness() { + return strikethroughThickness; + } + + /** + * Returns offset of the Strikethrough line. + */ + @Override + public float getStrikethroughOffset() { + return strikethroughOffset; + } + + /** + * Returns the leading. + */ + @Override + public float getLeading() { + return leading; + } + + /** + * Returns the height of the font. + */ + @Override + public float getHeight() { + //return height; // equals to (ascent + descent + leading); + return ascent + descent + leading; + } + + /** + * Returns the descent. + */ + @Override + public float getDescent() { + return descent; + } + + /** + * Returns the ascent. + */ + @Override + public float getAscent() { + return ascent; + } + + /** + * Returns logical thickness of the Underline. + */ + public int getLogicalUnderlineThickness() { + return lUnderlineThickness; + } + + /** + * Returns logical offset of the Underline. + */ + public int getLogicalUnderlineOffset() { + return lUnderlineOffset; + } + + /** + * Returns logical thickness of the Strikethrough line. + */ + public int getLogicalStrikethroughThickness() { + return lStrikethroughThickness; + } + + /** + * Returns logical offset of the Strikethrough line. + */ + public int getLogicalStrikethroughOffset() { + return lStrikethroughOffset; + } + + /** + * Returns the logical leading. + */ + public int getLogicalLeading() { + return lLeading; + } + + /** + * Returns the logical height of the font. + */ + public int getLogicalHeight() { + return lHeight; // equals to (ascent + descent + leading); + } + + /** + * Returns the logical descent. + */ + public int getLogicalDescent() { + return lDescent; + } + + /** + * Returns the logical ascent. + */ + public int getLogicalAscent() { + return lAscent; + } + + /** + * Returns the logical size of the widest char. + */ + public int getLogicalMaxCharWidth() { + return lMaxCharWidth; + } + + /** + * Returns the size of the widest char. + */ + public float getMaxCharWidth() { + return maxCharWidth; + } + + /** + * Set num chars to the desired value. + * + * @param num specified number of chars + */ + public void setNumChars(int num){ + numChars = num; + } + + @Override + public Object clone(){ + try{ + return super.clone(); + }catch (CloneNotSupportedException e){ + return null; + } + } + +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/gl/font/TextDecorator.java b/awt/org/apache/harmony/awt/gl/font/TextDecorator.java new file mode 100644 index 000000000..81905fd60 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/TextDecorator.java @@ -0,0 +1,433 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/* + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package org.apache.harmony.awt.gl.font; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.font.TextAttribute; +import java.awt.geom.Area; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.text.AttributedCharacterIterator.Attribute; +import java.util.Map; + +/** + * This class is responsible for rendering text decorations like + * underline, strikethrough, text with background, etc. + */ +public class TextDecorator { + private static final TextDecorator inst = new TextDecorator(); + private TextDecorator() {} + static TextDecorator getInstance() { + return inst; + } + + /** + * This class encapsulates a set of decoration attributes for a single text run. + */ + static class Decoration { + private static final BasicStroke UNDERLINE_LOW_ONE_PIXEL_STROKE = + new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10); + + private static final BasicStroke UNDERLINE_LOW_TWO_PIXEL_STROKE = + new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10); + + private static final BasicStroke UNDERLINE_LOW_DOTTED_STROKE = + new BasicStroke( + 1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, + new float[] { 1, 1 }, 0 + ); + + private static final BasicStroke UNDERLINE_LOW_DOTTED_STROKE2 = + new BasicStroke( + 1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, + new float[] { 1, 1 }, 1 + ); + + private static final BasicStroke UNDERLINE_LOW_DASHED_STROKE = + new BasicStroke( + 1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, + new float[] { 4, 4 }, 0 + ); + + boolean ulOn = false; // Have standard underline? + BasicStroke ulStroke; + + BasicStroke imUlStroke; // Stroke for INPUT_METHOD_UNDERLINE + BasicStroke imUlStroke2; // Specially for UNDERLINE_LOW_GRAY + + boolean strikeThrough; + BasicStroke strikeThroughStroke; + + boolean haveStrokes = false; // Strokes already created? + + boolean swapBfFg; + Paint bg; // background color + Paint fg; // foreground color + + Paint graphicsPaint; // Slot for saving current paint + + Decoration( + Integer imUl, + boolean swap, + boolean sth, + Paint bg, Paint fg, + boolean ulOn) { + + if (imUl != null) { + // Determine which stroke to use + if (imUl == TextAttribute.UNDERLINE_LOW_ONE_PIXEL) { + this.imUlStroke = Decoration.UNDERLINE_LOW_ONE_PIXEL_STROKE; + } else if (imUl == TextAttribute.UNDERLINE_LOW_TWO_PIXEL) { + this.imUlStroke = Decoration.UNDERLINE_LOW_TWO_PIXEL_STROKE; + } else if (imUl == TextAttribute.UNDERLINE_LOW_DOTTED) { + this.imUlStroke = Decoration.UNDERLINE_LOW_DOTTED_STROKE; + } else if (imUl == TextAttribute.UNDERLINE_LOW_GRAY) { + this.imUlStroke = Decoration.UNDERLINE_LOW_DOTTED_STROKE; + this.imUlStroke2 = Decoration.UNDERLINE_LOW_DOTTED_STROKE2; + } else if (imUl == TextAttribute.UNDERLINE_LOW_DASHED) { + this.imUlStroke = Decoration.UNDERLINE_LOW_DASHED_STROKE; + } + } + + this.ulOn = ulOn; // Has underline + this.swapBfFg = swap; + this.strikeThrough = sth; + this.bg = bg; + this.fg = fg; + } + + /** + * Creates strokes of proper width according to the info + * stored in the BasicMetrics + * @param metrics - basic metrics + */ + private void getStrokes(BasicMetrics metrics) { + if (!haveStrokes) { + if (strikeThrough) { + strikeThroughStroke = + new BasicStroke( + metrics.strikethroughThickness, + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, + 10 + ); + } + + if (ulOn) { + ulStroke = + new BasicStroke( + metrics.underlineThickness, + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, + 10 + ); + } + + haveStrokes = true; + } + } + } + + /** + * Creates Decoration object from the set of text attributes + * @param attributes - text attributes + * @return Decoration object + */ + static Decoration getDecoration(Map attributes) { + if (attributes == null) { + return null; // It is for plain text + } + + Object underline = attributes.get(TextAttribute.UNDERLINE); + boolean hasStandardUnderline = underline == TextAttribute.UNDERLINE_ON; + + Object imUnderline = attributes.get(TextAttribute.INPUT_METHOD_UNDERLINE); + Integer imUl = (Integer) imUnderline; + + boolean swapBgFg = + TextAttribute.SWAP_COLORS_ON.equals( + attributes.get(TextAttribute.SWAP_COLORS) + ); + + boolean strikeThrough = + TextAttribute.STRIKETHROUGH_ON.equals( + attributes.get(TextAttribute.STRIKETHROUGH) + ); + + Paint fg = (Paint) attributes.get(TextAttribute.FOREGROUND); + Paint bg = (Paint) attributes.get(TextAttribute.BACKGROUND); + + if ( + !hasStandardUnderline && + imUnderline == null && + fg == null && + bg == null && + !swapBgFg && + !strikeThrough + ) { + return null; + } + return new Decoration(imUl, swapBgFg, strikeThrough, bg, fg, hasStandardUnderline); + } + + /** + * Fills the background before drawing if needed. + * + * @param trs - text segment + * @param g2d - graphics to draw to + * @param xOffset - offset in X direction to the upper left corner of the + * layout from the origin of the graphics + * @param yOffset - offset in Y direction to the upper left corner of the + * layout from the origin of the graphics + */ + static void prepareGraphics( + TextRunSegment trs, Graphics2D g2d, + float xOffset, float yOffset + ) { + Decoration d = trs.decoration; + + if (d.fg == null && d.bg == null && d.swapBfFg == false) { + return; // Nothing to do + } + + d.graphicsPaint = g2d.getPaint(); + + if (d.fg == null) { + d.fg = d.graphicsPaint; + } + + if (d.swapBfFg) { + // Fill background area + g2d.setPaint(d.fg); + Rectangle2D bgArea = trs.getLogicalBounds(); + Rectangle2D toFill = + new Rectangle2D.Double( + bgArea.getX() + xOffset, + bgArea.getY() + yOffset, + bgArea.getWidth(), + bgArea.getHeight() + ); + g2d.fill(toFill); + + // Set foreground color + g2d.setPaint(d.bg == null ? Color.WHITE : d.bg); + } else { + if (d.bg != null) { // Fill background area + g2d.setPaint(d.bg); + Rectangle2D bgArea = trs.getLogicalBounds(); + Rectangle2D toFill = + new Rectangle2D.Double( + bgArea.getX() + xOffset, + bgArea.getY() + yOffset, + bgArea.getWidth(), + bgArea.getHeight() + ); + g2d.fill(toFill); + } + + // Set foreground color + g2d.setPaint(d.fg); + } + } + + /** + * Restores the original state of the graphics if needed + * @param d - decoration + * @param g2d - graphics + */ + static void restoreGraphics(Decoration d, Graphics2D g2d) { + if (d.fg == null && d.bg == null && d.swapBfFg == false) { + return; // Nothing to do + } + + g2d.setPaint(d.graphicsPaint); + } + + /** + * Renders the text decorations + * @param trs - text run segment + * @param g2d - graphics to render to + * @param xOffset - offset in X direction to the upper left corner + * of the layout from the origin of the graphics + * @param yOffset - offset in Y direction to the upper left corner + * of the layout from the origin of the graphics + */ + static void drawTextDecorations( + TextRunSegment trs, Graphics2D g2d, + float xOffset, float yOffset + ) { + Decoration d = trs.decoration; + + if (!d.ulOn && d.imUlStroke == null && !d.strikeThrough) { + return; // Nothing to do + } + + float left = xOffset + (float) trs.getLogicalBounds().getMinX(); + float right = xOffset + (float) trs.getLogicalBounds().getMaxX(); + + Stroke savedStroke = g2d.getStroke(); + + d.getStrokes(trs.metrics); + + if (d.strikeThrough) { + float y = trs.y + yOffset + trs.metrics.strikethroughOffset; + g2d.setStroke(d.strikeThroughStroke); + g2d.draw(new Line2D.Float(left, y, right, y)); + } + + if (d.ulOn) { + float y = trs.y + yOffset + trs.metrics.underlineOffset; + g2d.setStroke(d.ulStroke); + g2d.draw(new Line2D.Float(left, y, right, y)); + } + + if (d.imUlStroke != null) { + float y = trs.y + yOffset + trs.metrics.underlineOffset; + g2d.setStroke(d.imUlStroke); + g2d.draw(new Line2D.Float(left, y, right, y)); + if (d.imUlStroke2 != null) { + y++; + g2d.setStroke(d.imUlStroke2); + g2d.draw(new Line2D.Float(left, y, right, y)); + } + } + + g2d.setStroke(savedStroke); + } + + /** + * Extends the visual bounds of the text run segment to + * include text decorations. + * @param trs - text segment + * @param segmentBounds - bounds of the undecorated text + * @param d - decoration + * @return extended bounds + */ + static Rectangle2D extendVisualBounds( + TextRunSegment trs, + Rectangle2D segmentBounds, + Decoration d + ) { + if (d == null) { + return segmentBounds; + } + double minx = segmentBounds.getMinX(); + double miny = segmentBounds.getMinY(); + double maxx = segmentBounds.getMaxX(); + double maxy = segmentBounds.getMaxY(); + + Rectangle2D lb = trs.getLogicalBounds(); + + if (d.swapBfFg || d.bg != null) { + minx = Math.min(lb.getMinX() - trs.x, minx); + miny = Math.min(lb.getMinY() - trs.y, miny); + maxx = Math.max(lb.getMaxX() - trs.x, maxx); + maxy = Math.max(lb.getMaxY() - trs.y, maxy); + } + + if (d.ulOn || d.imUlStroke != null || d.strikeThrough) { + minx = Math.min(lb.getMinX() - trs.x, minx); + maxx = Math.max(lb.getMaxX() - trs.x, maxx); + + d.getStrokes(trs.metrics); + + if (d.ulStroke != null) { + maxy = Math.max( + maxy, + trs.metrics.underlineOffset + + d.ulStroke.getLineWidth() + ); + } + + if (d.imUlStroke != null) { + maxy = Math.max( + maxy, + trs.metrics.underlineOffset + + d.imUlStroke.getLineWidth() + + (d.imUlStroke2 == null ? 0 : d.imUlStroke2.getLineWidth()) + ); + } + } + + return new Rectangle2D.Double(minx, miny, maxx-minx, maxy-miny); + } + + /** + * Extends the outline of the text run segment to + * include text decorations. + * @param trs - text segment + * @param segmentOutline - outline of the undecorated text + * @param d - decoration + * @return extended outline + */ + static Shape extendOutline( + TextRunSegment trs, + Shape segmentOutline, + Decoration d + ) { + if (d == null || !d.ulOn && d.imUlStroke == null && !d.strikeThrough) { + return segmentOutline; // Nothing to do + } + + Area res = new Area(segmentOutline); + + float left = (float) trs.getLogicalBounds().getMinX() - trs.x; + float right = (float) trs.getLogicalBounds().getMaxX() - trs.x; + + d.getStrokes(trs.metrics); + + if (d.strikeThrough) { + float y = trs.metrics.strikethroughOffset; + res.add(new Area(d.strikeThroughStroke.createStrokedShape( + new Line2D.Float(left, y, right, y) + ))); + } + + if (d.ulOn) { + float y = trs.metrics.underlineOffset; + res.add(new Area(d.ulStroke.createStrokedShape( + new Line2D.Float(left, y, right, y) + ))); + } + + if (d.imUlStroke != null) { + float y = trs.metrics.underlineOffset; + res.add(new Area(d.imUlStroke.createStrokedShape( + new Line2D.Float(left, y, right, y) + ))); + + if (d.imUlStroke2 != null) { + y++; + res.add(new Area(d.imUlStroke2.createStrokedShape( + new Line2D.Float(left, y, right, y) + ))); + } + } + + return res; + } +} diff --git a/awt/org/apache/harmony/awt/gl/font/TextMetricsCalculator.java b/awt/org/apache/harmony/awt/gl/font/TextMetricsCalculator.java new file mode 100644 index 000000000..be5762a40 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/TextMetricsCalculator.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + */ + +package org.apache.harmony.awt.gl.font; + +import java.awt.font.LineMetrics; +import java.awt.font.GraphicAttribute; +import java.awt.Font; +import java.util.HashMap; +import java.util.ArrayList; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * This class operates with an arbitrary text string which can include + * any number of style, font and direction runs. It is responsible for computation + * of the text metrics, such as ascent, descent, leading and advance. Actually, + * each text run segment contains logic which allows it to compute its own metrics and + * responsibility of this class is to combine metrics for all segments included in the text, + * managed by the associated TextRunBreaker object. + */ +public class TextMetricsCalculator { + TextRunBreaker breaker; // Associated run breaker + + // Metrics + float ascent = 0; + float descent = 0; + float leading = 0; + float advance = 0; + + private float baselineOffsets[]; + int baselineIndex; + + public TextMetricsCalculator(TextRunBreaker breaker) { + this.breaker = breaker; + checkBaselines(); + } + + /** + * Returns either values cached by checkBaselines method or reasonable + * values for the TOP and BOTTOM alignments. + * @param baselineIndex - baseline index + * @return baseline offset + */ + float getBaselineOffset(int baselineIndex) { + if (baselineIndex >= 0) { + return baselineOffsets[baselineIndex]; + } else if (baselineIndex == GraphicAttribute.BOTTOM_ALIGNMENT) { + return descent; + } else if (baselineIndex == GraphicAttribute.TOP_ALIGNMENT) { + return -ascent; + } else { + // awt.3F=Invalid baseline index + throw new IllegalArgumentException(Messages.getString("awt.3F")); //$NON-NLS-1$ + } + } + + public float[] getBaselineOffsets() { + float ret[] = new float[baselineOffsets.length]; + System.arraycopy(baselineOffsets, 0, ret, 0, baselineOffsets.length); + return ret; + } + + /** + * Take baseline offsets from the first font or graphic attribute + * and normalizes them, than caches the results. + */ + public void checkBaselines() { + // Take baseline offsets of the first font and normalize them + HashMap fonts = breaker.fonts; + + Object val = fonts.get(new Integer(0)); + + if (val instanceof Font) { + Font firstFont = (Font) val; + LineMetrics lm = firstFont.getLineMetrics(breaker.text, 0, 1, breaker.frc); + baselineOffsets = lm.getBaselineOffsets(); + baselineIndex = lm.getBaselineIndex(); + } else if (val instanceof GraphicAttribute) { + // Get first graphic attribute and use it + GraphicAttribute ga = (GraphicAttribute) val; + + int align = ga.getAlignment(); + + if ( + align == GraphicAttribute.TOP_ALIGNMENT || + align == GraphicAttribute.BOTTOM_ALIGNMENT + ) { + baselineIndex = GraphicAttribute.ROMAN_BASELINE; + } else { + baselineIndex = align; + } + + baselineOffsets = new float[3]; + baselineOffsets[0] = 0; + baselineOffsets[1] = (ga.getDescent() - ga.getAscent()) / 2.f; + baselineOffsets[2] = -ga.getAscent(); + } else { // Use defaults - Roman baseline and zero offsets + baselineIndex = GraphicAttribute.ROMAN_BASELINE; + baselineOffsets = new float[3]; + } + + // Normalize offsets if needed + if (baselineOffsets[baselineIndex] != 0) { + float baseOffset = baselineOffsets[baselineIndex]; + for (int i = 0; i < baselineOffsets.length; i++) { + baselineOffsets[i] -= baseOffset; + } + } + } + + /** + * Computes metrics for the text managed by the associated TextRunBreaker + */ + void computeMetrics() { + + ArrayList segments = breaker.runSegments; + + float maxHeight = 0; + float maxHeightLeading = 0; + + for (int i = 0; i < segments.size(); i++) { + TextRunSegment segment = segments.get(i); + BasicMetrics metrics = segment.metrics; + int baseline = metrics.baseLineIndex; + + if (baseline >= 0) { + float baselineOffset = baselineOffsets[metrics.baseLineIndex]; + float fixedDescent = metrics.descent + baselineOffset; + + ascent = Math.max(ascent, metrics.ascent - baselineOffset); + descent = Math.max(descent, fixedDescent); + leading = Math.max(leading, fixedDescent + metrics.leading); + } else { // Position is not fixed by the baseline, need sum of ascent and descent + float height = metrics.ascent + metrics.descent; + + maxHeight = Math.max(maxHeight, height); + maxHeightLeading = Math.max(maxHeightLeading, height + metrics.leading); + } + } + + // Need to increase sizes for graphics? + if (maxHeightLeading != 0) { + descent = Math.max(descent, maxHeight - ascent); + leading = Math.max(leading, maxHeightLeading - ascent); + } + + // Normalize leading + leading -= descent; + + BasicMetrics currMetrics; + float currAdvance = 0; + + for (int i = 0; i < segments.size(); i++) { + TextRunSegment segment = segments.get(breaker.getSegmentFromVisualOrder(i)); + currMetrics = segment.metrics; + + segment.y = getBaselineOffset(currMetrics.baseLineIndex) + + currMetrics.superScriptOffset; + segment.x = currAdvance; + + currAdvance += segment.getAdvance(); + } + + advance = currAdvance; + } + + /** + * Computes metrics and creates BasicMetrics object from them + * @return basic metrics + */ + public BasicMetrics createMetrics() { + computeMetrics(); + return new BasicMetrics(this); + } + + /** + * Corrects advance after justification. Gets BasicMetrics object + * and updates advance stored into it. + * @param metrics - metrics with outdated advance which should be corrected + */ + public void correctAdvance(BasicMetrics metrics) { + ArrayList segments = breaker.runSegments; + TextRunSegment segment = segments.get(breaker + .getSegmentFromVisualOrder(segments.size() - 1)); + + advance = segment.x + segment.getAdvance(); + metrics.advance = advance; + } +} diff --git a/awt/org/apache/harmony/awt/gl/font/TextRunBreaker.java b/awt/org/apache/harmony/awt/gl/font/TextRunBreaker.java new file mode 100644 index 000000000..be606f708 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/TextRunBreaker.java @@ -0,0 +1,861 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + */ + +package org.apache.harmony.awt.gl.font; + + +import java.awt.geom.GeneralPath; +import java.awt.geom.Rectangle2D; +import java.awt.im.InputMethodHighlight; +import java.awt.font.*; +import java.awt.*; +import java.text.AttributedCharacterIterator; +import java.text.Annotation; +import java.text.AttributedCharacterIterator.Attribute; +import java.util.*; + +import org.apache.harmony.awt.gl.font.TextDecorator.Decoration; +import org.apache.harmony.awt.internal.nls.Messages; +import org.apache.harmony.misc.HashCode; +// TODO - bidi not implemented yet + +/** + * This class is responsible for breaking the text into the run segments + * with constant font, style, other text attributes and direction. + * It also stores the created text run segments and covers functionality + * related to the operations on the set of segments, like calculating metrics, + * rendering, justification, hit testing, etc. + */ +public class TextRunBreaker implements Cloneable { + AttributedCharacterIterator aci; + FontRenderContext frc; + + char[] text; + + byte[] levels; + + HashMap fonts; + HashMap decorations; + + // Related to default font substitution + int forcedFontRunStarts[]; + + ArrayList runSegments = new ArrayList(); + + // For fast retrieving of the segment containing + // character with known logical index + int logical2segment[]; + int segment2visual[]; // Visual order of segments TODO - implement + int visual2segment[]; + int logical2visual[]; + int visual2logical[]; + + SegmentsInfo storedSegments; + private boolean haveAllSegments = false; + int segmentsStart, segmentsEnd; + + float justification = 1.0f; + + public TextRunBreaker(AttributedCharacterIterator aci, FontRenderContext frc) { + this.aci = aci; + this.frc = frc; + + segmentsStart = aci.getBeginIndex(); + segmentsEnd = aci.getEndIndex(); + + int len = segmentsEnd - segmentsStart; + text = new char[len]; + aci.setIndex(segmentsEnd); + while (len-- != 0) { // Going in backward direction is faster? Simplier checks here? + text[len] = aci.previous(); + } + + createStyleRuns(); + } + + /** + * Visual order of text segments may differ from the logical order. + * This method calculates visual position of the segment from its logical position. + * @param segmentNum - logical position of the segment + * @return visual position of the segment + */ + int getVisualFromSegmentOrder(int segmentNum) { + return (segment2visual == null) ? segmentNum : segment2visual[segmentNum]; + } + + /** + * Visual order of text segments may differ from the logical order. + * This method calculates logical position of the segment from its visual position. + * @param visual - visual position of the segment + * @return logical position of the segment + */ + int getSegmentFromVisualOrder(int visual) { + return (visual2segment == null) ? visual : visual2segment[visual]; + } + + /** + * Visual order of the characters may differ from the logical order. + * This method calculates visual position of the character from its logical position. + * @param logical - logical position of the character + * @return visual position + */ + int getVisualFromLogical(int logical) { + return (logical2visual == null) ? logical : logical2visual[logical]; + } + + /** + * Visual order of the characters may differ from the logical order. + * This method calculates logical position of the character from its visual position. + * @param visual - visual position + * @return logical position + */ + int getLogicalFromVisual(int visual) { + return (visual2logical == null) ? visual : visual2logical[visual]; + } + + /** + * Calculates the end index of the level run, limited by the given text run. + * @param runStart - run start + * @param runEnd - run end + * @return end index of the level run + */ + int getLevelRunLimit(int runStart, int runEnd) { + if (levels == null) { + return runEnd; + } + int endLevelRun = runStart + 1; + byte level = levels[runStart]; + + while (endLevelRun <= runEnd && levels[endLevelRun] == level) { + endLevelRun++; + } + + return endLevelRun; + } + + /** + * Adds InputMethodHighlight to the attributes + * @param attrs - text attributes + * @return patched text attributes + */ + Map unpackAttributes(Map attrs) { + if (attrs.containsKey(TextAttribute.INPUT_METHOD_HIGHLIGHT)) { + Map styles = null; + + Object val = attrs.get(TextAttribute.INPUT_METHOD_HIGHLIGHT); + + if (val instanceof Annotation) { + val = ((Annotation) val).getValue(); + } + + if (val instanceof InputMethodHighlight) { + InputMethodHighlight ihl = ((InputMethodHighlight) val); + styles = ihl.getStyle(); + + if (styles == null) { + Toolkit tk = Toolkit.getDefaultToolkit(); + styles = tk.mapInputMethodHighlight(ihl); + } + } + + if (styles != null) { + HashMap newAttrs = new HashMap(); + newAttrs.putAll(attrs); + newAttrs.putAll(styles); + return newAttrs; + } + } + + return attrs; + } + + /** + * Breaks the text into separate style runs. + */ + void createStyleRuns() { + // TODO - implement fast and simple case + fonts = new HashMap(); + decorations = new HashMap(); + //// + + ArrayList forcedFontRunStartsList = null; + + Map attributes = null; + + // Check justification attribute + Object val = aci.getAttribute(TextAttribute.JUSTIFICATION); + if (val != null) { + justification = ((Float) val).floatValue(); + } + + for ( + int index = segmentsStart, nextRunStart = segmentsStart; + index < segmentsEnd; + index = nextRunStart, aci.setIndex(index) + ) { + nextRunStart = aci.getRunLimit(); + attributes = unpackAttributes(aci.getAttributes()); + + TextDecorator.Decoration d = TextDecorator.getDecoration(attributes); + decorations.put(new Integer(index), d); + + // Find appropriate font or place GraphicAttribute there + + // 1. Try to pick up CHAR_REPLACEMENT (compatibility) + Font value = (Font)attributes.get(TextAttribute.CHAR_REPLACEMENT); + + if (value == null) { + // 2. Try to Get FONT + value = (Font)attributes.get(TextAttribute.FONT); + + if (value == null) { + // 3. Try to create font from FAMILY + if (attributes.get(TextAttribute.FAMILY) != null) { + value = Font.getFont(attributes); + } + + if (value == null) { + // 4. No attributes found, using default. + if (forcedFontRunStartsList == null) { + forcedFontRunStartsList = new ArrayList(); + } + FontFinder.findFonts( + text, + index, + nextRunStart, + forcedFontRunStartsList, + fonts + ); + value = fonts.get(new Integer(index)); + } + } + } + + fonts.put(new Integer(index), value); + } + + // We have added some default fonts, so we have some extra runs in text + if (forcedFontRunStartsList != null) { + forcedFontRunStarts = new int[forcedFontRunStartsList.size()]; + for (int i=0; i runStart) { + maxPos = Math.min(element, maxPos); + break; + } + } + } + + return Math.min(aci.getRunLimit(), maxPos); + } + + /** + * Creates segments for the text run with + * constant decoration, font and bidi level + * @param runStart - run start + * @param runEnd - run end + */ + public void createSegments(int runStart, int runEnd) { + int endStyleRun, endLevelRun; + + // TODO - update levels + + int pos = runStart, levelPos; + + aci.setIndex(pos); + final int firstRunStart = aci.getRunStart(); + Object tdd = decorations.get(new Integer(firstRunStart)); + Object fontOrGAttr = fonts.get(new Integer(firstRunStart)); + + logical2segment = new int[runEnd - runStart]; + + do { + endStyleRun = getStyleRunLimit(pos, runEnd); + + // runStart can be non-zero, but all arrays will be indexed from 0 + int ajustedPos = pos - runStart; + int ajustedEndStyleRun = endStyleRun - runStart; + levelPos = ajustedPos; + do { + endLevelRun = getLevelRunLimit(levelPos, ajustedEndStyleRun); + + if (fontOrGAttr instanceof GraphicAttribute) { + runSegments.add( + new TextRunSegmentImpl.TextRunSegmentGraphic( + (GraphicAttribute)fontOrGAttr, + endLevelRun - levelPos, + levelPos + runStart) + ); + Arrays.fill(logical2segment, levelPos, endLevelRun, runSegments.size()-1); + } else { + TextRunSegmentImpl.TextSegmentInfo i = + new TextRunSegmentImpl.TextSegmentInfo( + levels == null ? 0 : levels[ajustedPos], + (Font) fontOrGAttr, + frc, + text, + levelPos + runStart, + endLevelRun + runStart + ); + + runSegments.add( + new TextRunSegmentImpl.TextRunSegmentCommon( + i, + (TextDecorator.Decoration) tdd + ) + ); + Arrays.fill(logical2segment, levelPos, endLevelRun, runSegments.size()-1); + } + + levelPos = endLevelRun; + } while (levelPos < ajustedEndStyleRun); + + // Prepare next iteration + pos = endStyleRun; + tdd = decorations.get(new Integer(pos)); + fontOrGAttr = fonts.get(new Integer(pos)); + } while (pos < runEnd); + } + + /** + * Checks if text run segments are up to date and creates the new segments if not. + */ + public void createAllSegments() { + if ( !haveAllSegments && + (logical2segment == null || + logical2segment.length != segmentsEnd - segmentsStart) + ) { // Check if we don't have all segments yet + resetSegments(); + createSegments(segmentsStart, segmentsEnd); + } + + haveAllSegments = true; + } + + /** + * Calculates position where line should be broken without + * taking into account word boundaries. + * @param start - start index + * @param maxAdvance - maximum advance, width of the line + * @return position where to break + */ + public int getLineBreakIndex(int start, float maxAdvance) { + int breakIndex; + TextRunSegment s = null; + + for ( + int segmentIndex = logical2segment[start]; + segmentIndex < runSegments.size(); + segmentIndex++ + ) { + s = runSegments.get(segmentIndex); + breakIndex = s.getCharIndexFromAdvance(maxAdvance, start); + + if (breakIndex < s.getEnd()) { + return breakIndex; + } + maxAdvance -= s.getAdvanceDelta(start, s.getEnd()); + start = s.getEnd(); + } + + return s.getEnd(); + } + + /** + * Inserts character into the managed text. + * @param newParagraph - new character iterator + * @param insertPos - insertion position + */ + public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) { + aci = newParagraph; + + char insChar = aci.setIndex(insertPos); + + Integer key = new Integer(insertPos); + + insertPos -= aci.getBeginIndex(); + + char newText[] = new char[text.length + 1]; + System.arraycopy(text, 0, newText, 0, insertPos); + newText[insertPos] = insChar; + System.arraycopy(text, insertPos, newText, insertPos+1, text.length - insertPos); + text = newText; + + if (aci.getRunStart() == key.intValue() && aci.getRunLimit() == key.intValue() + 1) { + createStyleRuns(); // We have to create one new run, could be optimized + } else { + shiftStyleRuns(key, 1); + } + + resetSegments(); + + segmentsEnd++; + } + + /** + * Deletes character from the managed text. + * @param newParagraph - new character iterator + * @param deletePos - deletion position + */ + public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) { + aci = newParagraph; + + Integer key = new Integer(deletePos); + + deletePos -= aci.getBeginIndex(); + + char newText[] = new char[text.length - 1]; + System.arraycopy(text, 0, newText, 0, deletePos); + System.arraycopy(text, deletePos+1, newText, deletePos, newText.length - deletePos); + text = newText; + + if (fonts.get(key) != null) { + fonts.remove(key); + } + + shiftStyleRuns(key, -1); + + resetSegments(); + + segmentsEnd--; + } + + /** + * Shift all runs after specified position, needed to perfom insertion + * or deletion in the managed text + * @param pos - position where to start + * @param shift - shift, could be negative + */ + private void shiftStyleRuns(Integer pos, final int shift) { + ArrayList keys = new ArrayList(); + + Integer key, oldkey; + for (Iterator it = fonts.keySet().iterator(); it.hasNext(); ) { + oldkey = it.next(); + if (oldkey.intValue() > pos.intValue()) { + keys.add(oldkey); + } + } + + for (int i=0; i(); + logical2segment = null; + segment2visual = null; + visual2segment = null; + levels = null; + haveAllSegments = false; + } + + private class SegmentsInfo { + ArrayList runSegments; + int logical2segment[]; + int segment2visual[]; + int visual2segment[]; + byte levels[]; + int segmentsStart; + int segmentsEnd; + } + + /** + * Saves the internal state of the class + * @param newSegStart - new start index in the text + * @param newSegEnd - new end index in the text + */ + public void pushSegments(int newSegStart, int newSegEnd) { + storedSegments = new SegmentsInfo(); + storedSegments.runSegments = this.runSegments; + storedSegments.logical2segment = this.logical2segment; + storedSegments.segment2visual = this.segment2visual; + storedSegments.visual2segment = this.visual2segment; + storedSegments.levels = this.levels; + storedSegments.segmentsStart = segmentsStart; + storedSegments.segmentsEnd = segmentsEnd; + + resetSegments(); + + segmentsStart = newSegStart; + segmentsEnd = newSegEnd; + } + + /** + * Restores the internal state of the class + */ + public void popSegments() { + if (storedSegments == null) { + return; + } + + this.runSegments = storedSegments.runSegments; + this.logical2segment = storedSegments.logical2segment; + this.segment2visual = storedSegments.segment2visual; + this.visual2segment = storedSegments.visual2segment; + this.levels = storedSegments.levels; + this.segmentsStart = storedSegments.segmentsStart; + this.segmentsEnd = storedSegments.segmentsEnd; + storedSegments = null; + + if (runSegments.size() == 0 && logical2segment == null) { + haveAllSegments = false; + } else { + haveAllSegments = true; + } + } + + @Override + public Object clone() { + try { + TextRunBreaker res = (TextRunBreaker) super.clone(); + res.storedSegments = null; + ArrayList newSegments = new ArrayList(runSegments.size()); + for (int i = 0; i < runSegments.size(); i++) { + TextRunSegment seg = runSegments.get(i); + newSegments.add((TextRunSegment)seg.clone()); + } + res.runSegments = newSegments; + return res; + } catch (CloneNotSupportedException e) { + // awt.3E=Clone not supported + throw new UnsupportedOperationException(Messages.getString("awt.3E")); //$NON-NLS-1$ + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TextRunBreaker)) { + return false; + } + + TextRunBreaker br = (TextRunBreaker) obj; + + if (br.getACI().equals(aci) && br.frc.equals(frc)) { + return true; + } + + return false; + } + + @Override + public int hashCode() { + return HashCode.combine(aci.hashCode(), frc.hashCode()); + } + + /** + * Renders the managed text + * @param g2d - graphics where to render + * @param xOffset - offset in X direction to the upper left corner + * of the layout from the origin of the graphics + * @param yOffset - offset in Y direction to the upper left corner + * of the layout from the origin of the graphics + */ + public void drawSegments(Graphics2D g2d, float xOffset, float yOffset) { + for (int i=0; i= x) || // We are in the segment + (endOfPrevSeg < x && bounds.getMinX() > x)) { // We are somewhere between the segments + return segment.hitTest(x,y); + } + endOfPrevSeg = bounds.getMaxX(); + } + + return isLTR() ? TextHitInfo.trailing(text.length) : TextHitInfo.leading(0); + } + + public float getJustification() { + return justification; + } + + /** + * Calculates position of the last non whitespace character + * in the managed text. + * @return position of the last non whitespace character + */ + public int getLastNonWhitespace() { + int lastNonWhitespace = text.length; + + while (lastNonWhitespace >= 0) { + lastNonWhitespace--; + if (!Character.isWhitespace(text[lastNonWhitespace])) { + break; + } + } + + return lastNonWhitespace; + } + + /** + * Performs justification of the managed text by changing segment positions + * and positions of the glyphs inside of the segments. + * @param gap - amount of space which should be compensated by justification + */ + public void justify(float gap) { + // Ignore trailing logical whitespace + int firstIdx = segmentsStart; + int lastIdx = getLastNonWhitespace() + segmentsStart; + JustificationInfo jInfos[] = new JustificationInfo[5]; + float gapLeft = gap; + + int highestPriority = -1; + // GlyphJustificationInfo.PRIORITY_KASHIDA is 0 + // GlyphJustificationInfo.PRIORITY_NONE is 3 + for (int priority = 0; priority <= GlyphJustificationInfo.PRIORITY_NONE + 1; priority++) { + JustificationInfo jInfo = new JustificationInfo(); + jInfo.lastIdx = lastIdx; + jInfo.firstIdx = firstIdx; + jInfo.grow = gap > 0; + jInfo.gapToFill = gapLeft; + + if (priority <= GlyphJustificationInfo.PRIORITY_NONE) { + jInfo.priority = priority; + } else { + jInfo.priority = highestPriority; // Last pass + } + + for (int i = 0; i < runSegments.size(); i++) { + TextRunSegment segment = runSegments.get(i); + if (segment.getStart() <= lastIdx) { + segment.updateJustificationInfo(jInfo); + } + } + + if (jInfo.priority == highestPriority) { + jInfo.absorb = true; + jInfo.absorbedWeight = jInfo.weight; + } + + if (jInfo.weight != 0) { + if (highestPriority < 0) { + highestPriority = priority; + } + jInfos[priority] = jInfo; + } else { + continue; + } + + gapLeft -= jInfo.growLimit; + + if (((gapLeft > 0) ^ jInfo.grow) || gapLeft == 0) { + gapLeft = 0; + jInfo.gapPerUnit = jInfo.gapToFill/jInfo.weight; + break; + } + jInfo.useLimits = true; + + if (jInfo.absorbedWeight > 0) { + jInfo.absorb = true; + jInfo.absorbedGapPerUnit = + (jInfo.gapToFill-jInfo.growLimit)/jInfo.absorbedWeight; + break; + } + } + + float currJustificationOffset = 0; + for (int i = 0; i < runSegments.size(); i++) { + TextRunSegment segment = + runSegments.get(getSegmentFromVisualOrder(i)); + segment.x += currJustificationOffset; + currJustificationOffset += segment.doJustification(jInfos); + } + + justification = -1; // Make further justification impossible + } + + /** + * This class represents the information collected before the actual + * justification is started and needed to perform the justification. + * This information is closely related to the information stored in the + * GlyphJustificationInfo for the text represented by glyph vectors. + */ + class JustificationInfo { + boolean grow; + boolean absorb = false; + boolean useLimits = false; + int priority = 0; + float weight = 0; + float absorbedWeight = 0; + float growLimit = 0; + + int lastIdx; + int firstIdx; + + float gapToFill; + + float gapPerUnit = 0; // Precalculated value, gapToFill / weight + float absorbedGapPerUnit = 0; // Precalculated value, gapToFill / weight + } +} diff --git a/awt/org/apache/harmony/awt/gl/font/TextRunSegment.java b/awt/org/apache/harmony/awt/gl/font/TextRunSegment.java new file mode 100644 index 000000000..1cd2c055a --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/TextRunSegment.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/* + * @author Oleg V. Khaschansky + * @version $Revision$ + */ + +package org.apache.harmony.awt.gl.font; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.font.TextHitInfo; +import java.awt.geom.Rectangle2D; + +/** + * Abstract class which represents the segment of the text with constant attributes + * running in one direction (i.e. constant level). + */ +public abstract class TextRunSegment implements Cloneable { + float x; // Calculated x location of this segment on the screen + float y; // Calculated y location of this segment on the screen + + BasicMetrics metrics; // Metrics of this text run segment + TextDecorator.Decoration decoration; // Underline, srikethrough, etc. + Rectangle2D logicalBounds = null; // Logical bounding box for the segment + Rectangle2D visualBounds = null; // Visual bounding box for the segment + + /** + * Returns start index of the segment + * @return start index + */ + abstract int getStart(); + + /** + * Returns end index of the segment + * @return end index + */ + abstract int getEnd(); + + /** + * Returns the number of characters in the segment + * @return number of characters + */ + abstract int getLength(); + + /** + * Renders this text run segment + * @param g2d - graphics to render to + * @param xOffset - X offset from the graphics origin to the + * origin of the text layout + * @param yOffset - Y offset from the graphics origin to the + * origin of the text layout + */ + abstract void draw(Graphics2D g2d, float xOffset, float yOffset); + + /** + * Creates black box bounds shape for the specified range + * @param start - range sart + * @param limit - range end + * @return black box bounds shape + */ + abstract Shape getCharsBlackBoxBounds(int start, int limit); + + /** + * Returns the outline shape + * @return outline + */ + abstract Shape getOutline(); + + /** + * Returns visual bounds of this segment + * @return visual bounds + */ + abstract Rectangle2D getVisualBounds(); + + /** + * Returns logical bounds of this segment + * @return logical bounds + */ + abstract Rectangle2D getLogicalBounds(); + + /** + * Calculates advance of the segment + * @return advance + */ + abstract float getAdvance(); + + /** + * Calculates advance delta between two characters + * @param start - 1st position + * @param end - 2nd position + * @return advance increment between specified positions + */ + abstract float getAdvanceDelta(int start, int end); + + /** + * Calculates index of the character which advance is equal to + * the given. If the given advance is greater then the segment + * advance it returns the position after the last character. + * @param advance - given advance + * @param start - character, from which to start measuring advance + * @return character index + */ + abstract int getCharIndexFromAdvance(float advance, int start); + + /** + * Checks if the character doesn't contribute to the text advance + * @param index - character index + * @return true if the character has zero advance + */ + abstract boolean charHasZeroAdvance(int index); + + /** + * Calculates position of the character on the screen + * @param index - character index + * @return X coordinate of the character position + */ + abstract float getCharPosition(int index); + + /** + * Returns the advance of the individual character + * @param index - character index + * @return character advance + */ + abstract float getCharAdvance(int index); + + /** + * Creates text hit info from the hit position + * @param x - X coordinate relative to the origin of the layout + * @param y - Y coordinate relative to the origin of the layout + * @return hit info + */ + abstract TextHitInfo hitTest(float x, float y); + + /** + * Collects justification information into JustificationInfo object + * @param jInfo - JustificationInfo object + */ + abstract void updateJustificationInfo(TextRunBreaker.JustificationInfo jInfo); + + /** + * Performs justification of the segment. + * Updates positions of individual characters. + * @param jInfos - justification information, gathered by the previous passes + * @return amount of growth or shrink of the segment + */ + abstract float doJustification(TextRunBreaker.JustificationInfo jInfos[]); + + @Override + public abstract Object clone(); +} diff --git a/awt/org/apache/harmony/awt/gl/font/TextRunSegmentImpl.java b/awt/org/apache/harmony/awt/gl/font/TextRunSegmentImpl.java new file mode 100644 index 000000000..0ec2d05da --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/font/TextRunSegmentImpl.java @@ -0,0 +1,979 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + */ + +package org.apache.harmony.awt.gl.font; + +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.Rectangle2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +// XXX - TODO - bidi not implemented yet +//import java.text.Bidi; +import java.util.Arrays; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * Date: Apr 25, 2005 + * Time: 4:33:18 PM + * + * This class contains the implementation of the behavior of the + * text run segment with constant text attributes and direction. + */ +public class TextRunSegmentImpl { + + /** + * This class contains basic information required for creation + * of the glyph-based text run segment. + */ + public static class TextSegmentInfo { + // XXX - TODO - bidi not implemented yet + //Bidi bidi; + + Font font; + FontRenderContext frc; + + char text[]; + + int start; + int end; + int length; + + int flags = 0; + + byte level = 0; + + TextSegmentInfo( + byte level, + Font font, FontRenderContext frc, + char text[], int start, int end + ) { + this.font = font; + this.frc = frc; + this.text = text; + this.start = start; + this.end = end; + this.level = level; + length = end - start; + } + } + + /** + * This class represents a simple text segment backed by the glyph vector + */ + public static class TextRunSegmentCommon extends TextRunSegment { + TextSegmentInfo info; + private GlyphVector gv; + private float advanceIncrements[]; + private int char2glyph[]; + private GlyphJustificationInfo gjis[]; // Glyph justification info + + TextRunSegmentCommon(TextSegmentInfo i, TextDecorator.Decoration d) { + // XXX - todo - check support bidi + i.flags &= ~0x09; // Clear bidi flags + + if ((i.level & 0x1) != 0) { + i.flags |= Font.LAYOUT_RIGHT_TO_LEFT; + } + + info = i; + this.decoration = d; + + LineMetrics lm = i.font.getLineMetrics(i.text, i.start, i.end, i.frc); + this.metrics = new BasicMetrics(lm, i.font); + + if (lm.getNumChars() != i.length) { // XXX todo - This should be handled + // awt.41=Font returned unsupported type of line metrics. This case is known, but not supported yet. + throw new UnsupportedOperationException( + Messages.getString("awt.41")); //$NON-NLS-1$ + } + } + + @Override + public Object clone() { + return new TextRunSegmentCommon(info, decoration); + } + + /** + * Creates glyph vector from the managed text if needed + * @return glyph vector + */ + private GlyphVector getGlyphVector() { + if (gv==null) { + gv = info.font.layoutGlyphVector( + info.frc, + info.text, + info.start, + info.end - info.start, // NOTE: This parameter violates + // spec, it is count, + // not limit as spec states + info.flags + ); + } + + return gv; + } + + /** + * Renders this text run segment + * @param g2d - graphics to render to + * @param xOffset - X offset from the graphics origin to the + * origin of the text layout + * @param yOffset - Y offset from the graphics origin to the + * origin of the text layout + */ + @Override + void draw(Graphics2D g2d, float xOffset, float yOffset) { + if (decoration == null) { + g2d.drawGlyphVector(getGlyphVector(), xOffset + x, yOffset + y); + } else { + TextDecorator.prepareGraphics(this, g2d, xOffset, yOffset); + g2d.drawGlyphVector(getGlyphVector(), xOffset + x, yOffset + y); + TextDecorator.drawTextDecorations(this, g2d, xOffset, yOffset); + TextDecorator.restoreGraphics(decoration, g2d); + } + } + + /** + * Returns visual bounds of this segment + * @return visual bounds + */ + @Override + Rectangle2D getVisualBounds() { + if (visualBounds == null) { + visualBounds = + TextDecorator.extendVisualBounds( + this, + getGlyphVector().getVisualBounds(), + decoration + ); + + visualBounds.setRect( + x + visualBounds.getX(), + y + visualBounds.getY(), + visualBounds.getWidth(), + visualBounds.getHeight() + ); + } + + return (Rectangle2D) visualBounds.clone(); + } + + /** + * Returns logical bounds of this segment + * @return logical bounds + */ + @Override + Rectangle2D getLogicalBounds() { + if (logicalBounds == null) { + logicalBounds = getGlyphVector().getLogicalBounds(); + + logicalBounds.setRect( + x + logicalBounds.getX(), + y + logicalBounds.getY(), + logicalBounds.getWidth(), + logicalBounds.getHeight() + ); + } + + return (Rectangle2D) logicalBounds.clone(); + } + + @Override + float getAdvance() { + return (float) getLogicalBounds().getWidth(); + } + + /** + * Attemts to map each character to the corresponding advance increment + */ + void initAdvanceMapping() { + GlyphVector gv = getGlyphVector(); + int charIndicies[] = gv.getGlyphCharIndices(0, gv.getNumGlyphs(), null); + advanceIncrements = new float[info.length]; + + for (int i=0; i info.length) { + end = info.length; + } + + float sum = 0; + for (int i=start; i info.length) { + limit = info.length; + } + + GeneralPath result = new GeneralPath(); + + int glyphIndex = 0; + + for (int i=start; i info.length) { + index = info.length; + } + + float result = 0; + + int glyphIndex = getChar2Glyph()[index]; + result = (float) getGlyphVector().getGlyphPosition(glyphIndex).getX(); + + // Shift to the segment's coordinates + result += x; + + return result; + } + + /** + * Returns the advance of the individual character + * @param index - character index + * @return character advance + */ + @Override + float getCharAdvance(int index) { + if (advanceIncrements == null) { + initAdvanceMapping(); + } + + return advanceIncrements[index - this.getStart()]; + } + + /** + * Returns the outline shape + * @return outline + */ + @Override + Shape getOutline() { + AffineTransform t = AffineTransform.getTranslateInstance(x, y); + return t.createTransformedShape( + TextDecorator.extendOutline( + this, + getGlyphVector().getOutline(), + decoration + ) + ); + } + + /** + * Checks if the character doesn't contribute to the text advance + * @param index - character index + * @return true if the character has zero advance + */ + @Override + boolean charHasZeroAdvance(int index) { + if (advanceIncrements == null) { + initAdvanceMapping(); + } + + return advanceIncrements[index - this.getStart()] == 0; + } + + /** + * Creates text hit info from the hit position + * @param hitX - X coordinate relative to the origin of the layout + * @param hitY - Y coordinate relative to the origin of the layout + * @return hit info + */ + @Override + TextHitInfo hitTest(float hitX, float hitY) { + hitX -= x; + + float glyphPositions[] = + getGlyphVector().getGlyphPositions(0, info.length+1, null); + + int glyphIdx; + boolean leading = false; + for (glyphIdx = 1; glyphIdx <= info.length; glyphIdx++) { + if (glyphPositions[(glyphIdx)*2] >= hitX) { + float advance = + glyphPositions[(glyphIdx)*2] - glyphPositions[(glyphIdx-1)*2]; + leading = glyphPositions[(glyphIdx-1)*2] + advance/2 > hitX ? true : false; + glyphIdx--; + break; + } + } + + if (glyphIdx == info.length) { + glyphIdx--; + } + + int charIdx = getGlyphVector().getGlyphCharIndex(glyphIdx); + + return (leading) ^ ((info.level & 0x1) == 0x1)? + TextHitInfo.leading(charIdx + info.start) : + TextHitInfo.trailing(charIdx + info.start); + } + + /** + * Collects GlyphJustificationInfo objects from the glyph vector + * @return array of all GlyphJustificationInfo objects + */ + private GlyphJustificationInfo[] getGlyphJustificationInfos() { + if (gjis == null) { + GlyphVector gv = getGlyphVector(); + int nGlyphs = gv.getNumGlyphs(); + int charIndicies[] = gv.getGlyphCharIndices(0, nGlyphs, null); + gjis = new GlyphJustificationInfo[nGlyphs]; + + // Patch: temporary patch, getGlyphJustificationInfo is not implemented + float fontSize = info.font.getSize2D(); + GlyphJustificationInfo defaultInfo = + new GlyphJustificationInfo( + 0, // weight + false, GlyphJustificationInfo.PRIORITY_NONE, 0, 0, // grow + false, GlyphJustificationInfo.PRIORITY_NONE, 0, 0); // shrink + GlyphJustificationInfo spaceInfo = new GlyphJustificationInfo( + fontSize, // weight + true, GlyphJustificationInfo.PRIORITY_WHITESPACE, 0, fontSize, // grow + true, GlyphJustificationInfo.PRIORITY_WHITESPACE, 0, fontSize); // shrink + + //////// + // Temporary patch, getGlyphJustificationInfo is not implemented + for (int i = 0; i < nGlyphs; i++) { + //gjis[i] = getGlyphVector().getGlyphJustificationInfo(i); + + char c = info.text[charIndicies[i] + info.start]; + if (Character.isWhitespace(c)) { + gjis[i] = spaceInfo; + } else { + gjis[i] = defaultInfo; + } + // End patch + } + } + + return gjis; + } + + /** + * Collects justification information into JustificationInfo object + * @param jInfo - JustificationInfo object + */ + @Override + void updateJustificationInfo(TextRunBreaker.JustificationInfo jInfo) { + int lastChar = Math.min(jInfo.lastIdx, info.end) - info.start; + boolean haveFirst = info.start <= jInfo.firstIdx; + boolean haveLast = info.end >= (jInfo.lastIdx + 1); + + int prevGlyphIdx = -1; + int currGlyphIdx; + + if (jInfo.grow) { // Check how much we can grow/shrink on current priority level + for (int i=0; i 0 ? jInfos[lastPriority] : null; + + boolean haveFirst = info.start <= firstInfo.firstIdx; + boolean haveLast = info.end >= (firstInfo.lastIdx + 1); + + // Here we suppose that GLYPHS are ordered LEFT TO RIGHT + int firstGlyph = haveFirst ? + getChar2Glyph()[firstInfo.firstIdx - info.start] : + getChar2Glyph()[0]; + + int lastGlyph = haveLast ? + getChar2Glyph()[firstInfo.lastIdx - info.start] : + getChar2Glyph()[info.length - 1]; + if (haveLast) { + lastGlyph--; + } + + TextRunBreaker.JustificationInfo currInfo; + float glyphOffset = 0; + float positionIncrement = 0; + float sideIncrement = 0; + + if (haveFirst) { // Don't add padding before first char + GlyphJustificationInfo gji = getGlyphJustificationInfos()[firstGlyph]; + currInfo = jInfos[gji.growPriority]; + if (currInfo != null) { + if (currInfo.useLimits) { + if (currInfo.absorb) { + glyphOffset += gji.weight * currInfo.absorbedGapPerUnit; + } else if ( + lastInfo != null && + lastInfo.priority == currInfo.priority + ) { + glyphOffset += gji.weight * lastInfo.absorbedGapPerUnit; + } + glyphOffset += + firstInfo.grow ? + gji.growRightLimit : + -gji.shrinkRightLimit; + } else { + glyphOffset += gji.weight * currInfo.gapPerUnit; + } + } + + firstGlyph++; + } + + if (firstInfo.grow) { + for (int i=firstGlyph; i<=lastGlyph; i++) { + GlyphJustificationInfo gji = getGlyphJustificationInfos()[i]; + currInfo = jInfos[gji.growPriority]; + if (currInfo == null) { + // We still have to increment glyph position + Point2D glyphPos = getGlyphVector().getGlyphPosition(i); + glyphPos.setLocation(glyphPos.getX() + glyphOffset, glyphPos.getY()); + getGlyphVector().setGlyphPosition(i, glyphPos); + + continue; + } + + if (currInfo.useLimits) { + glyphOffset += gji.growLeftLimit; + if (currInfo.absorb) { + sideIncrement = gji.weight * currInfo.absorbedGapPerUnit; + glyphOffset += sideIncrement; + positionIncrement = glyphOffset; + glyphOffset += sideIncrement; + } else if (lastInfo != null && lastInfo.priority == currInfo.priority) { + sideIncrement = gji.weight * lastInfo.absorbedGapPerUnit; + glyphOffset += sideIncrement; + positionIncrement = glyphOffset; + glyphOffset += sideIncrement; + } else { + positionIncrement = glyphOffset; + } + glyphOffset += gji.growRightLimit; + } else { + sideIncrement = gji.weight * currInfo.gapPerUnit; + glyphOffset += sideIncrement; + positionIncrement = glyphOffset; + glyphOffset += sideIncrement; + } + + Point2D glyphPos = getGlyphVector().getGlyphPosition(i); + glyphPos.setLocation(glyphPos.getX() + positionIncrement, glyphPos.getY()); + getGlyphVector().setGlyphPosition(i, glyphPos); + } + } else { + for (int i=firstGlyph; i<=lastGlyph; i++) { + GlyphJustificationInfo gji = getGlyphJustificationInfos()[i]; + currInfo = jInfos[gji.shrinkPriority]; + if (currInfo == null) { + // We still have to increment glyph position + Point2D glyphPos = getGlyphVector().getGlyphPosition(i); + glyphPos.setLocation(glyphPos.getX() + glyphOffset, glyphPos.getY()); + getGlyphVector().setGlyphPosition(i, glyphPos); + + continue; + } + + if (currInfo.useLimits) { + glyphOffset -= gji.shrinkLeftLimit; + if (currInfo.absorb) { + sideIncrement = gji.weight * currInfo.absorbedGapPerUnit; + glyphOffset += sideIncrement; + positionIncrement = glyphOffset; + glyphOffset += sideIncrement; + } else if (lastInfo != null && lastInfo.priority == currInfo.priority) { + sideIncrement = gji.weight * lastInfo.absorbedGapPerUnit; + glyphOffset += sideIncrement; + positionIncrement = glyphOffset; + glyphOffset += sideIncrement; + } else { + positionIncrement = glyphOffset; + } + glyphOffset -= gji.shrinkRightLimit; + } else { + sideIncrement = gji.weight * currInfo.gapPerUnit; + glyphOffset += sideIncrement; + positionIncrement = glyphOffset; + glyphOffset += sideIncrement; + } + + Point2D glyphPos = getGlyphVector().getGlyphPosition(i); + glyphPos.setLocation(glyphPos.getX() + positionIncrement, glyphPos.getY()); + getGlyphVector().setGlyphPosition(i, glyphPos); + } + } + + + if (haveLast) { // Don't add padding after last char + lastGlyph++; + + GlyphJustificationInfo gji = getGlyphJustificationInfos()[lastGlyph]; + currInfo = jInfos[gji.growPriority]; + + if (currInfo != null) { + if (currInfo.useLimits) { + glyphOffset += firstInfo.grow ? gji.growLeftLimit : -gji.shrinkLeftLimit; + if (currInfo.absorb) { + glyphOffset += gji.weight * currInfo.absorbedGapPerUnit; + } else if (lastInfo != null && lastInfo.priority == currInfo.priority) { + glyphOffset += gji.weight * lastInfo.absorbedGapPerUnit; + } + } else { + glyphOffset += gji.weight * currInfo.gapPerUnit; + } + } + + // Ajust positions of all glyphs after last glyph + for (int i=lastGlyph; i length) { + return length + this.start; + } + return charOffset + start + this.start; + } + + @Override + int getStart() { + return start; + } + + @Override + int getEnd() { + return start + length; + } + + @Override + int getLength() { + return length; + } + + @Override + Shape getCharsBlackBoxBounds(int start, int limit) { + start -= this.start; + limit -= this.start; + + if (limit > length) { + limit = length; + } + + Rectangle2D charBounds = ga.getBounds(); + charBounds.setRect( + charBounds.getX() + ga.getAdvance() * start + x, + charBounds.getY() + y, + charBounds.getWidth() + ga.getAdvance() * (limit - start), + charBounds.getHeight() + ); + + return charBounds; + } + + @Override + float getCharPosition(int index) { + index -= start; + if (index > length) { + index = length; + } + + return ga.getAdvance() * index + x; + } + + @Override + float getCharAdvance(int index) { + return ga.getAdvance(); + } + + @Override + Shape getOutline() { + AffineTransform t = AffineTransform.getTranslateInstance(x, y); + return t.createTransformedShape( + TextDecorator.extendOutline(this, getVisualBounds(), decoration) + ); + } + + @Override + boolean charHasZeroAdvance(int index) { + return false; + } + + @Override + TextHitInfo hitTest(float hitX, float hitY) { + hitX -= x; + + float tmp = hitX / ga.getAdvance(); + int hitIndex = Math.round(tmp); + + if (tmp > hitIndex) { + return TextHitInfo.leading(hitIndex + this.start); + } + return TextHitInfo.trailing(hitIndex + this.start); + } + + @Override + void updateJustificationInfo(TextRunBreaker.JustificationInfo jInfo) { + // Do nothing + } + + @Override + float doJustification(TextRunBreaker.JustificationInfo jInfos[]) { + // Do nothing + return 0; + } + } +} diff --git a/awt/org/apache/harmony/awt/gl/image/BufferedImageGraphics2D.java b/awt/org/apache/harmony/awt/gl/image/BufferedImageGraphics2D.java new file mode 100644 index 000000000..f1d64fbec --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/BufferedImageGraphics2D.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexey A. Petrenko + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.image; + +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.WritableRaster; + +import org.apache.harmony.awt.gl.CommonGraphics2D; +import org.apache.harmony.awt.gl.Surface; +import org.apache.harmony.awt.gl.render.JavaBlitter; +import org.apache.harmony.awt.gl.render.NativeImageBlitter; + +/** + * BufferedImageGraphics2D is implementation of CommonGraphics2D for + * drawing on buffered images. + */ +public class BufferedImageGraphics2D extends CommonGraphics2D { + private BufferedImage bi = null; + private Rectangle bounds = null; + + public BufferedImageGraphics2D(BufferedImage bi) { + super(); + this.bi = bi; + this.bounds = new Rectangle(0, 0, bi.getWidth(), bi.getHeight()); + clip(bounds); + dstSurf = Surface.getImageSurface(bi); + if(dstSurf.isNativeDrawable()){ + blitter = NativeImageBlitter.getInstance(); + }else{ + blitter = JavaBlitter.getInstance(); + } + } + + @Override + public void copyArea(int x, int y, int width, int height, int dx, int dy) { + } + + @Override + public Graphics create() { + BufferedImageGraphics2D res = new BufferedImageGraphics2D(bi); + copyInternalFields(res); + return res; + } + + @Override + public GraphicsConfiguration getDeviceConfiguration() { + return null; + } + + public ColorModel getColorModel() { + return bi.getColorModel(); + } + + public WritableRaster getWritableRaster() { + return bi.getRaster(); + } +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/gl/image/BufferedImageSource.java b/awt/org/apache/harmony/awt/gl/image/BufferedImageSource.java new file mode 100644 index 000000000..0fe25a2e8 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/BufferedImageSource.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ + +package org.apache.harmony.awt.gl.image; + +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageProducer; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; +import java.util.Hashtable; + +public class BufferedImageSource implements ImageProducer { + + private Hashtable properties; + private ColorModel cm; + private WritableRaster raster; + private int width; + private int height; + + private ImageConsumer ic; + + public BufferedImageSource(BufferedImage image, Hashtable properties){ + if(properties == null) { + this.properties = new Hashtable(); + } else { + this.properties = properties; + } + + width = image.getWidth(); + height = image.getHeight(); + cm = image.getColorModel(); + raster = image.getRaster(); + } + + public BufferedImageSource(BufferedImage image){ + this(image, null); + } + + public boolean isConsumer(ImageConsumer ic) { + return (this.ic == ic); + } + + public void startProduction(ImageConsumer ic) { + addConsumer(ic); + } + + public void requestTopDownLeftRightResend(ImageConsumer ic) { + } + + public void removeConsumer(ImageConsumer ic) { + if (this.ic == ic) { + this.ic = null; + } + } + + public void addConsumer(ImageConsumer ic) { + this.ic = ic; + startProduction(); + } + + private void startProduction(){ + try { + ic.setDimensions(width, height); + ic.setProperties(properties); + ic.setColorModel(cm); + ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT | + ImageConsumer.COMPLETESCANLINES | + ImageConsumer.SINGLEFRAME | + ImageConsumer.SINGLEPASS); + if(cm instanceof IndexColorModel && + raster.getTransferType() == DataBuffer.TYPE_BYTE || + cm instanceof ComponentColorModel && + raster.getTransferType() == DataBuffer.TYPE_BYTE && + raster.getNumDataElements() == 1){ + DataBufferByte dbb = (DataBufferByte) raster.getDataBuffer(); + byte data[] = dbb.getData(); + int off = dbb.getOffset(); + ic.setPixels(0, 0, width, height, cm, data, off, width); + }else if(cm instanceof DirectColorModel && + raster.getTransferType() == DataBuffer.TYPE_INT){ + DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer(); + int data[] = dbi.getData(); + int off = dbi.getOffset(); + ic.setPixels(0, 0, width, height, cm, data, off, width); + }else if(cm instanceof DirectColorModel && + raster.getTransferType() == DataBuffer.TYPE_BYTE){ + DataBufferByte dbb = (DataBufferByte) raster.getDataBuffer(); + byte data[] = dbb.getData(); + int off = dbb.getOffset(); + ic.setPixels(0, 0, width, height, cm, data, off, width); + }else{ + ColorModel rgbCM = ColorModel.getRGBdefault(); + int pixels[] = new int[width]; + Object pix = null; + for(int y = 0; y < height; y++){ + for(int x = 0 ; x < width; x++){ + pix = raster.getDataElements(x, y, pix); + pixels[x] = cm.getRGB(pix); + } + ic.setPixels(0, y, width, 1, rgbCM, pixels, 0, width); + } + } + ic.imageComplete(ImageConsumer.STATICIMAGEDONE); + }catch (NullPointerException e){ + if (ic != null) { + ic.imageComplete(ImageConsumer.IMAGEERROR); + } + } + } + +} diff --git a/awt/org/apache/harmony/awt/gl/image/ByteArrayDecodingImageSource.java b/awt/org/apache/harmony/awt/gl/image/ByteArrayDecodingImageSource.java new file mode 100644 index 000000000..cc6d7cf66 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/ByteArrayDecodingImageSource.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ +/* + * Created on 10.02.2005 + * + */ +package org.apache.harmony.awt.gl.image; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +public class ByteArrayDecodingImageSource extends DecodingImageSource { + + byte imagedata[]; + int imageoffset; + int imagelength; + + public ByteArrayDecodingImageSource(byte imagedata[], int imageoffset, + int imagelength){ + this.imagedata = imagedata; + this.imageoffset = imageoffset; + this.imagelength = imagelength; + } + + public ByteArrayDecodingImageSource(byte imagedata[]){ + this(imagedata, 0, imagedata.length); + } + + @Override + protected boolean checkConnection() { + return true; + } + + @Override + protected InputStream getInputStream() { + // BEGIN android-modified + // TODO: Why does a ByteArrayInputStream need to be buffered at all? + return new BufferedInputStream(new ByteArrayInputStream(imagedata, + imageoffset, imagelength), 1024); + // END android-modified + } + +} diff --git a/awt/org/apache/harmony/awt/gl/image/DataBufferListener.java b/awt/org/apache/harmony/awt/gl/image/DataBufferListener.java new file mode 100644 index 000000000..8793050a3 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/DataBufferListener.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + * Created on 13.03.2006 + * + */ +package org.apache.harmony.awt.gl.image; + +public interface DataBufferListener { + + void dataChanged(); + void dataTaken(); + void dataReleased(); + +} diff --git a/awt/org/apache/harmony/awt/gl/image/DecodingImageSource.java b/awt/org/apache/harmony/awt/gl/image/DecodingImageSource.java new file mode 100644 index 000000000..958d691ce --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/DecodingImageSource.java @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +/* + * Created on 18.01.2005 + */ +package org.apache.harmony.awt.gl.image; + +import java.awt.image.ImageConsumer; +import java.awt.image.ImageProducer; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * This is an abstract class that encapsulates a main part of ImageProducer functionality + * for the images being decoded by the native decoders, like PNG, JPEG and GIF. + * It helps to integrate image decoders into producer/consumer model. It provides + * functionality for working with several decoder instances and several image consumers + * simultaneously. + */ +public abstract class DecodingImageSource implements ImageProducer { + List consumers = new ArrayList(5); + List decoders = new ArrayList(5); + boolean loading; + + ImageDecoder decoder; + + protected abstract boolean checkConnection(); + + protected abstract InputStream getInputStream(); + + public synchronized void addConsumer(ImageConsumer ic) { + if (!checkConnection()) { // No permission for this consumer + ic.imageComplete(ImageConsumer.IMAGEERROR); + return; + } + + ImageConsumer cons = findConsumer(consumers, ic); + + if (cons == null) { // Try to look in the decoders + ImageDecoder d = null; + + // Check for all existing decoders + for (Iterator i = decoders.iterator(); i.hasNext();) { + d = i.next(); + cons = findConsumer(d.consumers, ic); + if (cons != null) { + break; + } + } + } + + if (cons == null) { // Not found, add this consumer + consumers.add(ic); + } + } + + /** + * This method stops sending data to the given consumer + * @param ic - consumer + */ + private void abortConsumer(ImageConsumer ic) { + ic.imageComplete(ImageConsumer.IMAGEERROR); + consumers.remove(ic); + } + + /** + * This method stops sending data to the list of consumers. + * @param consumersList - list of consumers + */ + private void abortAllConsumers(List consumersList) { + for (ImageConsumer imageConsumer : consumersList) { + abortConsumer(imageConsumer); + } + } + + public synchronized void removeConsumer(ImageConsumer ic) { + ImageDecoder d = null; + + // Remove in all existing decoders + for (Iterator i = decoders.iterator(); i.hasNext();) { + d = i.next(); + removeConsumer(d.consumers, ic); + if (d.consumers.size() <= 0) { + d.terminate(); + } + } + + // Remove in the current queue of consumers + removeConsumer(consumers, ic); + } + + /** + * Static implementation of removeConsumer method + * @param consumersList - list of consumers + * @param ic - consumer to be removed + */ + private static void removeConsumer(List consumersList, ImageConsumer ic) { + ImageConsumer cons = null; + + for (Iterator i = consumersList.iterator(); i.hasNext();) { + cons = i.next(); + if (cons.equals(ic)) { + i.remove(); + } + } + } + + public void requestTopDownLeftRightResend(ImageConsumer consumer) { + // Do nothing + } + + public synchronized void startProduction(ImageConsumer ic) { + if (ic != null) { + addConsumer(ic); + } + + if (!loading && consumers.size() > 0) { + ImageLoader.addImageSource(this); + loading = true; + } + } + + public synchronized boolean isConsumer(ImageConsumer ic) { + ImageDecoder d = null; + + // Check for all existing decoders + for (Iterator i = decoders.iterator(); i.hasNext();) { + d = i.next(); + if (findConsumer(d.consumers, ic) != null) { + return true; + } + } + + // Check current queue of consumers + return findConsumer(consumers, ic) != null; + } + + /** + * Checks if the consumer is in the list and returns it it is there + * @param consumersList - list of consumers + * @param ic - consumer + * @return consumer if found, null otherwise + */ + private static ImageConsumer findConsumer(List consumersList, ImageConsumer ic) { + ImageConsumer res = null; + + for (Iterator i = consumersList.iterator(); i.hasNext();) { + res = i.next(); + if (res.equals(ic)) { + return res; + } + } + + return null; + } + + /** + * Use this method to finish decoding or lock the list of consumers + * for a particular decoder + * @param d - decoder + */ + synchronized void lockDecoder(ImageDecoder d) { + if (d == decoder) { + decoder = null; + startProduction(null); + } + } + + /** + * Tries to find an appropriate decoder for the input stream and adds it + * to the list of decoders + * @return created decoder + */ + private ImageDecoder createDecoder() { + InputStream is = getInputStream(); + + ImageDecoder decoder; + + if (is == null) { + decoder = null; + } else { + decoder = ImageDecoder.createDecoder(this, is); + } + + if (decoder != null) { + synchronized (this) { + decoders.add(decoder); + this.decoder = decoder; + loading = false; + consumers = new ArrayList(5); // Reset queue + } + + return decoder; + } + // We were not able to find appropriate decoder + List cs; + synchronized (this) { + cs = consumers; + consumers = new ArrayList(5); + loading = false; + } + abortAllConsumers(cs); + + return null; + } + + /** + * Stop the given decoder and remove it from the list + * @param dr - decoder + */ + private synchronized void removeDecoder(ImageDecoder dr) { + lockDecoder(dr); + decoders.remove(dr); + } + + /** + * This method serves as an entry point. + * It starts the decoder and loads the image data. + */ + public void load() { + synchronized (this) { + if (consumers.size() == 0) { + loading = false; + return; + } + } + + ImageDecoder d = createDecoder(); + if (d != null) { + try { + decoder.decodeImage(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + removeDecoder(d); + abortAllConsumers(d.consumers); + } + } + } +} diff --git a/awt/org/apache/harmony/awt/gl/image/FileDecodingImageSource.java b/awt/org/apache/harmony/awt/gl/image/FileDecodingImageSource.java new file mode 100644 index 000000000..54d466432 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/FileDecodingImageSource.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +/* + * Created on 20.01.2005 + */ +package org.apache.harmony.awt.gl.image; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +public class FileDecodingImageSource extends DecodingImageSource { + String filename; + + public FileDecodingImageSource(String file) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(file); + } + + filename = file; + } + + @Override +protected boolean checkConnection() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + try { + security.checkRead(filename); + } catch (SecurityException e) { + return false; + } + } + + return true; + } + + @Override +protected InputStream getInputStream() { + try { + // BEGIN android-modified + return new BufferedInputStream(new FileInputStream(filename), 8192); + // END android-modified + } catch (FileNotFoundException e) { + return null; + } + } + +} diff --git a/awt/org/apache/harmony/awt/gl/image/GifDecoder.java b/awt/org/apache/harmony/awt/gl/image/GifDecoder.java new file mode 100644 index 000000000..7ecb15bfe --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/GifDecoder.java @@ -0,0 +1,692 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +/* +* Created on 27.01.2005 +*/ +package org.apache.harmony.awt.gl.image; + +import java.awt.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.IndexColorModel; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; + +public class GifDecoder extends ImageDecoder { + // initializes proper field IDs + private static native void initIDs(); + + static { + System.loadLibrary("gl"); //$NON-NLS-1$ + initIDs(); + } + + // ImageConsumer hints: common + private static final int baseHints = + ImageConsumer.SINGLEPASS | ImageConsumer.COMPLETESCANLINES | + ImageConsumer.SINGLEFRAME; + // ImageConsumer hints: interlaced + private static final int interlacedHints = + baseHints | ImageConsumer.RANDOMPIXELORDER; + + // Impossible color value - no translucent pixels allowed + static final int IMPOSSIBLE_VALUE = 0x0FFFFFFF; + + // I/O buffer + private static final int BUFFER_SIZE = 1024; + private byte buffer[] = new byte[BUFFER_SIZE]; + + GifDataStream gifDataStream = new GifDataStream(); + GifGraphicBlock currBlock; + + // Pointer to native structure which store decoding state + // between subsequent decoding/IO-suspension cycles + private long hNativeDecoder; // NULL initially + + // Number of bytes eaten by the native decoder + private int bytesConsumed; + + private boolean consumersPrepared; + private Hashtable properties = new Hashtable(); + + // Could be set up by java code or native method when + // transparent pixel index changes or local color table encountered + private boolean forceRGB; + + private byte screenBuffer[]; + private int screenRGBBuffer[]; + + public GifDecoder(DecodingImageSource src, InputStream is) { + super(src, is); + } + + private static native int[] toRGB(byte imageData[], byte colormap[], int transparentColor); + + private static native void releaseNativeDecoder(long hDecoder); + + private native int decode( + byte input[], + int bytesInBuffer, + long hDecoder, + GifDataStream dataStream, + GifGraphicBlock currBlock + ); + + private int[] getScreenRGBBuffer() { + if (screenRGBBuffer == null) { + if (screenBuffer != null) { + int transparentColor = + gifDataStream.logicalScreen.globalColorTable.cm.getTransparentPixel(); + transparentColor = transparentColor > 0 ? transparentColor : IMPOSSIBLE_VALUE; + screenRGBBuffer = + toRGB( + screenBuffer, + gifDataStream.logicalScreen.globalColorTable.colors, + transparentColor + ); + } else { + int size = gifDataStream.logicalScreen.logicalScreenHeight * + gifDataStream.logicalScreen.logicalScreenWidth; + screenRGBBuffer = new int[size]; + } + } + + return screenRGBBuffer; + } + + private void prepareConsumers() { + GifLogicalScreen gls = gifDataStream.logicalScreen; + setDimensions(gls.logicalScreenWidth, + gls.logicalScreenHeight); + setProperties(properties); + + currBlock = gifDataStream.graphicBlocks.get(0); + if (forceRGB) { + setColorModel(ColorModel.getRGBdefault()); + } else { + setColorModel(gls.globalColorTable.getColorModel(currBlock.transparentColor)); + } + + // Fill screen buffer with the background or transparent color + if (forceRGB) { + int fillColor = 0xFF000000; + if (gls.backgroundColor != IMPOSSIBLE_VALUE) { + fillColor = gls.backgroundColor; + } + + Arrays.fill(getScreenRGBBuffer(), fillColor); + } else { + int fillColor = 0; + + if (gls.backgroundColor != IMPOSSIBLE_VALUE) { + fillColor = gls.backgroundColor; + } else { + fillColor = gls.globalColorTable.cm.getTransparentPixel(); + } + + screenBuffer = new byte[gls.logicalScreenHeight*gls.logicalScreenWidth]; + Arrays.fill(screenBuffer, (byte) fillColor); + } + + setHints(interlacedHints); // XXX - always random pixel order + } + + @Override + public void decodeImage() throws IOException { + try { + int bytesRead = 0; + int needBytes, offset, bytesInBuffer = 0; + boolean eosReached = false; + GifGraphicBlock blockToDispose = null; + + // Create new graphic block + if (currBlock == null) { + currBlock = new GifGraphicBlock(); + gifDataStream.graphicBlocks.add(currBlock); + } + + // Read from the input stream + for (;;) { + needBytes = BUFFER_SIZE - bytesInBuffer; + offset = bytesInBuffer; + + bytesRead = inputStream.read(buffer, offset, needBytes); + + if (bytesRead < 0) { + eosReached = true; + bytesRead = 0; + } // Don't break, maybe something left in buffer + + // Keep track on how much bytes left in buffer + bytesInBuffer += bytesRead; + + // Here we pass number of new bytes read from the input stream (bytesRead) + // since native decoder uses java buffer and doesn't have its own + // buffer. So it adds this number to the number of bytes left + // in buffer from the previous call. + int numLines = decode( + buffer, + bytesRead, + hNativeDecoder, + gifDataStream, + currBlock); + + // Keep track on how much bytes left in buffer + bytesInBuffer -= bytesConsumed; + + if ( + !consumersPrepared && + gifDataStream.logicalScreen.completed && + gifDataStream.logicalScreen.globalColorTable.completed && + (currBlock.imageData != null || // Have transparent pixel filled + currBlock.rgbImageData != null) + ) { + prepareConsumers(); + consumersPrepared = true; + } + + if (bytesConsumed < 0) { + break; // Error exit + } + + if (currBlock != null) { + if (numLines != 0) { + // Dispose previous image only before showing next + if (blockToDispose != null) { + blockToDispose.dispose(); + blockToDispose = null; + } + + currBlock.sendNewData(this, numLines); + } + + if (currBlock.completed && hNativeDecoder != 0) { + blockToDispose = currBlock; // Dispose only before showing new pixels + currBlock = new GifGraphicBlock(); + gifDataStream.graphicBlocks.add(currBlock); + } + } + + if (hNativeDecoder == 0) { + break; + } + + if (eosReached && numLines == 0) { // Maybe image is truncated... + releaseNativeDecoder(hNativeDecoder); + break; + } + } + } finally { + closeStream(); + } + + // Here all animation goes + // Repeat image loopCount-1 times or infinitely if loopCount = 0 + if (gifDataStream.loopCount != 1) { + if (currBlock.completed == false) { + gifDataStream.graphicBlocks.remove(currBlock); + } + + int numFrames = gifDataStream.graphicBlocks.size(); + // At first last block will be disposed + GifGraphicBlock gb = + gifDataStream.graphicBlocks.get(numFrames-1); + + ImageLoader.beginAnimation(); + + while (gifDataStream.loopCount != 1) { + if (gifDataStream.loopCount != 0) { + gifDataStream.loopCount--; + } + + // Show all frames + for (int i=0; i graphicBlocks = new ArrayList(10); // Of GifGraphicBlocks + + // Comments from the image + String comments[]; + } + + class GifLogicalScreen { + // Indicates that reading of this block accomplished + boolean completed = false; + + int logicalScreenWidth; + int logicalScreenHeight; + + int backgroundColor = IMPOSSIBLE_VALUE; + + GifColorTable globalColorTable = new GifColorTable(); + } + + class GifGraphicBlock { + // Indicates that reading of this block accomplished + boolean completed = false; + + final static int DISPOSAL_NONE = 0; + final static int DISPOSAL_NODISPOSAL = 1; + final static int DISPOSAL_BACKGROUND = 2; + final static int DISPOSAL_RESTORE = 3; + + int disposalMethod; + int delayTime; // Multiplied by 10 already + int transparentColor = IMPOSSIBLE_VALUE; + + int imageLeft; + int imageTop; + int imageWidth; + int imageHeight; + + // Auxilliary variables to minimize computations + int imageRight; + int imageBottom; + + boolean interlace; + + // Don't need local color table - if it is specified + // image data are converted to RGB in the native code + + byte imageData[] = null; + int rgbImageData[] = null; + + private int currY = 0; // Current output scanline + + int[] getRgbImageData() { + if (rgbImageData == null) { + rgbImageData = + toRGB( + imageData, + gifDataStream.logicalScreen.globalColorTable.colors, + transparentColor + ); + if (transparentColor != IMPOSSIBLE_VALUE) { + transparentColor = + gifDataStream.logicalScreen.globalColorTable.cm.getRGB(transparentColor); + transparentColor &= 0x00FFFFFF; + } + } + return rgbImageData; + } + + private void replaceTransparentPixels(int numLines) { + List graphicBlocks = gifDataStream.graphicBlocks; + int prevBlockIndex = graphicBlocks.indexOf(this) - 1; + + if (prevBlockIndex >= 0) { + int maxY = currY + numLines + imageTop; + int offset = currY * imageWidth; + + // Update right and bottom coordinates + imageRight = imageLeft + imageWidth; + imageBottom = imageTop + imageHeight; + + int globalWidth = gifDataStream.logicalScreen.logicalScreenWidth; + int pixelValue, imageOffset; + int rgbData[] = forceRGB ? getRgbImageData() : null; + + for (int y = currY + imageTop; y < maxY; y++) { + imageOffset = globalWidth * y + imageLeft; + for (int x = imageLeft; x < imageRight; x++) { + pixelValue = forceRGB ? + rgbData[offset] : + imageData[offset] & 0xFF; + if (pixelValue == transparentColor) { + if (forceRGB) { + pixelValue = getScreenRGBBuffer() [imageOffset]; + rgbData[offset] = pixelValue; + } else { + pixelValue = screenBuffer [imageOffset]; + imageData[offset] = (byte) pixelValue; + } + } + offset++; + imageOffset++; + } // for + } // for + + } // if (prevBlockIndex >= 0) + } + + public void sendNewData(GifDecoder decoder, int numLines) { + // Get values for transparent pixels + // from the perevious frames + if (transparentColor != IMPOSSIBLE_VALUE) { + replaceTransparentPixels(numLines); + } + + if (forceRGB) { + decoder.setPixels( + imageLeft, + imageTop + currY, + imageWidth, + numLines, + ColorModel.getRGBdefault(), + getRgbImageData(), + currY*imageWidth, + imageWidth + ); + } else { + decoder.setPixels( + imageLeft, + imageTop + currY, + imageWidth, + numLines, + null, + imageData, + currY*imageWidth, + imageWidth + ); + } + + currY += numLines; + } + + public void dispose() { + imageComplete(ImageConsumer.SINGLEFRAMEDONE); + + // Show current frame until delayInterval will not elapse + if (delayTime > 0) { + try { + Thread.sleep(delayTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else { + Thread.yield(); // Allow consumers to consume data + } + + // Don't dispose if image is outside of the visible area + if (imageLeft > gifDataStream.logicalScreen.logicalScreenWidth || + imageTop > gifDataStream.logicalScreen.logicalScreenHeight) { + disposalMethod = DISPOSAL_NONE; + } + + switch(disposalMethod) { + case DISPOSAL_BACKGROUND: { + if (forceRGB) { + getRgbImageData(); // Ensure that transparentColor is RGB, not index + + int data[] = new int[imageWidth*imageHeight]; + + // Compatibility: Fill with transparent color if we have one + if (transparentColor != IMPOSSIBLE_VALUE) { + Arrays.fill( + data, + transparentColor + ); + } else { + Arrays.fill( + data, + gifDataStream.logicalScreen.backgroundColor + ); + } + + setPixels( + imageLeft, + imageTop, + imageWidth, + imageHeight, + ColorModel.getRGBdefault(), + data, + 0, + imageWidth + ); + + sendToScreenBuffer(data); + } else { + byte data[] = new byte[imageWidth*imageHeight]; + + // Compatibility: Fill with transparent color if we have one + if (transparentColor != IMPOSSIBLE_VALUE) { + Arrays.fill( + data, + (byte) transparentColor + ); + } else { + Arrays.fill( + data, + (byte) gifDataStream.logicalScreen.backgroundColor + ); + } + + setPixels( + imageLeft, + imageTop, + imageWidth, + imageHeight, + null, + data, + 0, + imageWidth + ); + + sendToScreenBuffer(data); + } + break; + } + case DISPOSAL_RESTORE: { + screenBufferToScreen(); + break; + } + case DISPOSAL_NONE: + case DISPOSAL_NODISPOSAL: + default: { + // Copy transmitted data to the screen buffer + Object data = forceRGB ? (Object) getRgbImageData() : imageData; + sendToScreenBuffer(data); + break; + } + } + } + + private void sendToScreenBuffer(Object data) { + int dataInt[]; + byte dataByte[]; + + int width = gifDataStream.logicalScreen.logicalScreenWidth; + + + if (forceRGB) { + dataInt = (int[]) data; + + if (imageWidth == width) { + System.arraycopy(dataInt, + 0, + getScreenRGBBuffer(), + imageLeft + imageTop*width, + dataInt.length + ); + } else { // Each scanline + copyScanlines(dataInt, getScreenRGBBuffer(), width); + } + } else { + dataByte = (byte[]) data; + + if (imageWidth == width) { + System.arraycopy(dataByte, + 0, + screenBuffer, + imageLeft + imageTop*width, + dataByte.length + ); + } else { // Each scanline + copyScanlines(dataByte, screenBuffer, width); + } + } + } // sendToScreenBuffer + + private void copyScanlines(Object src, Object dst, int width) { + for (int i=0; i 0) { + if (transparentColor == IMPOSSIBLE_VALUE) { + return cm = + new IndexColorModel(8, size, colors, 0, false); + } + + if (transparentColor > size) { + size = transparentColor + 1; + } + return cm = + new IndexColorModel(8, size, colors, 0, false, transparentColor); + } + + return cm = null; // Force default ARGB color model + } + } +} diff --git a/awt/org/apache/harmony/awt/gl/image/ImageDecoder.java b/awt/org/apache/harmony/awt/gl/image/ImageDecoder.java new file mode 100644 index 000000000..d16128e55 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/ImageDecoder.java @@ -0,0 +1,258 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +/* + * Created on 18.01.2005 + */ +package org.apache.harmony.awt.gl.image; + +import com.android.internal.awt.AndroidImageDecoder; + +import java.awt.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ConcurrentModificationException; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; + + +/** + * This class contains common functionality for all image decoders. + */ +public abstract class ImageDecoder { + + /** Image types */ + public static final int GENERIC_DECODER = 0; + public static final int JPG_DECODER = 1; + public static final int GIF_DECODER = 2; + public static final int PNG_DECODER = 3; + + private static final int MAX_BYTES_IN_SIGNATURE = 8; + + protected List consumers; + protected InputStream inputStream; + protected DecodingImageSource src; + + protected boolean terminated; + + /** + * Chooses appropriate image decoder by looking into input stream and checking + * the image signature. + * @param src - image producer, required for passing data to it from the + * created decoder via callbacks + * @param is - stream + * @return decoder + */ + static ImageDecoder createDecoder(DecodingImageSource src, InputStream is) { + InputStream markable; + + if (!is.markSupported()) { + // BEGIN android-modified + markable = new BufferedInputStream(is, 8192); + // END android-modified + } else { + markable = is; + } + + // Read the signature from the stream and then reset it back + try { + markable.mark(MAX_BYTES_IN_SIGNATURE); + + byte[] signature = new byte[MAX_BYTES_IN_SIGNATURE]; + markable.read(signature, 0, MAX_BYTES_IN_SIGNATURE); + markable.reset(); + + if ((signature[0] & 0xFF) == 0xFF && + (signature[1] & 0xFF) == 0xD8 && + (signature[2] & 0xFF) == 0xFF) { // JPEG + return loadDecoder(PNG_DECODER, src, is); + } else if ((signature[0] & 0xFF) == 0x47 && // G + (signature[1] & 0xFF) == 0x49 && // I + (signature[2] & 0xFF) == 0x46) { // F + return loadDecoder(GIF_DECODER, src, is); + } else if ((signature[0] & 0xFF) == 137 && // PNG signature: 137 80 78 71 13 10 26 10 + (signature[1] & 0xFF) == 80 && + (signature[2] & 0xFF) == 78 && + (signature[3] & 0xFF) == 71 && + (signature[4] & 0xFF) == 13 && + (signature[5] & 0xFF) == 10 && + (signature[6] & 0xFF) == 26 && + (signature[7] & 0xFF) == 10) { + return loadDecoder(JPG_DECODER, src, is); + } + + return loadDecoder(GENERIC_DECODER, src, is); + + } catch (IOException e) { // Silently + } + + return null; + } + + /* + * In the future, we might return different decoders for differen image types. + * But for now, we always return the generic one. + * Also: we could add a factory to load image decoder. + */ + private static ImageDecoder loadDecoder(int type, DecodingImageSource src, + InputStream is) { + return new AndroidImageDecoder(src, is); + } + + protected ImageDecoder(DecodingImageSource _src, InputStream is) { + src = _src; + consumers = src.consumers; + inputStream = is; + } + + public abstract void decodeImage() throws IOException; + + public synchronized void closeStream() { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + } + } + } + + /** + * Stops the decoding by interrupting the current decoding thread. + * Used when all consumers are removed and there's no more need to + * run the decoder. + */ + public void terminate() { + src.lockDecoder(this); + closeStream(); + + AccessController.doPrivileged( + new PrivilegedAction() { + public Void run() { + Thread.currentThread().interrupt(); + return null; + } + } + ); + + terminated = true; + } + + protected void setDimensions(int w, int h) { + if (terminated) { + return; + } + + for (ImageConsumer ic : consumers) { + ic.setDimensions(w, h); + } + } + + protected void setProperties(Hashtable props) { + if (terminated) { + return; + } + + for (ImageConsumer ic : consumers) { + ic.setProperties(props); + } + } + + protected void setColorModel(ColorModel cm) { + if (terminated) { + return; + } + + for (ImageConsumer ic : consumers) { + ic.setColorModel(cm); + } + } + + protected void setHints(int hints) { + if (terminated) { + return; + } + + for (ImageConsumer ic : consumers) { + ic.setHints(hints); + } + } + + protected void setPixels( + int x, int y, + int w, int h, + ColorModel model, + byte pix[], + int off, int scansize + ) { + if (terminated) { + return; + } + + src.lockDecoder(this); + + for (ImageConsumer ic : consumers) { + ic.setPixels(x, y, w, h, model, pix, off, scansize); + } + } + + protected void setPixels( + int x, int y, + int w, int h, + ColorModel model, + int pix[], + int off, int scansize + ) { + if (terminated) { + return; + } + + src.lockDecoder(this); + + for (ImageConsumer ic : consumers) { + ic.setPixels(x, y, w, h, model, pix, off, scansize); + } + } + + protected void imageComplete(int status) { + if (terminated) { + return; + } + + src.lockDecoder(this); + + ImageConsumer ic = null; + + for (Iterator i = consumers.iterator(); i.hasNext();) { + try { + ic = i.next(); + } catch (ConcurrentModificationException e) { + i = consumers.iterator(); + continue; + } + ic.imageComplete(status); + } + } + +} diff --git a/awt/org/apache/harmony/awt/gl/image/ImageLoader.java b/awt/org/apache/harmony/awt/gl/image/ImageLoader.java new file mode 100644 index 000000000..5c7d18032 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/ImageLoader.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +/* + * Created on 18.01.2005 + */ +package org.apache.harmony.awt.gl.image; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * This class provides functionality for simultaneous loading of + * several images and running animation. + */ +public class ImageLoader extends Thread { + // Contains ImageLoader objects + // and queue of image sources waiting to be loaded + static class ImageLoadersStorage { + private static final int MAX_THREADS = 5; + private static final int TIMEOUT = 4000; + static ImageLoadersStorage instance; + + List queue = new LinkedList(); + List loaders = new ArrayList(MAX_THREADS); + + private int freeLoaders; + + private ImageLoadersStorage() {} + + static ImageLoadersStorage getStorage() { + if (instance == null) { + instance = new ImageLoadersStorage(); + } + + return instance; + } + } + + ImageLoader() { + super(); + setDaemon(true); + } + + /** + * This method creates a new thread which is able to load an image + * or run animation (if the number of existing loader threads does not + * exceed the limit). + */ + private static void createLoader() { + final ImageLoadersStorage storage = ImageLoadersStorage.getStorage(); + + synchronized(storage.loaders) { + if (storage.loaders.size() < ImageLoadersStorage.MAX_THREADS) { + AccessController.doPrivileged( + new PrivilegedAction() { + public Void run() { + ImageLoader loader = new ImageLoader(); + storage.loaders.add(loader); + loader.start(); + return null; + } + }); + } + } + } + + /** + * Adds a new image source to the queue and starts a new loader + * thread if required + * @param imgSrc - image source + */ + public static void addImageSource(DecodingImageSource imgSrc) { + ImageLoadersStorage storage = ImageLoadersStorage.getStorage(); + synchronized(storage.queue) { + if (!storage.queue.contains(imgSrc)) { + storage.queue.add(imgSrc); + } + if (storage.freeLoaders == 0) { + createLoader(); + } + + storage.queue.notify(); + } + } + + /** + * Waits for a new ImageSource until timout expires. + * Loader thread will terminate after returning from this method + * if timeout expired and image source was not picked up from the queue. + * @return image source picked up from the queue or null if timeout expired + */ + private static DecodingImageSource getWaitingImageSource() { + ImageLoadersStorage storage = ImageLoadersStorage.getStorage(); + + synchronized(storage.queue) { + DecodingImageSource isrc = null; + + if (storage.queue.size() == 0) { + try { + storage.freeLoaders++; + storage.queue.wait(ImageLoadersStorage.TIMEOUT); + } catch (InterruptedException e) { + return null; + } finally { + storage.freeLoaders--; + } + } + + if (storage.queue.size() > 0) { + isrc = storage.queue.get(0); + storage.queue.remove(0); + } + + return isrc; + } + } + + /** + * Entry point of the loader thread. Picks up image sources and + * runs decoders for them while there are available image sources in the queue. + * If there are no and timeout expires it terminates. + */ + @Override + public void run() { + ImageLoadersStorage storage = ImageLoadersStorage.getStorage(); + + try { + while (storage.loaders.contains(this)) { + Thread.interrupted(); // Reset the interrupted flag + DecodingImageSource isrc = getWaitingImageSource(); + if (isrc != null) { + try { + isrc.load(); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + break; // Don't wait if timeout expired - terminate loader + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + synchronized(storage.loaders) { + storage.loaders.remove(Thread.currentThread()); + } + } + } + + /** + * Removes current thread from loaders (so we are able + * to create more loaders) and decreases its priority. + */ + static void beginAnimation() { + ImageLoadersStorage storage = ImageLoadersStorage.getStorage(); + Thread currThread = Thread.currentThread(); + + synchronized(storage) { + storage.loaders.remove(currThread); + + if (storage.freeLoaders < storage.queue.size()) { + createLoader(); + } + } + + currThread.setPriority(Thread.MIN_PRIORITY); + } + + /** + * Sends the current thread to wait for the new images to load + * if there are free placeholders for loaders + */ + static void endAnimation() { + ImageLoadersStorage storage = ImageLoadersStorage.getStorage(); + Thread currThread = Thread.currentThread(); + + synchronized(storage) { + if (storage.loaders.size() < ImageLoadersStorage.MAX_THREADS && + !storage.loaders.contains(currThread) + ) { + storage.loaders.add(currThread); + } + } + + currThread.setPriority(Thread.NORM_PRIORITY); + } +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/gl/image/JpegDecoder.java b/awt/org/apache/harmony/awt/gl/image/JpegDecoder.java new file mode 100644 index 000000000..2e64427f0 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/JpegDecoder.java @@ -0,0 +1,231 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.image; + +import java.awt.image.*; +import java.awt.color.ColorSpace; +import java.awt.*; +import java.io.IOException; +import java.io.InputStream; +import java.util.Hashtable; + +import org.apache.harmony.awt.internal.nls.Messages; + +public class JpegDecoder extends ImageDecoder { + // Only 2 output colorspaces expected. Others are converted into + // these ones. + // 1. Grayscale + public static final int JCS_GRAYSCALE = 1; + // 2. RGB + public static final int JCS_RGB = 2; + + // Flags for the consumer, progressive JPEG + private static final int hintflagsProgressive = + ImageConsumer.SINGLEFRAME | // JPEG is a static image + ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible + ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines + // Flags for the consumer, singlepass JPEG + private static final int hintflagsSingle = + ImageConsumer.SINGLEPASS | + hintflagsProgressive; + + // Buffer for the stream + private static final int BUFFER_SIZE = 1024; + private byte buffer[] = new byte[BUFFER_SIZE]; + + // 3 possible color models only + private static ColorModel cmRGB; + private static ColorModel cmGray; + + // initializes proper field IDs + private static native void initIDs(); + + // Pointer to native structure which store decoding state + // between subsequent decoding/IO-suspension cycles + private long hNativeDecoder = 0; // NULL initially + + private boolean headerDone = false; + + // Next 4 members are filled by the native method (decompress). + // We can simply check if imageWidth is still negative to find + // out if they are already filled. + private int imageWidth = -1; + private int imageHeight = -1; + private boolean progressive = false; + private int jpegColorSpace = 0; + + // Stores number of bytes consumed by the native decoder + private int bytesConsumed = 0; + // Stores current scanline returned by the decoder + private int currScanline = 0; + + private ColorModel cm = null; + + static { + System.loadLibrary("jpegdecoder"); //$NON-NLS-1$ + + cmGray = new ComponentColorModel( + ColorSpace.getInstance(ColorSpace.CS_GRAY), + false, false, + Transparency.OPAQUE, DataBuffer.TYPE_BYTE + ); + + // Create RGB color model + cmRGB = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF); + + initIDs(); + } + + public JpegDecoder(DecodingImageSource src, InputStream is) { + super(src, is); + } + + /* + public JpegDecoder(InputStream iStream, ImageConsumer iConsumer) { + inputStream = iStream; + consumer = iConsumer; + } + */ + + /** + * @return - not NULL if call is successful + */ + private native Object decode( + byte[] input, + int bytesInBuffer, + long hDecoder); + + private static native void releaseNativeDecoder(long hDecoder); + + @Override + public void decodeImage() throws IOException { + try { + int bytesRead = 0, dataLength = 0; + boolean eosReached = false; + int needBytes, offset, bytesInBuffer = 0; + byte byteOut[] = null; + int intOut[] = null; + // Read from the input stream + for (;;) { + needBytes = BUFFER_SIZE - bytesInBuffer; + offset = bytesInBuffer; + + bytesRead = inputStream.read(buffer, offset, needBytes); + + if (bytesRead < 0) { + bytesRead = 0;//break; + eosReached = true; + } // Don't break, maybe something left in buffer + + // Keep track on how much bytes left in buffer + bytesInBuffer += bytesRead; + + // Here we pass overall number of bytes left in the java buffer + // (bytesInBuffer) since jpeg decoder has its own buffer and consumes + // as many bytes as it can. If there are any unconsumed bytes + // it didn't add them to its buffer... + Object arr = decode( + buffer, + bytesInBuffer, + hNativeDecoder); + + // Keep track on how much bytes left in buffer + bytesInBuffer -= bytesConsumed; + + if (!headerDone && imageWidth != -1) { + returnHeader(); + headerDone = true; + } + + if (bytesConsumed < 0) { + break; // Error exit + } + + if (arr instanceof byte[]) { + byteOut = (byte[]) arr; + dataLength = byteOut.length; + returnData(byteOut, currScanline); + } else if (arr instanceof int[]) { + intOut = (int[]) arr; + dataLength = intOut.length; + returnData(intOut, currScanline); + } else { + dataLength = 0; + } + + if (hNativeDecoder == 0) { + break; + } + + if (dataLength == 0 && eosReached) { + releaseNativeDecoder(hNativeDecoder); + break; // Probably image is truncated + } + } + imageComplete(ImageConsumer.STATICIMAGEDONE); + } catch (IOException e) { + throw e; + } finally { + closeStream(); + } + } + + public void returnHeader() { + setDimensions(imageWidth, imageHeight); + + switch (jpegColorSpace) { + case JCS_GRAYSCALE: cm = cmGray; break; + case JCS_RGB: cm = cmRGB; break; + default: + // awt.3D=Unknown colorspace + throw new IllegalArgumentException(Messages.getString("awt.3D")); //$NON-NLS-1$ + } + setColorModel(cm); + + setHints(progressive ? hintflagsProgressive : hintflagsSingle); + + setProperties(new Hashtable()); // Empty + } + + // Send the data to the consumer + public void returnData(int data[], int currScanLine) { + // Send 1 or more scanlines to the consumer. + int numScanlines = data.length / imageWidth; + if (numScanlines > 0) { + setPixels( + 0, currScanLine - numScanlines, + imageWidth, numScanlines, + cm, data, 0, imageWidth + ); + } + } + + public void returnData(byte data[], int currScanLine) { + int numScanlines = data.length / imageWidth; + if (numScanlines > 0) { + setPixels( + 0, currScanLine - numScanlines, + imageWidth, numScanlines, + cm, data, 0, imageWidth + ); + } + } +} diff --git a/awt/org/apache/harmony/awt/gl/image/OffscreenImage.java b/awt/org/apache/harmony/awt/gl/image/OffscreenImage.java new file mode 100644 index 000000000..3445f8e82 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/OffscreenImage.java @@ -0,0 +1,532 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ +/* + * Created on 22.12.2004 + * + */ +package org.apache.harmony.awt.gl.image; + +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.awt.image.DirectColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; +import java.util.Hashtable; +import java.util.Vector; + +import org.apache.harmony.awt.gl.ImageSurface; +import org.apache.harmony.awt.internal.nls.Messages; + + +/** + * This class represent implementation of abstract Image class + */ +public class OffscreenImage extends Image implements ImageConsumer { + + static final ColorModel rgbCM = ColorModel.getRGBdefault(); + ImageProducer src; + BufferedImage image; + ColorModel cm; + WritableRaster raster; + boolean isIntRGB; + Hashtable properties; + Vector observers; + int width; + int height; + int imageState; + int hints; + private boolean producing; + private ImageSurface imageSurf; + + public OffscreenImage(ImageProducer ip){ + imageState = 0; + src = ip; + width = -1; + height = -1; + observers = new Vector(); + producing = false; + } + + @Override + public Object getProperty(String name, ImageObserver observer) { + if(name == null) { + // awt.38=Property name is not defined + throw new NullPointerException(Messages.getString("awt.38")); //$NON-NLS-1$ + } + if(properties == null){ + addObserver(observer); + startProduction(); + if(properties == null) { + return null; + } + } + Object prop = properties.get(name); + if(prop == null) { + prop = UndefinedProperty; + } + return prop; + } + + @Override + public ImageProducer getSource() { + return src; + } + + @Override + public int getWidth(ImageObserver observer) { + if((imageState & ImageObserver.WIDTH) == 0){ + addObserver(observer); + startProduction(); + if((imageState & ImageObserver.WIDTH) == 0) { + return -1; + } + } + return width; + } + + @Override + public int getHeight(ImageObserver observer) { + if((imageState & ImageObserver.HEIGHT) == 0){ + addObserver(observer); + startProduction(); + if((imageState & ImageObserver.HEIGHT) == 0) { + return -1; + } + } + return height; + } + + @Override + public Graphics getGraphics() { + // awt.39=This method is not implemented for image obtained from ImageProducer + throw new UnsupportedOperationException(Messages.getString("awt.39")); //$NON-NLS-1$ + } + + @Override + public void flush() { + stopProduction(); + imageUpdate(this, ImageObserver.ABORT, -1, -1, -1, -1); + imageState &= ~ImageObserver.ERROR; + imageState = 0; + image = null; + cm = null; + raster = null; + hints = 0; + width = -1; + height = -1; + } + + public void setProperties(Hashtable properties) { + this.properties = properties; + imageUpdate(this, ImageObserver.PROPERTIES, 0, 0, width, height); + } + + public void setColorModel(ColorModel cm) { + this.cm = cm; + } + + /* + * We suppose what in case loading JPEG image then image has DirectColorModel + * and for infill image Raster will use setPixels method with int array. + * + * In case loading GIF image, for raster infill, is used setPixels method with + * byte array and Color Model is IndexColorModel. But Color Model may + * be changed during this process. Then is called setPixels method with + * int array and image force to default color model - int ARGB. The rest + * pixels are sending in DirectColorModel. + */ + public void setPixels(int x, int y, int w, int h, ColorModel model, + int[] pixels, int off, int scansize) { + if(raster == null){ + if(cm == null){ + if(model == null) { + // awt.3A=Color Model is null + throw new NullPointerException(Messages.getString("awt.3A")); //$NON-NLS-1$ + } + cm = model; + } + createRaster(); + } + + if(model == null) { + model = cm; + } + if(cm != model){ + forceToIntARGB(); + } + + if(cm == model && model.getTransferType() == DataBuffer.TYPE_INT && + raster.getNumDataElements() == 1){ + + DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer(); + int data[] = dbi.getData(); + int scanline = raster.getWidth(); + int rof = dbi.getOffset() + y * scanline + x; + for(int lineOff = off, line = y; line < y + h; + line++, lineOff += scansize, rof += scanline){ + + System.arraycopy(pixels, lineOff, data, rof, w); + } + + }else if(isIntRGB){ + int buff[] = new int[w]; + DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer(); + int data[] = dbi.getData(); + int scanline = raster.getWidth(); + int rof = dbi.getOffset() + y * scanline + x; + for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize, + rof += scanline) { + + for (int sx = x, idx = 0; sx < x + w; sx++, idx++) { + buff[idx] = model.getRGB(pixels[sOff + idx]); + } + System.arraycopy(buff, 0, data, rof, w); + } + }else{ + Object buf = null; + for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) { + for (int sx = x, idx = 0; sx < x + w; sx++, idx++) { + int rgb = model.getRGB(pixels[sOff + idx]); + buf = cm.getDataElements(rgb, buf); + raster.setDataElements(sx, sy, buf); + } + } + } + + if (imageSurf != null) { + imageSurf.invalidate(); + } + + imageUpdate(this, ImageObserver.SOMEBITS, 0, 0, width, height); + } + + public void setPixels(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int off, int scansize) { + + if(raster == null){ + if(cm == null){ + if(model == null) { + // awt.3A=Color Model is null + throw new NullPointerException(Messages.getString("awt.3A")); //$NON-NLS-1$ + } + cm = model; + } + createRaster(); + } + if(model == null) { + model = cm; + } + if(model != cm){ + forceToIntARGB(); + } + + if(isIntRGB){ + int buff[] = new int[w]; + IndexColorModel icm = (IndexColorModel) model; + int colorMap[] = new int[icm.getMapSize()]; + icm.getRGBs(colorMap); + DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer(); + int data[] = dbi.getData(); + int scanline = raster.getWidth(); + int rof = dbi.getOffset() + y * scanline + x; + if(model instanceof IndexColorModel){ + + for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize, + rof += scanline) { + for (int sx = x, idx = 0; sx < x + w; sx++, idx++) { + buff[idx] = colorMap[pixels[sOff + idx] & 0xff]; + } + System.arraycopy(buff, 0, data, rof, w); + } + }else{ + + for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize, + rof += scanline) { + for (int sx = x, idx = 0; sx < x + w; sx++, idx++) { + buff[idx] = model.getRGB(pixels[sOff + idx] & 0xff); + } + System.arraycopy(buff, 0, data, rof, w); + } + } + }else if(model == cm && model.getTransferType() == DataBuffer.TYPE_BYTE && + raster.getNumDataElements() == 1){ + + DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer(); + byte data[] = dbb.getData(); + int scanline = raster.getWidth(); + int rof = dbb.getOffset() + y * scanline + x; + for(int lineOff = off, line = y; line < y + h; + line++, lineOff += scansize, rof += scanline){ + System.arraycopy(pixels, lineOff, data, rof, w); + } + // BEGIN android-added (taken from newer Harmony) + }else if(model == cm && model.getTransferType() == DataBuffer.TYPE_BYTE && + cm instanceof ComponentColorModel){ + + int nc = cm.getNumComponents(); + byte stride[] = new byte[scansize]; + for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) { + System.arraycopy(pixels, sOff, stride, 0, scansize); + + raster.setDataElements(x, sy, w, 1, stride); + } + // END android-added + }else { + for (int sy = y, sOff = off; sy < y + h; sy++, sOff += scansize) { + for (int sx = x, idx = 0; sx < x + w; sx++, idx++) { + int rgb = model.getRGB(pixels[sOff + idx] & 0xff); + raster.setDataElements(sx, sy, cm.getDataElements(rgb, null)); + } + } + } + + if (imageSurf != null) { + imageSurf.invalidate(); + } + + imageUpdate(this, ImageObserver.SOMEBITS, 0, 0, width, height); + } + + public void setDimensions(int width, int height) { + if(width <= 0 || height <= 0){ + imageComplete(ImageObserver.ERROR); + return; + } + + this.width = width; + this.height = height; + imageUpdate(this, (ImageObserver.HEIGHT | ImageObserver.WIDTH), + 0, 0, width, height); + } + + public void setHints(int hints) { + this.hints = hints; + } + + public void imageComplete(int state) { + int flag; + switch(state){ + case IMAGEABORTED: + flag = ImageObserver.ABORT; + break; + case IMAGEERROR: + flag = ImageObserver.ERROR | ImageObserver.ABORT; + break; + case SINGLEFRAMEDONE: + flag = ImageObserver.FRAMEBITS; + break; + case STATICIMAGEDONE: + flag = ImageObserver.ALLBITS; + break; + default: + // awt.3B=Incorrect ImageConsumer completion status + throw new IllegalArgumentException(Messages.getString("awt.3B")); //$NON-NLS-1$ + } + imageUpdate(this, flag, 0, 0, width, height); + + if((flag & (ImageObserver.ERROR | ImageObserver.ABORT | + ImageObserver.ALLBITS)) != 0 ) { + stopProduction(); + observers.removeAllElements(); + } + } + + public /*synchronized*/ BufferedImage getBufferedImage(){ + if(image == null){ + ColorModel model = getColorModel(); + WritableRaster wr = getRaster(); + if(model != null && wr != null) { + image = new BufferedImage(model, wr, model.isAlphaPremultiplied(), null); + } + } + return image; + } + + public /*synchronized*/ int checkImage(ImageObserver observer){ + addObserver(observer); + return imageState; + } + + public /*synchronized*/ boolean prepareImage(ImageObserver observer){ + if((imageState & ImageObserver.ERROR) != 0){ + if(observer != null){ + observer.imageUpdate(this, ImageObserver.ERROR | + ImageObserver.ABORT, -1, -1, -1, -1); + } + return false; + } + if((imageState & ImageObserver.ALLBITS) != 0) { + return true; + } + addObserver(observer); + startProduction(); + return ((imageState & ImageObserver.ALLBITS) != 0); + } + + public /*synchronized*/ ColorModel getColorModel(){ + if(cm == null) { + startProduction(); + } + return cm; + } + + public /*synchronized*/ WritableRaster getRaster(){ + if(raster == null) { + startProduction(); + } + return raster; + } + + public int getState(){ + return imageState; + } + + private /*synchronized*/ void addObserver(ImageObserver observer){ + if(observer != null){ + if(observers.contains(observer)) { + return; + } + if((imageState & ImageObserver.ERROR) != 0){ + observer.imageUpdate(this, ImageObserver.ERROR | + ImageObserver.ABORT, -1, -1, -1, -1); + return; + } + if((imageState & ImageObserver.ALLBITS) != 0){ + observer.imageUpdate(this, imageState, 0, 0, width, height); + return; + } + observers.addElement(observer); + } + } + + private synchronized void startProduction(){ + if(!producing){ + imageState &= ~ImageObserver.ABORT; + producing = true; + src.startProduction(this); + } + } + + private synchronized void stopProduction(){ + producing = false; + src.removeConsumer(this); + } + + private void createRaster(){ + try{ + raster = cm.createCompatibleWritableRaster(width, height); + isIntRGB = false; + if(cm instanceof DirectColorModel){ + DirectColorModel dcm = (DirectColorModel) cm; + if(dcm.getTransferType() == DataBuffer.TYPE_INT && + dcm.getRedMask() == 0xff0000 && + dcm.getGreenMask() == 0xff00 && + dcm.getBlueMask() == 0xff){ + isIntRGB = true; + } + } + }catch(Exception e){ + cm = ColorModel.getRGBdefault(); + raster = cm.createCompatibleWritableRaster(width, height); + isIntRGB = true; + } + } + + private /*synchronized*/ void imageUpdate(Image img, int infoflags, int x, int y, + int width, int height){ + + imageState |= infoflags; + for (ImageObserver observer : observers) { + observer.imageUpdate(this, infoflags, x, y, width, height); + } + +// notifyAll(); + } + + private void forceToIntARGB(){ + + int w = raster.getWidth(); + int h = raster.getHeight(); + + WritableRaster destRaster = rgbCM.createCompatibleWritableRaster(w, h); + + Object obj = null; + int pixels[] = new int[w]; + + if(cm instanceof IndexColorModel){ + IndexColorModel icm = (IndexColorModel) cm; + int colorMap[] = new int[icm.getMapSize()]; + icm.getRGBs(colorMap); + + for (int y = 0; y < h; y++) { + obj = raster.getDataElements(0, y, w, 1, obj); + byte ba[] = (byte[]) obj; + for (int x = 0; x < ba.length; x++) { + pixels[x] = colorMap[ba[x] & 0xff]; + } + destRaster.setDataElements(0, y, w, 1, pixels); + } + + }else{ + for(int y = 0; y < h; y++){ + for(int x = 0; x < w; x++){ + obj = raster.getDataElements(x, y, obj); + pixels[x] = cm.getRGB(obj); + } + destRaster.setDataElements(0, y, w, 1, pixels); + } + } + + synchronized(this){ + if(imageSurf != null){ + imageSurf.dispose(); + imageSurf = null; + } + if(image != null){ + image.flush(); + image = null; + } + cm = rgbCM; + raster = destRaster; + isIntRGB = true; + } + } + + public ImageSurface getImageSurface() { + if (imageSurf == null) { + ColorModel model = getColorModel(); + WritableRaster wr = getRaster(); + if(model != null && wr != null) { + imageSurf = new ImageSurface(model, wr); + } + } + return imageSurf; + } +} diff --git a/awt/org/apache/harmony/awt/gl/image/OrdinaryWritableRaster.java b/awt/org/apache/harmony/awt/gl/image/OrdinaryWritableRaster.java new file mode 100644 index 000000000..1748e1bdd --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/OrdinaryWritableRaster.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ +/* + * Created on 30.09.2004 + * + */ +package org.apache.harmony.awt.gl.image; + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; + +public class OrdinaryWritableRaster extends WritableRaster { + + public OrdinaryWritableRaster(SampleModel sampleModel, + DataBuffer dataBuffer, Rectangle aRegion, + Point sampleModelTranslate, WritableRaster parent) { + super(sampleModel, dataBuffer, aRegion, sampleModelTranslate, parent); + } + + public OrdinaryWritableRaster(SampleModel sampleModel, + DataBuffer dataBuffer, Point origin) { + super(sampleModel, dataBuffer, origin); + } + + public OrdinaryWritableRaster(SampleModel sampleModel, Point origin) { + super(sampleModel, origin); + } + + @Override + public void setDataElements(int x, int y, Object inData) { + super.setDataElements(x, y, inData); + } + + @Override + public void setDataElements(int x, int y, int w, int h, Object inData) { + super.setDataElements(x, y, w, h, inData); + } + + @Override + public WritableRaster createWritableChild(int parentX, int parentY, int w, + int h, int childMinX, int childMinY, int[] bandList) { + return super.createWritableChild(parentX, parentY, w, h, childMinX, + childMinY, bandList); + } + + @Override + public WritableRaster createWritableTranslatedChild(int childMinX, + int childMinY) { + return super.createWritableTranslatedChild(childMinX, childMinY); + } + + @Override + public WritableRaster getWritableParent() { + return super.getWritableParent(); + } + + @Override + public void setRect(Raster srcRaster) { + super.setRect(srcRaster); + } + + @Override + public void setRect(int dx, int dy, Raster srcRaster) { + super.setRect(dx, dy, srcRaster); + } + + @Override + public void setDataElements(int x, int y, Raster inRaster) { + super.setDataElements(x, y, inRaster); + } + + @Override + public void setPixel(int x, int y, int[] iArray) { + super.setPixel(x, y, iArray); + } + + @Override + public void setPixel(int x, int y, float[] fArray) { + super.setPixel(x, y, fArray); + } + + @Override + public void setPixel(int x, int y, double[] dArray) { + super.setPixel(x, y, dArray); + } + + @Override + public void setPixels(int x, int y, int w, int h, int[] iArray) { + super.setPixels(x, y, w, h, iArray); + } + + @Override + public void setPixels(int x, int y, int w, int h, float[] fArray) { + super.setPixels(x, y, w, h, fArray); + } + + @Override + public void setPixels(int x, int y, int w, int h, double[] dArray) { + super.setPixels(x, y, w, h, dArray); + } + + @Override + public void setSamples(int x, int y, int w, int h, int b, int[] iArray) { + super.setSamples(x, y, w, h, b, iArray); + } + + @Override + public void setSamples(int x, int y, int w, int h, int b, float[] fArray) { + super.setSamples(x, y, w, h, b, fArray); + } + + @Override + public void setSamples(int x, int y, int w, int h, int b, double[] dArray) { + super.setSamples(x, y, w, h, b, dArray); + } + + @Override + public void setSample(int x, int y, int b, int s) { + super.setSample(x, y, b, s); + } + + @Override + public void setSample(int x, int y, int b, float s) { + super.setSample(x, y, b, s); + } + + @Override + public void setSample(int x, int y, int b, double s) { + super.setSample(x, y, b, s); + } +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/gl/image/PngDecoder.java b/awt/org/apache/harmony/awt/gl/image/PngDecoder.java new file mode 100644 index 000000000..7e85600ac --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/PngDecoder.java @@ -0,0 +1,270 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Oleg V. Khaschansky + * @version $Revision$ + * + * @date: Jul 22, 2005 + */ + +package org.apache.harmony.awt.gl.image; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Hashtable; +import java.awt.color.ColorSpace; +import java.awt.image.*; +import java.awt.*; + +import org.apache.harmony.awt.internal.nls.Messages; + +public class PngDecoder extends ImageDecoder { + // initializes proper field IDs + private static native void initIDs(); + + static { + System.loadLibrary("gl"); //$NON-NLS-1$ + initIDs(); + } + + private static final int hintflags = + ImageConsumer.SINGLEFRAME | // PNG is a static image + ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible + ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines + + // Each pixel is a grayscale sample. + private static final int PNG_COLOR_TYPE_GRAY = 0; + // Each pixel is an R,G,B triple. + private static final int PNG_COLOR_TYPE_RGB = 2; + // Each pixel is a palette index, a PLTE chunk must appear. + private static final int PNG_COLOR_TYPE_PLTE = 3; + // Each pixel is a grayscale sample, followed by an alpha sample. + private static final int PNG_COLOR_TYPE_GRAY_ALPHA = 4; + // Each pixel is an R,G,B triple, followed by an alpha sample. + private static final int PNG_COLOR_TYPE_RGBA = 6; + + private static final int INPUT_BUFFER_SIZE = 4096; + private byte buffer[] = new byte[INPUT_BUFFER_SIZE]; + + // Buffers for decoded image data + byte byteOut[]; + int intOut[]; + + // Native pointer to png decoder data + private long hNativeDecoder; + + int imageWidth, imageHeight; + int colorType; + int bitDepth; + byte cmap[]; + + boolean transferInts; // Is transfer type int?.. or byte? + int dataElementsPerPixel = 1; + + ColorModel cm; + + int updateFromScanline; // First scanline to update + int numScanlines; // Number of scanlines to update + + private native long decode(byte[] input, int bytesInBuffer, long hDecoder); + + private static native void releaseNativeDecoder(long hDecoder); + + public PngDecoder(DecodingImageSource src, InputStream is) { + super(src, is); + } + + @Override + public void decodeImage() throws IOException { + try { + int bytesRead = 0; + int needBytes, offset, bytesInBuffer = 0; + // Read from the input stream + for (;;) { + needBytes = INPUT_BUFFER_SIZE - bytesInBuffer; + offset = bytesInBuffer; + + bytesRead = inputStream.read(buffer, offset, needBytes); + + if (bytesRead < 0) { // Break, nothing to read from buffer, image truncated? + releaseNativeDecoder(hNativeDecoder); + break; + } + + // Keep track on how much bytes left in buffer + bytesInBuffer += bytesRead; + hNativeDecoder = decode(buffer, bytesInBuffer, hNativeDecoder); + // PNG decoder always consumes all bytes at once + bytesInBuffer = 0; + + // if (bytesConsumed < 0) + //break; // Error exit + + returnData(); + + // OK, we decoded all the picture in the right way... + if (hNativeDecoder == 0) { + break; + } + } + + imageComplete(ImageConsumer.STATICIMAGEDONE); + } catch (IOException e) { + throw e; + } catch (RuntimeException e) { + imageComplete(ImageConsumer.IMAGEERROR); + throw e; + } finally { + closeStream(); + } + } + + @SuppressWarnings("unused") + private void returnHeader() { // Called from native code + setDimensions(imageWidth, imageHeight); + + switch (colorType) { + case PNG_COLOR_TYPE_GRAY: { + if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) { + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + // Create gray color model + int numEntries = 1 << bitDepth; + int scaleFactor = 255 / (numEntries-1); + byte comps[] = new byte[numEntries]; + for (int i = 0; i < numEntries; i++) { + comps[i] = (byte) (i * scaleFactor); + } + cm = new IndexColorModel(/*bitDepth*/8, numEntries, comps, comps, comps); + + transferInts = false; + break; + } + + case PNG_COLOR_TYPE_RGB: { + if (bitDepth != 8) { + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + cm = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF); + + transferInts = true; + break; + } + + case PNG_COLOR_TYPE_PLTE: { + if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) { + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + cm = new IndexColorModel(/*bitDepth*/8, cmap.length / 3, cmap, 0, false); + + transferInts = false; + break; + } + + case PNG_COLOR_TYPE_GRAY_ALPHA: { + if (bitDepth != 8) { + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), + true, false, + Transparency.TRANSLUCENT, + DataBuffer.TYPE_BYTE); + + transferInts = false; + dataElementsPerPixel = 2; + break; + } + + case PNG_COLOR_TYPE_RGBA: { + if (bitDepth != 8) { + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + cm = ColorModel.getRGBdefault(); + + transferInts = true; + break; + } + default: + // awt.3C=Unknown PNG color type + throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ + } + + // Create output buffer + if (transferInts) { + intOut = new int[imageWidth * imageHeight]; + } else { + byteOut = new byte[imageWidth * imageHeight * dataElementsPerPixel]; + } + + setColorModel(cm); + + setHints(hintflags); + setProperties(new Hashtable()); // Empty + } + + // Send the data to the consumer + private void returnData() { + // Send 1 or more scanlines to the consumer. + if (numScanlines > 0) { + // Native decoder could have returned + // some data from the next pass, handle it here + int pass1, pass2; + if (updateFromScanline + numScanlines > imageHeight) { + pass1 = imageHeight - updateFromScanline; + pass2 = updateFromScanline + numScanlines - imageHeight; + } else { + pass1 = numScanlines; + pass2 = 0; + } + + transfer(updateFromScanline, pass1); + if (pass2 != 0) { + transfer(0, pass2); + } + } + } + + private void transfer(int updateFromScanline, int numScanlines) { + if (transferInts) { + setPixels( + 0, updateFromScanline, + imageWidth, numScanlines, + cm, intOut, + updateFromScanline * imageWidth, + imageWidth + ); + } else { + setPixels( + 0, updateFromScanline, + imageWidth, numScanlines, + cm, byteOut, + updateFromScanline * imageWidth * dataElementsPerPixel, + imageWidth * dataElementsPerPixel + ); + } + } +} diff --git a/awt/org/apache/harmony/awt/gl/image/PngDecoderJava.java b/awt/org/apache/harmony/awt/gl/image/PngDecoderJava.java new file mode 100644 index 000000000..46545f9c4 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/PngDecoderJava.java @@ -0,0 +1,282 @@ +package org.apache.harmony.awt.gl.image; + +// A simple PNG decoder source code in Java. +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.IndexColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.zip.CRC32; +import java.util.zip.InflaterInputStream; + +//import javax.swing.JFrame; + +public class PngDecoderJava { + +/* + public static void main(String[] args) throws Exception { + String name = "logo.png"; + if (args.length > 0) + name = args[0]; + InputStream in = PngDecoderJava.class.getResourceAsStream(name); + final BufferedImage image = PngDecoderJava.decode(in); + in.close(); + + JFrame f = new JFrame() { + public void paint(Graphics g) { + Insets insets = getInsets(); + g.drawImage(image, insets.left, insets.top, null); + } + }; + f.setVisible(true); + Insets insets = f.getInsets(); + f.setSize(image.getWidth() + insets.left + insets.right, image + .getHeight() + + insets.top + insets.bottom); + } + */ + + public static BufferedImage decode(InputStream in) throws IOException { + DataInputStream dataIn = new DataInputStream(in); + readSignature(dataIn); + PNGData chunks = readChunks(dataIn); + + long widthLong = chunks.getWidth(); + long heightLong = chunks.getHeight(); + if (widthLong > Integer.MAX_VALUE || heightLong > Integer.MAX_VALUE) + throw new IOException("That image is too wide or tall."); + int width = (int) widthLong; + int height = (int) heightLong; + + ColorModel cm = chunks.getColorModel(); + WritableRaster raster = chunks.getRaster(); + + BufferedImage image = new BufferedImage(cm, raster, false, null); + + return image; + } + + protected static void readSignature(DataInputStream in) throws IOException { + long signature = in.readLong(); + if (signature != 0x89504e470d0a1a0aL) + throw new IOException("PNG signature not found!"); + } + + protected static PNGData readChunks(DataInputStream in) throws IOException { + PNGData chunks = new PNGData(); + + boolean trucking = true; + while (trucking) { + try { + // Read the length. + int length = in.readInt(); + if (length < 0) + throw new IOException("Sorry, that file is too long."); + // Read the type. + byte[] typeBytes = new byte[4]; + in.readFully(typeBytes); + // Read the data. + byte[] data = new byte[length]; + in.readFully(data); + // Read the CRC. + long crc = in.readInt() & 0x00000000ffffffffL; // Make it + // unsigned. + if (verifyCRC(typeBytes, data, crc) == false) + throw new IOException("That file appears to be corrupted."); + + PNGChunk chunk = new PNGChunk(typeBytes, data); + chunks.add(chunk); + } catch (EOFException eofe) { + trucking = false; + } + } + return chunks; + } + + protected static boolean verifyCRC(byte[] typeBytes, byte[] data, long crc) { + CRC32 crc32 = new CRC32(); + crc32.update(typeBytes); + crc32.update(data); + long calculated = crc32.getValue(); + return (calculated == crc); + } +} + +class PNGData { + private int mNumberOfChunks; + + private PNGChunk[] mChunks; + + public PNGData() { + mNumberOfChunks = 0; + mChunks = new PNGChunk[10]; + } + + public void add(PNGChunk chunk) { + mChunks[mNumberOfChunks++] = chunk; + if (mNumberOfChunks >= mChunks.length) { + PNGChunk[] largerArray = new PNGChunk[mChunks.length + 10]; + System.arraycopy(mChunks, 0, largerArray, 0, mChunks.length); + mChunks = largerArray; + } + } + + public long getWidth() { + return getChunk("IHDR").getUnsignedInt(0); + } + + public long getHeight() { return getChunk("IHDR").getUnsignedInt(4); + } + + public short getBitsPerPixel() { + return getChunk("IHDR").getUnsignedByte(8); + } + + public short getColorType() { + return getChunk("IHDR").getUnsignedByte(9); + } + + public short getCompression() { + return getChunk("IHDR").getUnsignedByte(10); + } + + public short getFilter() { + return getChunk("IHDR").getUnsignedByte(11); + } + + public short getInterlace() { + return getChunk("IHDR").getUnsignedByte(12); + } + + public ColorModel getColorModel() { + short colorType = getColorType(); + int bitsPerPixel = getBitsPerPixel(); + + if (colorType == 3) { + byte[] paletteData = getChunk("PLTE").getData(); + int paletteLength = paletteData.length / 3; + return new IndexColorModel(bitsPerPixel, paletteLength, + paletteData, 0, false); + } + System.out.println("Unsupported color type: " + colorType); + return null; + } + + public WritableRaster getRaster() { + int width = (int) getWidth(); + int height = (int) getHeight(); + int bitsPerPixel = getBitsPerPixel(); + short colorType = getColorType(); + + if (colorType == 3) { + byte[] imageData = getImageData(); + //Orig: DataBuffer db = new DataBufferByte(imageData, imageData.length); + int len = Math.max(imageData.length, (width - 1) * (height -1)); + DataBuffer db = new DataBufferByte(imageData, len); + WritableRaster raster = Raster.createPackedRaster(db, width, + height, bitsPerPixel, null); + return raster; + } else + System.out.println("Unsupported color type!"); + return null; + } + + public byte[] getImageData() { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + // Write all the IDAT data into the array. + for (int i = 0; i < mNumberOfChunks; i++) { + PNGChunk chunk = mChunks[i]; + if (chunk.getTypeString().equals("IDAT")) { + out.write(chunk.getData()); + } + } + out.flush(); + // Now deflate the data. + InflaterInputStream in = new InflaterInputStream( + new ByteArrayInputStream(out.toByteArray())); + ByteArrayOutputStream inflatedOut = new ByteArrayOutputStream(); + int readLength; + byte[] block = new byte[8192]; + while ((readLength = in.read(block)) != -1) + inflatedOut.write(block, 0, readLength); + inflatedOut.flush(); + byte[] imageData = inflatedOut.toByteArray(); + // Compute the real length. + int width = (int) getWidth(); + int height = (int) getHeight(); + int bitsPerPixel = getBitsPerPixel(); + int length = width * height * bitsPerPixel / 8; + + byte[] prunedData = new byte[length]; + + // We can only deal with non-interlaced images. + if (getInterlace() == 0) { + int index = 0; + for (int i = 0; i < length; i++) { + if ((i * 8 / bitsPerPixel) % width == 0) { + index++; // Skip the filter byte. + } + prunedData[i] = imageData[index++]; + } + } else + System.out.println("Couldn't undo interlacing."); + + return prunedData; + } catch (IOException ioe) { + } + return null; + } + + public PNGChunk getChunk(String type) { + for (int i = 0; i < mNumberOfChunks; i++) + if (mChunks[i].getTypeString().equals(type)) + return mChunks[i]; + return null; + } +} + +class PNGChunk { + private byte[] mType; + + private byte[] mData; + + public PNGChunk(byte[] type, byte[] data) { + mType = type; + mData = data; + } + + public String getTypeString() { + try { + return new String(mType, "UTF8"); + } catch (UnsupportedEncodingException uee) { + return ""; + } + } + + public byte[] getData() { + return mData; + } + + public long getUnsignedInt(int offset) { + long value = 0; + for (int i = 0; i < 4; i++) + value += (mData[offset + i] & 0xff) << ((3 - i) * 8); + return value; + } + + public short getUnsignedByte(int offset) { + return (short) (mData[offset] & 0x00ff); + } +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/gl/image/URLDecodingImageSource.java b/awt/org/apache/harmony/awt/gl/image/URLDecodingImageSource.java new file mode 100644 index 000000000..a1899d650 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/image/URLDecodingImageSource.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + */ +/* + * Created on 10.02.2005 + * + */ +package org.apache.harmony.awt.gl.image; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.security.Permission; + +public class URLDecodingImageSource extends DecodingImageSource { + + URL url; + + public URLDecodingImageSource(URL url){ + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkConnect(url.getHost(), url.getPort()); + try { + Permission p = url.openConnection().getPermission(); + security.checkPermission(p); + } catch (IOException e) { + } + } + this.url = url; + } + + @Override + protected boolean checkConnection() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + try { + security.checkConnect(url.getHost(), url.getPort()); + return true; + } catch (SecurityException e) { + return false; + } + } + return true; + } + + @Override + protected InputStream getInputStream() { + try{ + URLConnection uc = url.openConnection(); + // BEGIN android-modified + return new BufferedInputStream(uc.getInputStream(), 8192); + // END android-modified + }catch(IOException e){ + return null; + } + } + +} diff --git a/awt/org/apache/harmony/awt/gl/render/Blitter.java b/awt/org/apache/harmony/awt/gl/render/Blitter.java new file mode 100644 index 000000000..3b8012e72 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/render/Blitter.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + * Created on 14.11.2005 + * + */ +package org.apache.harmony.awt.gl.render; + +import java.awt.Color; +import java.awt.Composite; +import java.awt.geom.AffineTransform; + +import org.apache.harmony.awt.gl.MultiRectArea; +import org.apache.harmony.awt.gl.Surface; + +/** + * The interface for objects which can drawing Images on other Images which have + * Graphics or on the display. + */ +public interface Blitter { + + public abstract void blit(int srcX, int srcY, Surface srcSurf, + int dstX, int dstY, Surface dstSurf, int width, int height, + AffineTransform sysxform, AffineTransform xform, + Composite comp, Color bgcolor, + MultiRectArea clip); + + public abstract void blit(int srcX, int srcY, Surface srcSurf, + int dstX, int dstY, Surface dstSurf, int width, int height, + AffineTransform sysxform, Composite comp, Color bgcolor, + MultiRectArea clip); + + public abstract void blit(int srcX, int srcY, Surface srcSurf, + int dstX, int dstY, Surface dstSurf, int width, int height, + Composite comp, Color bgcolor, MultiRectArea clip); + +} diff --git a/awt/org/apache/harmony/awt/gl/render/JavaArcRasterizer.java b/awt/org/apache/harmony/awt/gl/render/JavaArcRasterizer.java new file mode 100644 index 000000000..b643b41ac --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/render/JavaArcRasterizer.java @@ -0,0 +1,502 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.render; + +import org.apache.harmony.awt.gl.MultiRectArea; + +public class JavaArcRasterizer { + + /** + * Adds particular arc segment to mra + */ + static void addX0LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) { + int x1 = 0; + for(int i = 0; i < line.length; i++) { + int x2 = line[i]; + int y = cy + (b - i); + if (x1 <= finish && x2 >= start) { + mra.addRect(cx + Math.max(x1, start), y, cx + Math.min(x2, finish), y); + } + x1 = x2 + 1; + } + } + + static void addX1LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) { + int x1 = 0; + for(int i = 0; i < line.length; i++) { + int x2 = line[i]; + int y = cy - (b - i); + if (x1 <= finish && x2 >= start) { + mra.addRect(cx + Math.max(x1, start), y, cx + Math.min(x2, finish), y); + } + x1 = x2 + 1; + } + } + + static void addX2LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) { + int x1 = 0; + for(int i = 0; i < line.length; i++) { + int x2 = line[i]; + int y = cy - (b - i); + if (x1 <= finish && x2 >= start) { + mra.addRect(cx - Math.min(x2, finish), y, cx - Math.max(x1, start), y); + } + x1 = x2 + 1; + } + } + + static void addX3LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) { + int x1 = 0; + for(int i = 0; i < line.length; i++) { + int x2 = line[i]; + int y = cy + (b - i); + if (x1 <= finish && x2 >= start) { + mra.addRect(cx - Math.min(x2, finish), y, cx - Math.max(x1, start), y); + } + x1 = x2 + 1; + } + } + + static void addY0LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) { + int y1 = 0; + for(int i = 0; i < line.length; i++) { + int x = cx + (b - i); + int y2 = line[i]; + if (y1 <= finish && y2 >= start) { + mra.addRect(x, cy + Math.max(y1, start), x, cy + Math.min(y2, finish)); + } + y1 = y2 + 1; + } + } + + static void addY1LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) { + int y1 = 0; + for(int i = 0; i < line.length; i++) { + int x = cx - (b - i); + int y2 = line[i]; + if (y1 <= finish && y2 >= start) { + mra.addRect(x, cy + Math.max(y1, start), x, cy + Math.min(y2, finish)); + } + y1 = y2 + 1; + } + } + + static void addY2LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) { + int y1 = 0; + for(int i = 0; i < line.length; i++) { + int x = cx - (b - i); + int y2 = line[i]; + if (y1 <= finish && y2 >= start) { + mra.addRect(x, cy - Math.min(y2, finish), x, cy - Math.max(y1, start)); + } + y1 = y2 + 1; + } + } + + static void addY3LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) { + int y1 = 0; + for(int i = 0; i < line.length; i++) { + int x = cx + (b - i); + int y2 = line[i]; + if (y1 <= finish && y2 >= start) { + mra.addRect(x, cy - Math.min(y2, finish), x, cy - Math.max(y1, start)); + } + y1 = y2 + 1; + } + } + + static void addX0Line(MultiRectArea mra, int[] line, int cx, int cy, int b) { + int prev = 0; + for(int i = 0; i < line.length; i++) { + mra.addRect(cx + prev, cy + (b - i), cx + line[i], cy + (b - i)); + prev = line[i] + 1; + } + } + + static void addX1Line(MultiRectArea mra, int[] line, int cx, int cy, int b) { + int prev = 0; + for(int i = 0; i < line.length; i++) { + mra.addRect(cx + prev, cy - (b - i), cx + line[i], cy - (b - i)); + prev = line[i] + 1; + } + } + + static void addX2Line(MultiRectArea mra, int[] line, int cx, int cy, int b) { + int prev = 0; + for(int i = 0; i < line.length; i++) { + mra.addRect(cx - line[i], cy - (b - i), cx - prev, cy - (b - i)); + prev = line[i] + 1; + } + } + + static void addX3Line(MultiRectArea mra, int[] line, int cx, int cy, int b) { + int prev = 0; + for(int i = 0; i < line.length; i++) { + mra.addRect(cx - line[i], cy + (b - i), cx - prev, cy + (b - i)); + prev = line[i] + 1; + } + } + + static void addY0Line(MultiRectArea mra, int[] line, int cx, int cy, int a) { + int prev = 0; + for(int i = 0; i < line.length; i++) { + mra.addRect(cx + (a - i), cy + prev, cx + (a - i), cy + line[i]); + prev = line[i] + 1; + } + } + + static void addY1Line(MultiRectArea mra, int[] line, int cx, int cy, int a) { + int prev = 0; + for(int i = 0; i < line.length; i++) { + mra.addRect(cx - (a - i), cy + prev, cx - (a - i), cy + line[i]); + prev = line[i] + 1; + } + } + + static void addY2Line(MultiRectArea mra, int[] line, int cx, int cy, int a) { + int prev = 0; + for(int i = 0; i < line.length; i++) { + mra.addRect(cx - (a - i), cy - line[i], cx - (a - i), cy - prev); + prev = line[i] + 1; + } + } + + static void addY3Line(MultiRectArea mra, int[] line, int cx, int cy, int a) { + int prev = 0; + for(int i = 0; i < line.length; i++) { + mra.addRect(cx + (a - i), cy - line[i], cx + (a - i), cy - prev); + prev = line[i] + 1; + } + } + + /** + * Returns normalized angle (from 0 to 360 degrees) + */ + static double getNormAngle(double angle) { + angle -= Math.floor(angle / 360) * 360; + if (angle < 0) { + angle += 360; + } + return angle; + } + + /** + * Creates arc lookup table + */ + static int[] createLine(int a, int b, int xcount, int ycount) { + int[] buf = new int[b - ycount + 1]; + int d = a * a + 2 * b * b - 2 * a * a * b; + int x = 0; + int y = b; + while (y >= ycount) { + if (d < 0) { + d = d + b * b * (4 * x + 6); + } else { + buf[b - y] = x; + d = d + b * b * (4 * x + 6) + 4 * a * a * (1 - y); + y--; + } + x++; + } + return buf; + } + + /** + * Adds head/tail arc segment to MultiRectArea + */ + static void addSeg(MultiRectArea mra, int cx1, int cy1, int cx2, int cy2, int a, int b, int[] xline, int[] yline, int[] bounds) { + switch(bounds[0]) { + case 0: + addY3LineSeg(mra, yline, cx2, cy1, a, bounds[1], bounds[2]); + break; + case 1: + addX1LineSeg(mra, xline, cx2, cy1, b, bounds[1], bounds[2]); + break; + case 2: + addX2LineSeg(mra, xline, cx1, cy1, b, bounds[1], bounds[2]); + break; + case 3: + addY2LineSeg(mra, yline, cx1, cy1, a, bounds[1], bounds[2]); + break; + case 4: + addY1LineSeg(mra, yline, cx1, cy2, a, bounds[1], bounds[2]); + break; + case 5: + addX3LineSeg(mra, xline, cx1, cy2, b, bounds[1], bounds[2]); + break; + case 6: + addX0LineSeg(mra, xline, cx2, cy2, b, bounds[1], bounds[2]); + break; + case 7: + addY0LineSeg(mra, yline, cx2, cy2, a, bounds[1], bounds[2]); + break; + } + } + + /** + * Returns bounds for non quadratic arc head + */ + static int[] getSegment1(double angle, int ax, int ay, int xcount, int ycount) { + int[] bounds = new int[3]; + switch((int)(angle / 90)) { + case 0: + if (xcount < ax) { + bounds[0] = 0; // Y3 + bounds[1] = -ay; + bounds[2] = ycount; + } else { + bounds[0] = 1; // X1 + bounds[1] = 0; + bounds[2] = ax; + } + break; + case 1: + if (xcount > -ax) { + bounds[0] = 2; // X2 + bounds[1] = -ax; + bounds[2] = xcount; + } else { + bounds[0] = 3; // Y2 + bounds[1] = 0; + bounds[2] = -ay; + } + break; + case 2: + if (xcount < -ax) { + bounds[0] = 4; // Y1 + bounds[1] = ay; + bounds[2] = ycount; + } else { + bounds[0] = 5; // X3 + bounds[1] = 0; + bounds[2] = -ax; + } + break; + case 3: + if (xcount > ax) { + bounds[0] = 6; // X0 + bounds[1] = ax; + bounds[2] = xcount; + } else { + bounds[0] = 7; // Y0 + bounds[1] = 0; + bounds[2] = ay; + } + break; + } + return bounds; + } + + /** + * Returns bounds for non quadratic arc tail + */ + static int[] getSegment2(double angle, int ax, int ay, int xcount, int ycount) { + int[] bounds = new int[3]; + switch((int)(angle / 90)) { + case 0: + if (xcount < ax) { + bounds[0] = 0; // Y3 + bounds[1] = 0; + bounds[2] = -ay; + } else { + bounds[0] = 1; // X1 + bounds[1] = ax; + bounds[2] = xcount; + } + break; + case 1: + if (xcount > -ax) { + bounds[0] = 2; // X2 + bounds[1] = 0; + bounds[2] = -ax; + } else { + bounds[0] = 3; // Y2 + bounds[1] = -ay; + bounds[2] = ycount; + } + break; + case 2: + if (xcount < -ax) { + bounds[0] = 4; // Y1 + bounds[1] = 0; + bounds[2] = ay; + } else { + bounds[0] = 5; // X3 + bounds[1] = -ax; + bounds[2] = xcount; + } + break; + case 3: + if (xcount > ax) { + bounds[0] = 6; // X0 + bounds[1] = 0; + bounds[2] = ax; + } else { + bounds[0] = 7; // Y0 + bounds[1] = ay; + bounds[2] = ycount; + } + break; + } + return bounds; + } + + /** + * Rasterizes arc using clippind and dashing style + * @param x1 - the x coordinate of the left-upper corner of the arc bounds + * @param y1 - the y coordinate of the left-upper corner of the arc bounds + * @param width - the width of the arc bounds + * @param height - the height of the arc bounds + * @param angleStart - the start angle of the arc in degrees + * @param angleExtent - the angle extent in degrees + * @param clip - the MultiRectArea object of clipping area + * @return a MultiRectArea of rasterizer arc + */ + public static MultiRectArea rasterize(int x, int y, int width, int height, double angleStart, double angleExtent, MultiRectArea clip) { + + MultiRectArea mra = new MultiRectArea(false); + + int cx1, cx2, cy1, cy2; + cx1 = cx2 = x + width / 2; + cy1 = cy2 = y + height / 2; + + if (width % 2 == 0) { + cx2--; + } + + if (height % 2 == 0) { + cy2--; + } + + int a = width / 2; + int b = height / 2; + double c = Math.sqrt(a * a + b * b); + + int xcount, ycount; + if (a < b) { + xcount = (int)Math.ceil(a * a / c); + ycount = (int)Math.floor(b * b / c); + } else { + xcount = (int)Math.floor(a * a / c); + ycount = (int)Math.ceil(b * b / c); + } + + int[] xline = createLine(a, b, xcount, ycount); + int[] yline = createLine(b, a, ycount, xcount); + + // Correct lines + int i = xline.length; + while(xline[--i] > xcount) { + xline[i] = xcount; + } + + i = yline.length; + while(yline[--i] > ycount) { + yline[i] = ycount; + } + + if (Math.abs(angleExtent) >= 360) { + // Rasterize CIRCLE + addX0Line(mra, xline, cx2, cy2, b); + addX1Line(mra, xline, cx2, cy1, b); + addX2Line(mra, xline, cx1, cy1, b); + addX3Line(mra, xline, cx1, cy2, b); + addY0Line(mra, yline, cx2, cy2, a); + addY1Line(mra, yline, cx1, cy2, a); + addY2Line(mra, yline, cx1, cy1, a); + addY3Line(mra, yline, cx2, cy1, a); + } else { + // Rasterize ARC + angleStart = getNormAngle(angleStart); + double angleFinish = getNormAngle(angleStart + angleExtent); + + if (angleExtent < 0) { + double tmp = angleStart; + angleStart = angleFinish; + angleFinish = tmp; + } + + double radStart = -Math.toRadians(angleStart); + double radFinish = -Math.toRadians(angleFinish); + int ax1 = (int)(a * Math.cos(radStart)); + int ay1 = (int)(b * Math.sin(radStart)); + int ax2 = (int)(a * Math.cos(radFinish)); + int ay2 = (int)(b * Math.sin(radFinish)); + + int[] seg1 = getSegment1(angleStart, ax1, ay1, xcount, ycount); + int[] seg2 = getSegment2(angleFinish, ax2, ay2, xcount, ycount); + + // Start and Finish located in the same quater + if (angleStart < angleFinish && seg1[0] == seg2[0]) { + if (seg1[0] % 2 == 0) { + seg1[2] = seg2[2]; + } else { + seg1[1] = seg2[1]; + } + addSeg(mra, cx1, cy1, cx2, cy2, a, b, xline, yline, seg1); + return mra; + } + + addSeg(mra, cx1, cy1, cx2, cy2, a, b, xline, yline, seg1); + addSeg(mra, cx1, cy1, cx2, cy2, a, b, xline, yline, seg2); + + int startSeg = (seg1[0] + 1) % 8; + int finishSeg = seg2[0]; + + while (startSeg != finishSeg) { + switch(startSeg) { + case 0: + addY3Line(mra, yline, cx2, cy1, a); + break; + case 1: + addX1Line(mra, xline, cx2, cy1, b); + break; + case 2: + addX2Line(mra, xline, cx1, cy1, b); + break; + case 3: + addY2Line(mra, yline, cx1, cy1, a); + break; + case 4: + addY1Line(mra, yline, cx1, cy2, a); + break; + case 5: + addX3Line(mra, xline, cx1, cy2, b); + break; + case 6: + addX0Line(mra, xline, cx2, cy2, b); + break; + case 7: + addY0Line(mra, yline, cx2, cy2, a); + break; + } + startSeg = (startSeg + 1) % 8; + } + } + + if (clip != null) { + mra.intersect(clip); + } + + return mra; + } + +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/gl/render/JavaBlitter.java b/awt/org/apache/harmony/awt/gl/render/JavaBlitter.java new file mode 100644 index 000000000..67e0a59b7 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/render/JavaBlitter.java @@ -0,0 +1,611 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Igor V. Stolyarov + * @version $Revision$ + * Created on 18.11.2005 + * + */ +package org.apache.harmony.awt.gl.render; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +import org.apache.harmony.awt.gl.MultiRectArea; +import org.apache.harmony.awt.gl.Surface; +import org.apache.harmony.awt.gl.XORComposite; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * Java implenetation of the Blitter interface. Using when we can't + * draw images natively. + */ +public class JavaBlitter implements Blitter { + + /** + * Instead of multiplication and division we are using values from + * Lookup tables. + */ + static byte mulLUT[][]; // Lookup table for multiplication + static byte divLUT[][]; // Lookup table for division + + static{ + mulLUT = new byte[256][256]; + for(int i = 0; i < 256; i++){ + for(int j = 0; j < 256; j++){ + mulLUT[i][j] = (byte)((float)(i * j)/255 + 0.5f); + } + } + divLUT = new byte[256][256]; + for(int i = 1; i < 256; i++){ + for(int j = 0; j < i; j++){ + divLUT[i][j] = (byte)(((float)j / 255) / ((float)i/ 255) * 255 + 0.5f); + } + for(int j = i; j < 256; j++){ + divLUT[i][j] = (byte)255; + } + } + } + + final static int AlphaCompositeMode = 1; + final static int XORMode = 2; + + final static JavaBlitter inst = new JavaBlitter(); + + public static JavaBlitter getInstance(){ + return inst; + } + + public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY, + Surface dstSurf, int width, int height, AffineTransform sysxform, + AffineTransform xform, Composite comp, Color bgcolor, + MultiRectArea clip) { + + if(xform == null){ + blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width, height, + sysxform, comp, bgcolor, clip); + }else{ + double scaleX = xform.getScaleX(); + double scaleY = xform.getScaleY(); + double scaledX = dstX / scaleX; + double scaledY = dstY / scaleY; + AffineTransform at = new AffineTransform(); + at.setToTranslation(scaledX, scaledY); + xform.concatenate(at); + sysxform.concatenate(xform); + blit(srcX, srcY, srcSurf, 0, 0, dstSurf, width, height, + sysxform, comp, bgcolor, clip); + } + + } + + public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY, + Surface dstSurf, int width, int height, AffineTransform sysxform, + Composite comp, Color bgcolor, MultiRectArea clip) { + + if(sysxform == null) { + sysxform = new AffineTransform(); + } + int type = sysxform.getType(); + switch(type){ + case AffineTransform.TYPE_TRANSLATION: + dstX += sysxform.getTranslateX(); + dstY += sysxform.getTranslateY(); + case AffineTransform.TYPE_IDENTITY: + blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, + width, height, comp, bgcolor, clip); + break; + default: + int srcW = srcSurf.getWidth(); + int srcH = srcSurf.getHeight(); + + int w = srcX + width < srcW ? width : srcW - srcX; + int h = srcY + height < srcH ? height : srcH - srcY; + + ColorModel srcCM = srcSurf.getColorModel(); + Raster srcR = srcSurf.getRaster().createChild(srcX, srcY, + w, h, 0, 0, null); + + ColorModel dstCM = dstSurf.getColorModel(); + WritableRaster dstR = dstSurf.getRaster(); + + transformedBlit(srcCM, srcR, 0, 0, dstCM, dstR, dstX, dstY, w, h, + sysxform, comp, bgcolor, clip); + + } + } + + public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY, + Surface dstSurf, int width, int height, Composite comp, + Color bgcolor, MultiRectArea clip) { + + javaBlt(srcX, srcY, srcSurf.getWidth(), srcSurf.getHeight(), + srcSurf.getColorModel(), srcSurf.getRaster(), dstX, dstY, + dstSurf.getWidth(), dstSurf.getHeight(), + dstSurf.getColorModel(), dstSurf.getRaster(), + width, height, comp, bgcolor, clip); + + } + public void javaBlt(int srcX, int srcY, int srcW, int srcH, + ColorModel srcCM, Raster srcRast, int dstX, int dstY, + int dstW, int dstH, ColorModel dstCM, WritableRaster dstRast, + int width, int height, Composite comp, Color bgcolor, + MultiRectArea clip){ + + int srcX2 = srcW - 1; + int srcY2 = srcH - 1; + int dstX2 = dstW - 1; + int dstY2 = dstH - 1; + + if(srcX < 0){ + width += srcX; + srcX = 0; + } + if(srcY < 0){ + height += srcY; + srcY = 0; + } + + if(dstX < 0){ + width += dstX; + srcX -= dstX; + dstX = 0; + } + if(dstY < 0){ + height += dstY; + srcY -= dstY; + dstY = 0; + } + + if(srcX > srcX2 || srcY > srcY2) { + return; + } + if(dstX > dstX2 || dstY > dstY2) { + return; + } + + if(srcX + width > srcX2) { + width = srcX2 - srcX + 1; + } + if(srcY + height > srcY2) { + height = srcY2 - srcY + 1; + } + if(dstX + width > dstX2) { + width = dstX2 - dstX + 1; + } + if(dstY + height > dstY2) { + height = dstY2 - dstY + 1; + } + + if(width <= 0 || height <= 0) { + return; + } + + int clipRects[]; + if(clip != null) { + clipRects = clip.rect; + } else { + clipRects = new int[]{5, 0, 0, dstW - 1, dstH - 1}; + } + + boolean isAlphaComp = false; + int rule = 0; + float alpha = 0; + boolean isXORComp = false; + Color xorcolor = null; + CompositeContext cont = null; + + if(comp instanceof AlphaComposite){ + isAlphaComp = true; + AlphaComposite ac = (AlphaComposite) comp; + rule = ac.getRule(); + alpha = ac.getAlpha(); + }else if(comp instanceof XORComposite){ + isXORComp = true; + XORComposite xcomp = (XORComposite) comp; + xorcolor = xcomp.getXORColor(); + }else{ + cont = comp.createContext(srcCM, dstCM, null); + } + + for(int i = 1; i < clipRects[0]; i += 4){ + int _sx = srcX; + int _sy = srcY; + + int _dx = dstX; + int _dy = dstY; + + int _w = width; + int _h = height; + + int cx = clipRects[i]; // Clipping left top X + int cy = clipRects[i + 1]; // Clipping left top Y + int cx2 = clipRects[i + 2]; // Clipping right bottom X + int cy2 = clipRects[i + 3]; // Clipping right bottom Y + + if(_dx > cx2 || _dy > cy2 || dstX2 < cx || dstY2 < cy) { + continue; + } + + if(cx > _dx){ + int shx = cx - _dx; + _w -= shx; + _dx = cx; + _sx += shx; + } + + if(cy > _dy){ + int shy = cy - _dy; + _h -= shy; + _dy = cy; + _sy += shy; + } + + if(_dx + _w > cx2 + 1){ + _w = cx2 - _dx + 1; + } + + if(_dy + _h > cy2 + 1){ + _h = cy2 - _dy + 1; + } + + if(_sx > srcX2 || _sy > srcY2) { + continue; + } + + if(isAlphaComp){ + alphaCompose(_sx, _sy, srcCM, srcRast, _dx, _dy, + dstCM, dstRast, _w, _h, rule, alpha, bgcolor); + }else if(isXORComp){ + xorCompose(_sx, _sy, srcCM, srcRast, _dx, _dy, + dstCM, dstRast, _w, _h, xorcolor); + }else{ + Raster sr = srcRast.createChild(_sx, _sy, _w, _h, 0, 0, null); + WritableRaster dr = dstRast.createWritableChild(_dx, _dy, + _w, _h, 0, 0, null); + cont.compose(sr, dr, dr); + } + } + } + + void alphaCompose(int srcX, int srcY, ColorModel srcCM, Raster srcRast, + int dstX, int dstY, ColorModel dstCM, WritableRaster dstRast, + int width, int height, int rule, float alpha, Color bgcolor){ + + Object srcPixel, dstPixel; + int srcConstAllpha = (int)(alpha * 255 + 0.5f); + int srcRGB, dstRGB = 0; + + if(bgcolor != null){ + dstRGB = bgcolor.getRGB(); + } + + for(int sy = srcY, dy = dstY, srcYMax = srcY + height; sy < srcYMax; sy++, dy++){ + for(int sx = srcX, dx = dstX, srcXMax = srcX + width; sx < srcXMax; sx++, dx++){ + srcPixel = srcRast.getDataElements(sx, sy, null); + srcRGB = srcCM.getRGB(srcPixel); + if(bgcolor == null){ + dstPixel = dstRast.getDataElements(dx, dy, null); + dstRGB = dstCM.getRGB(dstPixel); + } + + dstRGB = compose(srcRGB, srcCM.isAlphaPremultiplied(), + dstRGB, dstCM.hasAlpha(), dstCM.isAlphaPremultiplied(), + rule, srcConstAllpha); + + dstPixel = dstCM.getDataElements(dstRGB, null); + dstRast.setDataElements(dx,dy,dstPixel); + } + } + } + + void xorCompose(int srcX, int srcY, ColorModel srcCM, Raster srcRast, + int dstX, int dstY, ColorModel dstCM, WritableRaster dstRast, + int width, int height, Color xorcolor){ + + Object srcPixel, dstPixel; + int xorRGB = xorcolor.getRGB(); + int srcRGB, dstRGB; + + for(int sy = srcY, dy = dstY, srcYMax = srcY + height; sy < srcYMax; sy++, dy++){ + for(int sx = srcX, dx = dstX, srcXMax = srcX + width; sx < srcXMax; sx++, dx++){ + srcPixel = srcRast.getDataElements(sx, sy, null); + dstPixel = dstRast.getDataElements(dx, dy, null); + + srcRGB = srcCM.getRGB(srcPixel); + dstRGB = dstCM.getRGB(dstPixel); + dstRGB = srcRGB ^ xorRGB ^ dstRGB; + + dstRGB = 0xff000000 | dstRGB; + dstPixel = dstCM.getDataElements(dstRGB, dstPixel); + dstRast.setDataElements(dx,dy,dstPixel); + + } + } + + } + + private void transformedBlit(ColorModel srcCM, Raster srcR, int srcX, int srcY, + ColorModel dstCM, WritableRaster dstR, int dstX, int dstY, + int width, int height, AffineTransform at, Composite comp, + Color bgcolor,MultiRectArea clip) { + + Rectangle srcBounds = new Rectangle(width, height); + Rectangle dstBlitBounds = new Rectangle(dstX, dstY, srcR.getWidth(), srcR.getHeight()); + + Rectangle transSrcBounds = getBounds2D(at, srcBounds).getBounds(); + Rectangle transDstBlitBounds = getBounds2D(at, dstBlitBounds).getBounds(); + + int translateX = transDstBlitBounds.x - transSrcBounds.x; + int translateY = transDstBlitBounds.y - transSrcBounds.y; + + AffineTransform inv = null; + try { + inv = at.createInverse(); + } catch (NoninvertibleTransformException e) { + return; + } + + double[] m = new double[6]; + inv.getMatrix(m); + + int clipRects[]; + if(clip != null) { + clipRects = clip.rect; + } else { + clipRects = new int[]{5, 0, 0, dstR.getWidth(), dstR.getHeight()}; + } + + int compType = 0; + int srcConstAlpha = 0; + int rule = 0; + int bgRGB = bgcolor == null ? 0 : bgcolor.getRGB(); + int srcRGB = 0, dstRGB = 0; + Object srcVal = null, dstVal = null; + if(comp instanceof AlphaComposite){ + compType = AlphaCompositeMode; + AlphaComposite ac = (AlphaComposite) comp; + rule = ac.getRule(); + srcConstAlpha = (int)(ac.getAlpha() * 255 + 0.5f); + }else if(comp instanceof XORComposite){ + compType = XORMode; + XORComposite xor = (XORComposite) comp; + bgRGB = xor.getXORColor().getRGB(); + } + + for(int i = 1; i < clipRects[0]; i += 4){ + Rectangle dstBounds = new Rectangle(clipRects[i], clipRects[i + 1], 0, 0); + dstBounds.add(clipRects[i + 2] + 1, clipRects[i + 1]); + dstBounds.add(clipRects[i + 2] + 1, clipRects[i + 3] + 1); + dstBounds.add(clipRects[i], clipRects[i + 3] + 1); + + Rectangle bounds = dstBounds.intersection(transDstBlitBounds); + + int minSrcX = srcBounds.x; + int minSrcY = srcBounds.y; + int maxSrcX = minSrcX + srcBounds.width; + int maxSrcY = minSrcY + srcBounds.height; + + int minX = bounds.x; + int minY = bounds.y; + int maxX = minX + bounds.width; + int maxY = minY + bounds.height; + + int hx = (int)((m[0] * 256) + 0.5); + int hy = (int)((m[1] * 256) + 0.5); + int vx = (int)((m[2] * 256) + 0.5); + int vy = (int)((m[3] * 256) + 0.5); + int sx = (int)((m[4] + m[0] * (bounds.x - translateX) + m[2] * (bounds.y - translateY)) * 256 + 0.5); + int sy = (int)((m[5] + m[1] * (bounds.x - translateX) + m[3] * (bounds.y - translateY)) * 256 + 0.5); + + vx -= hx * bounds.width; + vy -= hy * bounds.width; + + for(int y = minY; y < maxY; y++) { + for(int x = minX; x < maxX; x++) { + int px = sx >> 8; + int py = sy >> 8; + if (px >= minSrcX && py >= minSrcY && px < maxSrcX && py < maxSrcY) { + switch(compType){ + case AlphaCompositeMode: + srcVal = srcR.getDataElements(px , py , null); + srcRGB = srcCM.getRGB(srcVal); + if(bgcolor != null){ + dstRGB = bgRGB; + }else{ + dstVal = dstR.getDataElements(x, y, null); + dstRGB = dstCM.getRGB(dstVal); + } + dstRGB = compose(srcRGB, srcCM.isAlphaPremultiplied(), + dstRGB, dstCM.hasAlpha(), dstCM.isAlphaPremultiplied(), + rule, srcConstAlpha); + dstVal = dstCM.getDataElements(dstRGB, null); + dstR.setDataElements(x, y, dstVal); + break; + + case XORMode: + srcVal = srcR.getDataElements(px , py , null); + srcRGB = srcCM.getRGB(srcVal); + dstVal = dstR.getDataElements(x, y, null); + dstRGB = dstCM.getRGB(dstVal); + dstRGB = srcRGB ^ bgRGB; + + dstRGB = 0xff000000 | dstRGB; + dstVal = dstCM.getDataElements(dstRGB, null); + dstR.setDataElements(x, y, dstVal); + break; + + default: + // awt.37=Unknown composite type {0} + throw new IllegalArgumentException(Messages.getString("awt.37", //$NON-NLS-1$ + comp.getClass())); + } + } + sx += hx; + sy += hy; + } + sx += vx; + sy += vy; + } + } + + } + + private Rectangle2D getBounds2D(AffineTransform at, Rectangle r) { + int x = r.x; + int y = r.y; + int width = r.width; + int height = r.height; + + float[] corners = { + x, y, + x + width, y, + x + width, y + height, + x, y + height + }; + + at.transform(corners, 0, corners, 0, 4); + + Rectangle2D.Float bounds = new Rectangle2D.Float(corners[0], corners[1], 0 , 0); + bounds.add(corners[2], corners[3]); + bounds.add(corners[4], corners[5]); + bounds.add(corners[6], corners[7]); + + return bounds; + } + + private int compose(int srcRGB, boolean isSrcAlphaPre, + int dstRGB, boolean dstHasAlpha, boolean isDstAlphaPre, + int rule, int srcConstAlpha){ + + int sa, sr, sg, sb, da, dr, dg, db; + + sa = (srcRGB >> 24) & 0xff; + sr = (srcRGB >> 16) & 0xff; + sg = (srcRGB >> 8) & 0xff; + sb = srcRGB & 0xff; + + if(isSrcAlphaPre){ + sa = mulLUT[srcConstAlpha][sa] & 0xff; + sr = mulLUT[srcConstAlpha][sr] & 0xff; + sg = mulLUT[srcConstAlpha][sg] & 0xff; + sb = mulLUT[srcConstAlpha][sb] & 0xff; + }else{ + sa = mulLUT[srcConstAlpha][sa] & 0xff; + sr = mulLUT[sa][sr] & 0xff; + sg = mulLUT[sa][sg] & 0xff; + sb = mulLUT[sa][sb] & 0xff; + } + + da = (dstRGB >> 24) & 0xff; + dr = (dstRGB >> 16) & 0xff; + dg = (dstRGB >> 8) & 0xff; + db = dstRGB & 0xff; + + if(!isDstAlphaPre){ + dr = mulLUT[da][dr] & 0xff; + dg = mulLUT[da][dg] & 0xff; + db = mulLUT[da][db] & 0xff; + } + + int Fs = 0; + int Fd = 0; + switch(rule){ + case AlphaComposite.CLEAR: + break; + + case AlphaComposite.DST: + Fd = 255; + break; + + case AlphaComposite.DST_ATOP: + Fs = 255 - da; + Fd = sa; + break; + + case AlphaComposite.DST_IN: + Fd = sa; + break; + + case AlphaComposite.DST_OUT: + Fd = 255 - sa; + break; + + case AlphaComposite.DST_OVER: + Fs = 255 - da; + Fd = 255; + break; + + case AlphaComposite.SRC: + Fs = 255; + break; + + case AlphaComposite.SRC_ATOP: + Fs = da; + Fd = 255 - sa; + break; + + case AlphaComposite.SRC_IN: + Fs = da; + break; + + case AlphaComposite.SRC_OUT: + Fs = 255 - da; + break; + + case AlphaComposite.SRC_OVER: + Fs = 255; + Fd = 255 - sa; + break; + + case AlphaComposite.XOR: + Fs = 255 - da; + Fd = 255 - sa; + break; + } + dr = (mulLUT[sr][Fs] & 0xff) + (mulLUT[dr][Fd] & 0xff); + dg = (mulLUT[sg][Fs] & 0xff) + (mulLUT[dg][Fd] & 0xff); + db = (mulLUT[sb][Fs] & 0xff) + (mulLUT[db][Fd] & 0xff); + + da = (mulLUT[sa][Fs] & 0xff) + (mulLUT[da][Fd] & 0xff); + + if(!isDstAlphaPre){ + if(da != 255){ + dr = divLUT[da][dr] & 0xff; + dg = divLUT[da][dg] & 0xff; + db = divLUT[da][db] & 0xff; + } + } + if(!dstHasAlpha) { + da = 0xff; + } + dstRGB = (da << 24) | (dr << 16) | (dg << 8) | db; + + return dstRGB; + + } + +} diff --git a/awt/org/apache/harmony/awt/gl/render/JavaLineRasterizer.java b/awt/org/apache/harmony/awt/gl/render/JavaLineRasterizer.java new file mode 100644 index 000000000..eb6f7b5cb --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/render/JavaLineRasterizer.java @@ -0,0 +1,760 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.render; + +import org.apache.harmony.awt.gl.MultiRectArea; + + +public class JavaLineRasterizer { + + /** + * LineDasher class provides dashing for particular dash style + */ + public static class LineDasher { + + int index; + float pos; + float phase; + float dash[]; + float inv[]; + boolean visible; + + public LineDasher() { + } + + public LineDasher(float dash[], float phase) { + this.dash = dash; + this.phase = phase; + + inv = new float[dash.length]; + int j = dash.length; + for (float element : dash) { + inv[--j] = element; + } + index = 0; + while (phase > dash[index]) { + phase -= dash[index]; + index = (index + 1) % dash.length; + } + visible = index % 2 == 0; + } + + void move(float step) { // main dasher + pos += step; + step += phase; + while(step >= dash[index]) { + step -= dash[index]; + index = (index + 1) % dash.length; + visible = !visible; + } + phase = step; + } + + float nextDash() { + phase = 0.0f; + index = (index + 1) % dash.length; + visible = !visible; + return dash[index]; + } + + LineDasher createDiagonal(double k, float length, boolean invert) { + LineDasher local = new LineDasher(); + local.dash = new float[dash.length]; + if (invert) { // inverted dasher + move(length); + local.phase = (float)((dash[index] - phase) * k); + local.visible = visible; + local.index = inv.length - index - 1; + for(int i = 0; i < inv.length; i++) { + local.dash[i] = (float)(inv[i] * k); + } + } else { + local.phase = (float)(phase * k); + local.visible = visible; + local.index = index; + for(int i = 0; i < dash.length; i++) { + local.dash[i] = (float)(dash[i] * k); + } + move(length); + } + return local; + } + + LineDasher createOrtogonal(float length, boolean invert) { + LineDasher local = new LineDasher(); + local.dash = new float[dash.length]; + if (invert) { // inverted dasher + move(length); + local.phase = dash[index] - phase; + local.visible = visible; + local.index = inv.length - index - 1; + local.dash = inv; + } else { + local.phase = phase; + local.visible = visible; + local.index = index; + local.dash = dash; + move(length); + } + return local; + } + + LineDasher createChild(float start) { + LineDasher child = new LineDasher(); + child.phase = phase; + child.visible = visible; + child.index = index; + child.dash = dash; + child.move(start); + return child; + } + + } + + /** + * Line class provides rasterization for different line types + */ + abstract static class Line { + + int x1, y1, x2, y2; + int x, y; + MultiRectArea dst; + + Line(int x1, int y1, int x2, int y2, MultiRectArea dst) { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + this.dst = dst; + } + + static abstract class Diag extends Line { + int dx, dy, adx, ady, sx, sy; + int eBase, ePos, eNeg; + int xcount; + int e; + + Diag(int x1, int y1, int x2, int y2, MultiRectArea dst) { + super(x1, y1, x2, y2, dst); + dx = x2 - x1; + dy = y2 - y1; + sy = 1; + if (dx > 0) { + adx = dx; + sx = 1; + } else { + adx = -dx; + sx = -1; + } + ady = dy; + } + + float getLength() { + return (float)Math.sqrt(dx * dx + dy * dy); + } + + static class Hor extends Diag { + + Hor(int x1, int y1, int x2, int y2, MultiRectArea dst) { + super(x1, y1, x2, y2, dst); + eBase = ady + ady - adx; + ePos = 2 * (ady - adx); + eNeg = ady + ady; + xcount = adx; + } + + @Override + void rasterize() { + e = eBase; + x = x1; + y = y1; + rasterize(xcount); + } + + @Override + void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) { + e = eBase + 2 * (ady * Math.abs(nx1 - x1) - adx * Math.abs(ny1 - y1)); + x = nx1; + y = ny1; + rasterize(dx > 0 ? nx2 - nx1 : nx1 - nx2); + } + + @Override + void rasterize(int count) { + int px = x; + while (count-- > 0) { + if (e >= 0) { + if (sx > 0) { + dst.addRect(px, y, x, y); + } else { + dst.addRect(x, y, px, y); + } + x += sx; + y += sy; + e += ePos; + px = x; + } else { + e += eNeg; + x += sx; + } + } + if (sx > 0) { + dst.addRect(px, y, x, y); + } else { + dst.addRect(x, y, px, y); + } + } + + @Override + void skip(int count) { + while (count-- > 0) { + x += sx; + if (e >= 0) { + y += sy; + e += ePos; + } else { + e += eNeg; + } + } + } + + } + + static class Ver extends Diag { + + Ver(int x1, int y1, int x2, int y2, MultiRectArea dst) { + super(x1, y1, x2, y2, dst); + eBase = adx + adx - ady; + ePos = 2 * (adx - ady); + eNeg = adx + adx; + xcount = ady; + } + + @Override + void rasterize() { + e = eBase; + x = x1; + y = y1; + rasterize(xcount); + } + + @Override + void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) { + e = eBase + 2 * (adx * Math.abs(ny1 - y1) - ady * Math.abs(nx1 - x1)); + x = nx1; + y = ny1; + rasterize(ny2 - ny1); + } + + @Override + void rasterize(int count) { + int py = y; + while (count-- > 0) { + if (e >= 0) { + dst.addRect(x, py, x, y); + x += sx; + y += sy; + e += ePos; + py = y; + } else { + y += sy; + e += eNeg; + } + } + dst.addRect(x, py, x, y); + } + + @Override + void skip(int count) { + while (count-- > 0) { + y += sy; + if (e >= 0) { + x += sx; + e += ePos; + } else { + e += eNeg; + } + } + } + + } + + static class HorDashed extends Hor { + + LineDasher local; + + HorDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher, boolean invert) { + super(x1, y1, x2, y2, dst); + float length = getLength(); + local = dasher.createDiagonal(xcount / length, length, invert); + } + + @Override + void rasterize() { + e = eBase; + x = x1; + y = y1; + rasterizeDash(xcount, local); + } + + @Override + void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) { + e = eBase + 2 * (ady * Math.abs(nx1 - x1) - adx * Math.abs(ny1 - y1)); + x = nx1; + y = ny1; + rasterizeDash(Math.abs(nx2 - nx1), local.createChild(Math.abs(nx1 - x1))); + } + + } + + static class VerDashed extends Ver { + + LineDasher local; + + VerDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher, boolean invert) { + super(x1, y1, x2, y2, dst); + float length = getLength(); + local = dasher.createDiagonal(xcount / length, length, invert); + } + + @Override + void rasterize() { + e = eBase; + x = x1; + y = y1; + rasterizeDash(xcount, local); + } + + @Override + void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) { + e = eBase + 2 * (adx * Math.abs(ny1 - y1) - ady * Math.abs(nx1 - x1)); + x = nx1; + y = ny1; + rasterizeDash(ny2 - ny1, local.createChild(ny1 - y1)); + } + + } + + @Override + void rasterize(int[] clip, int index) { + int cx1 = clip[index + 0]; + int cy1 = clip[index + 1]; + int cx2 = clip[index + 2] + 1; + int cy2 = clip[index + 3] + 1; + + int code1 = + (x1 < cx1 ? 1 : 0) | (x1 >= cx2 ? 2 : 0) | + (y1 < cy1 ? 8 : 0) | (y1 >= cy2 ? 4 : 0); + int code2 = + (x2 < cx1 ? 1 : 0) | (x2 >= cx2 ? 2 : 0) | + (y2 < cy1 ? 8 : 0) | (y2 >= cy2 ? 4 : 0); + + // Outside + if ((code1 & code2) != 0) { + return; + } + + // Inside + if (code1 == 0 && code2 == 0) { + rasterize(); + return; + } + + // Clip + int nx1 = x1; + int ny1 = y1; + int nx2 = x2; + int ny2 = y2; + // need to clip + cx1 -= x1; cx2 -= x1; + cy1 -= y1; cy2 -= y1; +// int d; + int newx1 = 0, newy1 = 0, newx2 = 0, newy2 = 0; + if (code1 != 0) { + newx1 = Integer.MAX_VALUE; + if ((code1 & 8) != 0) { + // clip point 1 with top clip bound + newy1 = cy1; + newx1 = clipY(dx, dy, newy1, true); + + } else if ((code1 & 4) != 0) { + // clip point 1 with bottom clip bound + newy1 = cy2 - 1; + newx1 = clipY(dx, dy, newy1, false); + } + if ((code1 & 1) != 0 && (cx1 > newx1 || newx1 == Integer.MAX_VALUE)) { + // clip point 1 with left clip bound + newx1 = cx1; + newy1 = clipX(dx, dy, newx1, false); + } else if ((code1 & 2) != 0 && (newx1 >= cx2 || newx1 == Integer.MAX_VALUE)) { + // clip point 1 with right clip bound + newx1 = cx2 - 1; + newy1 = clipX(dx, dy, newx1, false); + } + if (newx1 < cx1 || newx1 >= cx2 || newy1 < cy1 || newy1 >= cy2) { + return; + } +// d = 2 * (ady * Math.abs(newx1) - adx * Math.abs(newy1)) + 2 * ady - adx; + } else { +// d = (ady << 1) - adx; + } + + if (code2 != 0) { + newx2=Integer.MAX_VALUE; + if ((code2 & 8) != 0) { + // clip point 2 with top clip bound + newy2 = cy1; + newx2 = clipY(dx, dy, newy2, true); + } else if ((code2 & 4) != 0) { + // clip point 2 with bottom clip bound + newy2 = cy2 - 1; + newx2 = clipY(dx, dy, newy2, false); + } + if ((code2 & 1) != 0 && (cx1 > newx2 || newx2 == Integer.MAX_VALUE)) { + // clip point 2 with left clip bound + newx2 = cx1; + newy2 = clipX(dx, dy, newx2, false); + } else if ((code2 & 2) != 0 && (newx2 >= cx2 || newx2 == Integer.MAX_VALUE)) { + // clip point 2 with right clip bound + newx2 = cx2 - 1; + newy2 = clipX(dx, dy, newx2, false); + } + if (newx2 < cx1 || newx2 >= cx2 || newy2 < cy1 || newy2 >= cy2) { + return; + } + nx2 = x1 + newx2; + ny2 = y1 + newy2; + } + nx1 = x1 + newx1; + ny1 = y1 + newy1; + + rasterizeClipped(nx1, ny1, nx2, ny2); + } + + abstract void rasterizeClipped(int nx1, int ny1, int nx2, int ny2); + + } + + static abstract class Ortog extends Line { + + Ortog(int x1, int y1, int x2, int y2, MultiRectArea dst) { + super(x1, y1, x2, y2, dst); + } + + static class Hor extends Ortog { + + int dx; + + Hor(int x1, int y1, int x2, int y2, MultiRectArea dst) { + super(x1, y1, x2, y2, dst); + dx = x2 - x1; + } + + @Override + void rasterize() { + if (dx > 0) { + dst.addRect(x1, y1, x2, y2); + } else { + dst.addRect(x2, y2, x1, y1); + } + } + + @Override + void rasterize(int step) { + int px = x; + if (dx > 0) { + x += step; + dst.addRect(px, y1, x - 1, y2); + } else { + x -= step; + dst.addRect(x + 1, y2, px, y1); + } + } + + @Override + void skip(int step) { + if (dx > 0) { + x += step; + } else { + x -= step; + } + } + + void rasterizeClipped(int nx1, int nx2) { + if (nx1 < nx2) { + dst.addRect(nx1, y1, nx2, y1); + } else { + dst.addRect(nx2, y1, nx1, y1); + } + } + + @Override + void rasterize(int[] clip, int index) { + if (y1 >= clip[index + 1] && y1 <= clip[index + 3]) { + int cx1 = clip[index + 0]; + int cx2 = clip[index + 2]; + if (x1 <= cx2 && x2 >= cx1) { + int nx1, nx2; + if (dx > 0) { + nx1 = Math.max(x1, cx1); + nx2 = Math.min(x2, cx2); + } else { + nx2 = Math.max(x2, cx1); + nx1 = Math.min(x1, cx2); + } + rasterizeClipped(nx1, nx2); + } + } + } + + } + + static class Ver extends Ortog { + + int dy; + + Ver(int x1, int y1, int x2, int y2, MultiRectArea dst) { + super(x1, y1, x2, y2, dst); + dy = y2 - y1; + } + + @Override + void rasterize() { + dst.addRect(x1, y1, x2, y2); + } + + @Override + void rasterize(int step) { + int py = y; + y += step; + dst.addRect(x1, py, x2, y - 1); + } + + @Override + void skip(int step) { + y += step; + } + + void rasterizeClipped(int ny1, int ny2) { + dst.addRect(x1, ny1, x1, ny2); + } + + @Override + void rasterize(int[] clip, int index) { + if (x1 >= clip[index] && x1 <= clip[index + 2]) { + int cy1 = clip[index + 1]; + int cy2 = clip[index + 3]; + if (y1 <= cy2 && y2 >= cy1) { + rasterizeClipped(Math.max(y1, cy1), Math.min(y2, cy2)); + } + } + } + + } + + static class HorDashed extends Hor { + + LineDasher local; + + HorDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher) { + super(x1, y1, x2, y2, dst); + dx = x2 - x1; + local = dasher.createOrtogonal(Math.abs(dx), false); + } + + @Override + void rasterize() { + x = x1; + y = y1; + rasterizeDash(Math.abs(dx), local); + } + + @Override + void rasterizeClipped(int nx1, int nx2) { + x = nx1; + y = y1; + rasterizeDash(Math.abs(nx2 - nx1), local.createChild(Math.abs(nx1 - x1))); + } + + } + + static class VerDashed extends Ver { + + LineDasher local; + + VerDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher, boolean invert) { + super(x1, y1, x2, y2, dst); + dy = y2 - y1; + local = dasher.createOrtogonal(dy, invert); + } + + @Override + void rasterize() { + x = x1; + y = y1; + rasterizeDash(dy, local); + } + + @Override + void rasterizeClipped(int ny1, int ny2) { + x = x1; + y = ny1; + rasterizeDash(ny2 - ny1, local.createChild(ny1)); + } + + } + + } + + abstract void rasterize(); + abstract void rasterize(int[] clip, int index); + abstract void rasterize(int count); + abstract void skip(int count); + + void rasterizeDash(int count, LineDasher dasher) { + float delta = dasher.dash[dasher.index] - dasher.phase; + int step = (int)delta; + delta -= step; + while(count > step) { + if (dasher.visible) { + rasterize(step); + } else { + skip(step); + } + count -= step; + delta += dasher.nextDash(); + step = (int)delta; + delta -= step; + } + if (count > 0 && dasher.visible) { + rasterize(count); + dasher.move(count); + } + } + + } + + /** + * Common clipping method + */ + static int clip(int dX1, int dX2, int cX, boolean top) { + int adX1 = dX1 < 0 ? -dX1 : dX1; + int adX2 = dX2 < 0 ? -dX2 : dX2; + if (adX1 <= adX2) { + // obtuse intersection angle + return ((dX1 << 1) * cX + (dX1 > 0 ? dX2 : -dX2)) / (dX2 << 1); + } + int k; + if (top) { + k = -dX1 + (dX2 < 0 ? 0 : dX1 > 0 ? (dX2 << 1) : -(dX2 << 1)); + } else { + k = dX1 + (dX2 > 0 ? 0 : dX1 > 0 ? (dX2 << 1) : -(dX2 << 1)); + } + + k += dX1 > 0 == dX2 > 0 ? -1 : 1; + return ((dX1 << 1) * cX + k) / (dX2 << 1); + } + + /** + * Clipping along X axis + */ + static int clipX(int dx, int dy, int cy, boolean top) { + return clip(dy, dx, cy, top); + } + + /** + * Clipping along Y axis + */ + static int clipY(int dx, int dy, int cx, boolean top) { + return clip(dx, dy, cx, top); + } + + /** + * Rasterizes line using clippind and dashing style + * @param x1 - the x coordinate of the first control point + * @param y1 - the y coordinate of the first control point + * @param x2 - the x coordinate of the second control point + * @param y2 - the y coordinate of the second control point + * @param clip - the MultiRectArea object of clipping area + * @param dasher - the dasher style + * @param invert - the invert indicator, always false + * @return a MultiRectArea of rasterizer line + */ + public static MultiRectArea rasterize(int x1, int y1, int x2, int y2, MultiRectArea clip, LineDasher dasher, boolean invert) { + + MultiRectArea dst = new MultiRectArea(false); + int dx = x2 - x1; + int dy = y2 - y1; + + // Point + if (dx == 0 && dy == 0) { + if ((clip == null || clip.contains(x1, y1)) && (dasher == null || dasher.visible)) { + dst = new MultiRectArea(x1, y1, x1, y1); + } + return dst; + } + + if (dy < 0) { + return rasterize(x2, y2, x1, y1, clip, dasher, true); + } + + Line line; + if (dasher == null) { + if (dx == 0) { + line = new Line.Ortog.Ver(x1, y1, x2, y2, dst); + } else + if (dy == 0) { + line = new Line.Ortog.Hor(x1, y1, x2, y2, dst); + } else { + if (dy < Math.abs(dx)) { + line = new Line.Diag.Hor(x1, y1, x2, y2, dst); + } else { + line = new Line.Diag.Ver(x1, y1, x2, y2, dst); + } + } + } else { + if (dx == 0) { + line = new Line.Ortog.VerDashed(x1, y1, x2, y2, dst, dasher, invert); + } else + if (dy == 0) { + line = new Line.Ortog.HorDashed(x1, y1, x2, y2, dst, dasher); + } else { + if (dy < Math.abs(dx)) { + line = new Line.Diag.HorDashed(x1, y1, x2, y2, dst, dasher, invert); + } else { + line = new Line.Diag.VerDashed(x1, y1, x2, y2, dst, dasher, invert); + } + } + } + + + if (clip == null || clip.isEmpty()) { + line.rasterize(); + } else { + for(int i = 1; i < clip.rect[0]; i += 4) { + line.rasterize(clip.rect, i); + } + } + + return dst; + } + +} diff --git a/awt/org/apache/harmony/awt/gl/render/JavaShapeRasterizer.java b/awt/org/apache/harmony/awt/gl/render/JavaShapeRasterizer.java new file mode 100644 index 000000000..dbaaf539a --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/render/JavaShapeRasterizer.java @@ -0,0 +1,475 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Denis M. Kishenko + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.render; + +import java.awt.Shape; +import java.awt.geom.PathIterator; + +import org.apache.harmony.awt.gl.MultiRectArea; +import org.apache.harmony.awt.internal.nls.Messages; + +public class JavaShapeRasterizer { + + static final int POINT_CAPACITY = 16; + + int edgesCount; + int edgeCur; + int[] edgesX; + int[] edgesY; + int[] edgesYS; // Y coordinate of edge START point + int[] edgesN; + int[] edgesDY; + int[] bounds; + int boundCount; + boolean[] edgesExt; // Extremal points + + int activeCount; + float[] activeX; + int[] activeYEnd; + float[] activeXStep; + int[] activeDY; + boolean[] activeExt; + + int[] crossX; + int[] crossDY; + + Filler filler; + + /** + * Rasterization filler for different path rules + */ + static abstract class Filler { + + static class NonZero extends Filler { + @Override + void add(MultiRectArea.LineCash rect, int[] points, int[] orient, int length, int y) { + + int[] dst = new int[length]; + int dstLength = 1; + dst[0] = points[0]; + int count = 0; + boolean inside = true; + for(int i = 0; i < length; i++) { + count += orient[i] > 0 ? 1 : -1; + if (count == 0) { + dst[dstLength++] = points[i]; + inside = false; + } else { + if (!inside) { + dst[dstLength++] = points[i]; + inside = true; + } + } + + } + + for(int i = 1; i < dstLength; i += 2) { + dst[i]--; + } + + dstLength = excludeEmpty(dst, dstLength); +// System.out.println("test"); + + dstLength = union(dst, dstLength); + + rect.addLine(dst, dstLength); + } + } + + static class EvenOdd extends Filler { + @Override + void add(MultiRectArea.LineCash rect, int[] points, int[] orient, int length, int y) { + /* + int[] buf = new int[length]; + int j = 0; + for(int i = 0; i < length - 1; i++) { + if (points[i] != points[i + 1]) { + buf[j++] = points[i]; + } + } + */ + for(int i = 1; i < length; i += 2) { + points[i]--; + } + + length = excludeEmpty(points, length); +// System.out.println("test"); + + length = union(points, length); + rect.addLine(points, length); + /* + for(int i = 0; i < length;) { + rect.add(points[i++], y, points[i++], y); + } + */ + } + } + + abstract void add(MultiRectArea.LineCash rect, int[] points, int[] orient, int length, int y); + + static int excludeEmpty(int[] points, int length) { + int i = 0; + while(i < length) { + if (points[i] <= points[i + 1]) { + i += 2; + } else { + length -= 2; + System.arraycopy(points, i + 2, points, i, length - i); + } + } + return length; + } + + static int union(int[] points, int length) { + int i = 1; + while(i < length - 1) { + if (points[i] < points[i - 1]) { + System.arraycopy(points, i + 1, points, i - 1, length - i - 1); + length -= 2; + } else + if (points[i] >= points[i + 1] - 1) { + System.arraycopy(points, i + 2, points, i, length - i - 2); + length -= 2; + } else { + i += 2; + } + } + return length; + } + + } + + public JavaShapeRasterizer() { + } + + /** + * Checks buffer size and realloc if necessary + */ + int[] checkBufSize(int[] buf, int size) { + if (size == buf.length) { + int[] tmp; + tmp = new int[size + POINT_CAPACITY]; + System.arraycopy(buf, 0, tmp, 0, buf.length); + buf = tmp; + } + return buf; + } + + /** + * Adds to the buffers new edge + */ + void addEdge(int x, int y, int num) { + edgesX = checkBufSize(edgesX, edgesCount); + edgesY = checkBufSize(edgesY, edgesCount); + edgesN = checkBufSize(edgesN, edgesCount); + edgesX[edgesCount] = x; + edgesY[edgesCount] = y; + edgesN[edgesCount] = (num << 16) | edgesCount; + edgesCount++; + } + + /** + * Prepare all buffers and variable to rasterize shape + */ + void makeBuffer(PathIterator path, double flatness) { + edgesX = new int[POINT_CAPACITY]; + edgesY = new int[POINT_CAPACITY]; + edgesN = new int[POINT_CAPACITY]; + bounds = new int[POINT_CAPACITY]; + boundCount = 0; + edgesCount = 0; + + if (path.getWindingRule() == PathIterator.WIND_EVEN_ODD) { + filler = new Filler.EvenOdd(); + } else { + filler = new Filler.NonZero(); + } + float[] coords = new float[2]; + boolean closed = true; + while (!path.isDone()) { + switch(path.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + if (!closed) { + boundCount++; + bounds = checkBufSize(bounds, boundCount); + bounds[boundCount] = edgesCount; + } + addEdge((int)coords[0], (int)coords[1], boundCount); + closed = false; + break; + case PathIterator.SEG_LINETO: + addEdge((int)coords[0], (int)coords[1], boundCount); + break; + case PathIterator.SEG_CLOSE: + boundCount++; + bounds = checkBufSize(bounds, boundCount); + bounds[boundCount] = edgesCount; + closed = true; + break; + default: + // awt.36=Wrong segment + throw new RuntimeException(Messages.getString("awt.36")); //$NON-NLS-1$ + } + path.next(); + } + if (!closed) { + boundCount++; + bounds = checkBufSize(bounds, boundCount); + bounds[boundCount] = edgesCount; + } + } + + /** + * Sort buffers + */ + void sort(int[] master, int[] slave, int length) { + for(int i = 0; i < length - 1; i++) { + int num = i; + int min = master[num]; + for(int j = i + 1; j < length; j++) { + if (master[j] < min) { + num = j; + min = master[num]; + } + } + if (num != i) { + master[num] = master[i]; + master[i] = min; + min = slave[num]; + slave[num] = slave[i]; + slave[i] = min; + } + } + } + + int getNext(int cur) { + int n = edgesN[cur]; + int bound = n >> 16; + int num = (n & 0xFFFF) + 1; + if (num == bounds[bound + 1]) { + return bounds[bound]; + } + return num; + } + + int getPrev(int cur) { + int n = edgesN[cur]; + int bound = n >> 16; + int num = (n & 0xFFFF) - 1; + if (num < bounds[bound]) { + return bounds[bound + 1] - 1; + } + return num; + } + + int getNextShape(int cur) { + int bound = edgesN[cur] >> 16; + return bounds[bound + 1]; + } + + void init() { + + edgesYS = new int[edgesCount]; + System.arraycopy(edgesY, 0, edgesYS, 0, edgesCount); + // Create edgesDY + edgesDY = new int[edgesCount]; + for(int i = 0; i < edgesCount; i++) { + int dy = edgesY[getNext(i)] - edgesY[i]; + edgesDY[i] = dy; + } + + // Create edgesExt + edgesExt = new boolean[edgesCount]; + int prev = -1; + int i = 0; + int pos = 0; + while(i < edgesCount) { + + TOP: { + do { + if (edgesDY[i] > 0) { + break TOP; + } + i = getNext(i); + } while (i != pos); + i = pos = getNextShape(i); + continue; + } + + BOTTOM: { + do { + if (edgesDY[i] < 0) { + break BOTTOM; + } + if (edgesDY[i] > 0) { + prev = i; + } + i = getNext(i); + } while (i != pos); + i = pos = getNextShape(i); + continue; + } + + if (prev != -1) { + edgesExt[prev] = true; + } + edgesExt[i] = true; + } + + // Sort edgesY and edgesN + sort(edgesYS, edgesN, edgesCount); + + edgeCur = 0; + activeCount = 0; + activeX = new float[edgesCount]; + activeYEnd = new int[edgesCount]; + activeXStep = new float[edgesCount]; + activeDY = new int[edgesCount]; + activeExt = new boolean[edgesCount]; + + crossX = new int[edgesCount]; + crossDY = new int[edgesCount]; + } + + /** + * Marks edge as active + */ + void addActiveEdge(int levelY, int start, int end, boolean back) { + int dy = back ? -edgesDY[end] : edgesDY[start]; + if (dy <= 0) { + return; + } + int x1 = edgesX[start]; + int dx = edgesX[end] - x1; + activeX[activeCount] = x1; + activeYEnd[activeCount] = edgesY[end]; + activeXStep[activeCount] = dx / (float)dy; + activeDY[activeCount] = back ? -dy : dy; + activeExt[activeCount] = back ? edgesExt[end] : edgesExt[start]; + activeCount++; + } + + /** + * Find new active edges + */ + int findActiveEdges(int levelY) { + + int edgeActive = edgeCur; + while (edgeActive < edgesCount && edgesYS[edgeActive] == levelY) { + edgeActive++; + } + + int activeNext = edgeActive; + + while (edgeActive > edgeCur) { + edgeActive--; + int num = edgesN[edgeActive] & 0xFFFF; + addActiveEdge(levelY, num, getPrev(edgeActive), true); + addActiveEdge(levelY, num, getNext(edgeActive), false); + } + + edgeCur = activeNext; + + if (activeNext == edgesCount) { + return edgesY[edgesCount - 1]; + } + return edgesYS[activeNext]; + } + + /** + * Rasterizes shape with particular flatness + * @param shape - the souze Shape to be rasterized + * @param flatness - the rasterization flatness + * @return a MultiRectArea of rasterized shape + */ + public MultiRectArea rasterize(Shape shape, double flatness) { + + PathIterator path = shape.getPathIterator(null, flatness); + + // Shape is empty + if (path.isDone()) { + return new MultiRectArea(); + } + + makeBuffer(path, flatness); + + init(); + + int y = edgesYS[0]; + int nextY = y; + int crossCount; + + MultiRectArea.LineCash rect = new MultiRectArea.LineCash(edgesCount); + rect.setLine(y); + + while(y <= nextY) { + + crossCount = 0; + + if (y == nextY) { + + int i = activeCount; + while(i > 0) { + i--; + if (activeYEnd[i] == y) { + + activeCount--; + int length = activeCount - i; + if (length != 0) { + int pos = i + 1; + System.arraycopy(activeX, pos, activeX, i, length); + System.arraycopy(activeYEnd, pos, activeYEnd, i, length); + System.arraycopy(activeXStep, pos, activeXStep, i, length); + System.arraycopy(activeDY, pos, activeDY, i, length); + System.arraycopy(activeExt, pos, activeExt, i, length); + } + } + } + + nextY = findActiveEdges(y); + } + + // Get X crossings + for(int i = 0; i < activeCount; i++) { + crossX[crossCount] = (int)Math.ceil(activeX[i]); + crossDY[crossCount] = activeDY[i]; + crossCount++; + } + + if (crossCount == 0) { + rect.skipLine(); + } else { + // Sort X crossings + sort(crossX, crossDY, crossCount); + filler.add(rect, crossX, crossDY, crossCount, y); + } + + for(int i = 0; i < activeCount; i++) { + activeX[i] += activeXStep[i]; + } + + y++; + } + + return rect; + } + +} diff --git a/awt/org/apache/harmony/awt/gl/render/JavaTextRenderer.java b/awt/org/apache/harmony/awt/gl/render/JavaTextRenderer.java new file mode 100644 index 000000000..322ba5769 --- /dev/null +++ b/awt/org/apache/harmony/awt/gl/render/JavaTextRenderer.java @@ -0,0 +1,263 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Ilya S. Okomin + * @version $Revision$ + */ +package org.apache.harmony.awt.gl.render; + +import java.awt.*; +import java.awt.image.*; + + +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; + +import org.apache.harmony.awt.gl.TextRenderer; +import org.apache.harmony.awt.gl.font.CommonGlyphVector; +import org.apache.harmony.awt.gl.font.FontPeerImpl; +import org.apache.harmony.awt.gl.font.Glyph; +import org.apache.harmony.awt.gl.image.BufferedImageGraphics2D; + +public class JavaTextRenderer extends TextRenderer { + + public static final JavaTextRenderer inst = new JavaTextRenderer(); + + @Override + public void drawGlyphVector(Graphics2D g, GlyphVector glyphVector, + float x, float y) { + + AffineTransform at = g.getTransform(); + Rectangle c = g.getClipBounds(); + if (at != null){ + int atType = at.getType(); + if (atType == AffineTransform.TYPE_TRANSLATION) { + c.translate((int)Math.round(at.getTranslateX()), (int)Math.round(at.getTranslateY())); + } + } + + WritableRaster wr = ((BufferedImageGraphics2D)g).getWritableRaster(); + ColorModel cm = ((BufferedImageGraphics2D)g).getColorModel(); + + Rectangle rBounds = wr.getBounds(); + + Object color = cm.getDataElements(g.getColor().getRGB(), null); + + drawClipGlyphVector(wr, color, glyphVector, (int)Math.round(x + at.getTranslateX()), (int)Math.round(y + at.getTranslateY()), + Math.max(c.x,rBounds.x), + Math.max(c.y,rBounds.y), + Math.min((int)Math.round(c.getMaxX()), (int)Math.round(rBounds.getMaxX())), + Math.min((int)Math.round(c.getMaxY()), (int)Math.round(rBounds.getMaxY()))); + + } + + @SuppressWarnings("deprecation") + @Override + public void drawString(Graphics2D g, String str, float x, float y) { + AffineTransform at = g.getTransform(); + Rectangle c = g.getClipBounds(); + if (at != null){ + int atType = at.getType(); + if (atType == AffineTransform.TYPE_TRANSLATION) { + c.translate((int)Math.round(at.getTranslateX()), (int)Math.round(at.getTranslateY())); + } + } + WritableRaster wr = ((BufferedImageGraphics2D)g).getWritableRaster(); + ColorModel cm = ((BufferedImageGraphics2D)g).getColorModel(); + Rectangle rBounds = wr.getBounds(); + + Object color = cm.getDataElements(g.getColor().getRGB(), null); + + drawClipString(wr, color, str, (FontPeerImpl) (g.getFont().getPeer()), + (int)Math.round(x + at.getTranslateX()), (int)Math.round(y + at.getTranslateY()), + Math.max(c.x,rBounds.x), + Math.max(c.y,rBounds.y), + Math.min((int)Math.round(c.getMaxX()), (int)Math.round(rBounds.getMaxX())), + Math.min((int)Math.round(c.getMaxY()), (int)Math.round(rBounds.getMaxY()))); + + } + + /** + * + * Draws string on specified raster at desired position. + * + * @param raster specified WritableRaster to draw at + * @param color color of the text + * @param glyphVector GlyphVector object to draw + * @param x start X position to draw + * @param y start Y position to draw + * @param cMinX minimum x of the raster area to draw + * @param cMinY minimum y of the raster area to draw + * @param cMaxX maximum x of the raster area to draw + * @param cMaxY maximum y of the raster area to draw + */ + public void drawClipGlyphVector(WritableRaster raster, Object color, + GlyphVector glyphVector, int x, int y, + int cMinX, int cMinY, int cMaxX, int cMaxY) { + // TODO: implement complex clipping + + int xSrcSurf, ySrcSurf; // Start point in String rectangle + int xDstSurf, yDstSurf; // Start point in Surface rectangle + int clWidth, clHeight; + + for (int i = 0; i < glyphVector.getNumGlyphs(); i++) { + Glyph gl = ((CommonGlyphVector) glyphVector).vector[i]; + + if (gl.getPointWidth() == 0) { + continue; + } + + byte[] data = gl.getBitmap(); + if (data != null) { + Point2D pos = glyphVector.getGlyphPosition(i); + + xSrcSurf = 0;//gl.bmp_left; + ySrcSurf = 0;//gl.bmp_rows - gl.bmp_top; + + xDstSurf = x + (int)pos.getX() + (int) gl.getGlyphPointMetrics().getLSB();// + gl.bmp_left; + yDstSurf = y - gl.bmp_top/*getPointHeight()*/ + (int) pos.getY();// - (gl.bmp_rows-gl.bmp_top); + + int textWidth = gl.bmp_width; + int textHeight = gl.getPointHeight(); + + // if Regions don't intersect + if ((xDstSurf > cMaxX) || (yDstSurf > cMaxY) || (xDstSurf + textWidth < cMinX) + || (yDstSurf + textHeight < cMinY)) { + // Nothing to do + } else { + if (xDstSurf >= cMinX) { + clWidth = Math.min(textWidth, cMaxX - xDstSurf); + } else { + xSrcSurf += cMinX - xDstSurf; + clWidth = Math.min(cMaxX - cMinX, textWidth - (cMinX - xDstSurf)); + xDstSurf = cMinX; + } + if (yDstSurf >= cMinY) { + clHeight = Math.min(textHeight, cMaxY - yDstSurf); + } else { + ySrcSurf += cMinY - yDstSurf; + clHeight = Math.min(cMaxY - cMinY, textHeight - (cMinY - yDstSurf)); + yDstSurf = cMinY; + } + // Drawing on the Raster + for (int h=0; h cMaxX) || (yDstSurf > cMaxY) || (xDstSurf + textWidth < cMinX) + || (yDstSurf + textHeight < cMinY)) { + // Nothing to do + } else { + if (xDstSurf >= cMinX) { + clWidth = Math.min(textWidth, cMaxX - xDstSurf); + } else { + xSrcSurf += cMinX - xDstSurf; + clWidth = Math.min(cMaxX - cMinX, textWidth - (cMinX - xDstSurf)); + xDstSurf = cMinX; + } + if (yDstSurf >= cMinY) { + clHeight = Math.min(textHeight, cMaxY - yDstSurf); + } else { + ySrcSurf += cMinY - yDstSurf; + clHeight = Math.min(cMaxY - cMinY, textHeight - (cMinY - yDstSurf)); + yDstSurf = cMinY; + } + + // Drawing on the Raster + for (int h=0; h imInstances; // Map + private final Map localeIM; // Map last user-selected IM for locale + private final Set notifyIM; // set of IMs to notify of client window bounds changes + + /** + * a flag indicating that IM should be notified of client window + * position/visibility changes as soon as it is activated(new client + * appears) + */ + private boolean pendingClientNotify; + private Component nextComp; // component to gain focus after endComposition() + //???AWT: private final Set imWindows; // set of all IM windows created by this instance + private final NativeIM nativeIM; + + + + public InputMethodContext() { + notifyIM = new HashSet(); +//???AWT: imWindows = new HashSet(); + imInstances = new HashMap(); + localeIM = new HashMap(); + selectInputMethod(Locale.US); // not default? + nativeIM = (NativeIM) inputMethod; + } + + //???AWT + /* + @Override + public void dispatchEvent(AWTEvent event) { + int id = event.getID(); + if ((id >= FocusEvent.FOCUS_FIRST) && (id <=FocusEvent.FOCUS_LAST)) { + dispatchFocusEvent((FocusEvent) event); + } else { + // handle special KEY_PRESSED + // event to show IM selection menu + if (id == KeyEvent.KEY_PRESSED) { + KeyEvent ke = (KeyEvent) event; + IMManager.selectIM(ke, this, + IMManager.getWindow(ke.getComponent())); + } + // dispatch all input events to the current IM: + if (inputMethod != null) { + inputMethod.dispatchEvent(event); + } + } + } + + private void dispatchFocusEvent(FocusEvent fe) { + switch (fe.getID()) { + case FocusEvent.FOCUS_LOST: + if (inputMethod != null) { + inputMethod.deactivate(fe.isTemporary()); + } + break; + case FocusEvent.FOCUS_GAINED: + + Component comp = fe.getComponent(); + if (imWindows.contains(comp)) { + // prevent activating when IM windows + // attached to this context gain focus + return; + } + InputMethodContext lastActive = IMManager.getLastActiveIMC(); + if ((lastActive != this) && (lastActive != null)) { + lastActive.hideWindows(); + } + if (inputMethod != null) { + activateIM(inputMethod); + if (!getCompositionWindow().isEmpty()) { + IMManager.showCompositionWindow(composeWindow); + } + if (client == comp) { + if (nextComp != null) { + // temporarily got focus to + // end composition + endComposition(); + + // transfer focus to new client + client = nextComp; + nextComp = null; + client.requestFocusInWindow(); + } + } else if ((client != null) && getCompositionWindow().isVisible()) { + // temporarily return focus back + // to previous client to be able + // to end composition + nextComp = comp; + client.requestFocusInWindow(); + } else { + client = comp; + } + } + if (pendingClientNotify) { + notifyClientWindowChange(IMManager.getWindow(comp).getBounds()); + } + break; + } + + } + + private void activateIM(InputMethod im) { + im.activate(); + if ((nativeIM != null) && (im != nativeIM)) { + // when Java IM is active + // native input method editor must be + // explicitly disabled + nativeIM.disableIME(); + } + IMManager.setLastActiveIMC(this); + } + + @SuppressWarnings("deprecation") + private void hideWindows() { + if (inputMethod != null) { + inputMethod.hideWindows(); + } + if (composeWindow != null) { + composeWindow.hide(); + } + } + + private void createCompositionWindow() { + composeWindow = new CompositionWindow(client); + } + + private CompositionWindow getCompositionWindow() { + if (composeWindow == null) { + createCompositionWindow(); + } + composeWindow.setClient(client); + return composeWindow; + } + */ + + /** + * Gets input method requests for the current client + * irrespective of input style. + * @return input method requests of composition window if + * client is passive, + * otherwise input method requests of client + */ + private InputMethodRequests getIMRequests() { + InputMethodRequests imRequests = null; + + if (client != null) { + imRequests = client.getInputMethodRequests(); + //???AWT + /* + if (imRequests == null) { + imRequests = getCompositionWindow().getInputMethodRequests(); + } + */ + } + + return imRequests; + } + + /** + * Gets input method requests for the current client & input style. + * @return input method requests of composition window if + * input style is "below-the-spot"(or client is passive), + * otherwise client input method requests + */ + private InputMethodRequests getStyleIMRequests() { + //???AWT + /* + if (IMManager.belowTheSpot()) { + return getCompositionWindow().getInputMethodRequests(); + } + */ + return getIMRequests(); + } + + @Override + public void dispose() { + if (inputMethod != null) { + closeIM(inputMethod); + inputMethod.dispose(); + } + notifyIM.clear(); + super.dispose(); + } + + @Override + public void endComposition() { + if (inputMethod != null) { + inputMethod.endComposition(); + } + super.endComposition(); + } + + @Override + public Object getInputMethodControlObject() { + if (inputMethod != null) { + return inputMethod.getControlObject(); + } + return super.getInputMethodControlObject(); + } + + @Override + public Locale getLocale() { + if (inputMethod != null) { + return inputMethod.getLocale(); + } + return super.getLocale(); + } + + @Override + public boolean isCompositionEnabled() { + if (inputMethod != null) { + return inputMethod.isCompositionEnabled(); + } + return super.isCompositionEnabled(); + } + + @Override + public void reconvert() { + if (inputMethod != null) { + inputMethod.reconvert(); + } + super.reconvert(); + } + + //???AWT + /* + @Override + public void removeNotify(Component client) { + if ((inputMethod != null) && (client == this.client)) { + inputMethod.removeNotify(); + client = null; + // set flag indicating that IM should be notified + // as soon as it is activated(new client appears) + pendingClientNotify = true; + } + + super.removeNotify(client); + } + */ + + @Override + public boolean selectInputMethod(Locale locale) { + + if ((inputMethod != null) && inputMethod.setLocale(locale)) { + return true; + } + // first + // take last user-selected IM for locale + InputMethod newIM = localeIM.get(locale); + + // if not found search through IM descriptors + // and take already created instance if exists + // or create, store new IM instance in descriptor->instance map + //???AWT + /* + if (newIM == null) { + try { + newIM = getIMInstance(IMManager.getIMDescriptors().iterator(), + locale); + } catch (Exception e) { + // ignore exceptions - just return false + } + } + */ + + return switchToIM(locale, newIM); + } + + private boolean switchToIM(Locale locale, InputMethod newIM) { + //???AWT + /* + if (newIM != null) { + closeIM(inputMethod); + client = KeyboardFocusManager. + getCurrentKeyboardFocusManager().getFocusOwner(); + initIM(newIM, locale); + inputMethod = newIM; + + return true; + } + */ + return false; + } + + /** + * Is called when IM is selected from UI + */ + void selectIM(InputMethodDescriptor imd, Locale locale) { + try { + switchToIM(locale, getIMInstance(imd)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Gets input method instance for the given + * locale from the given list of descriptors + * @param descriptors iterator of the list of IM descriptors + * @param locale the locale to be supported by the IM + * @return input method instance + * @throws Exception + */ + private InputMethod getIMInstance(Iterator descriptors, + Locale locale) throws Exception { + while (descriptors.hasNext()) { + InputMethodDescriptor desc = descriptors.next(); + Locale[] locs = desc.getAvailableLocales(); + for (Locale element : locs) { + if (locale.equals(element)) { + return getIMInstance(desc); + } + } + } + return null; + } + + private InputMethod getIMInstance(InputMethodDescriptor imd) throws Exception { + InputMethod im = imInstances.get(imd); + if (im == null) { + im = imd.createInputMethod(); + im.setInputMethodContext(this); + imInstances.put(imd, im); + } + return im; + } + + private void initIM(InputMethod im, Locale locale) { + if (im == null) { + return; + } + im.setLocale(locale); + im.setCharacterSubsets(null); + //???AWT: activateIM(im); + try { + im.setCompositionEnabled(inputMethod != null ? + inputMethod.isCompositionEnabled() : true); + } catch (UnsupportedOperationException uoe) { + + } + + } + + private void closeIM(InputMethod im) { + if (im == null) { + return; + } + if (im.isCompositionEnabled()) { + im.endComposition(); + } + + im.deactivate(true); + im.hideWindows(); + + } + + @Override + public void setCharacterSubsets(Subset[] subsets) { + if (inputMethod != null) { + inputMethod.setCharacterSubsets(subsets); + } + super.setCharacterSubsets(subsets); + } + + @Override + public void setCompositionEnabled(boolean enable) { + if (inputMethod != null) { + inputMethod.setCompositionEnabled(enable); + } + super.setCompositionEnabled(enable); + } + + //???AWT + /* + public JFrame createInputMethodJFrame(String title, + boolean attachToInputContext) { + JFrame jf = new IMJFrame(title, attachToInputContext ? this : null); + imWindows.add(jf); + return jf; + } + + public Window createInputMethodWindow(String title, + boolean attachToInputContext) { + Window w = new IMWindow(title, attachToInputContext ? this : null); + imWindows.add(w); + return w; + } + */ + + @SuppressWarnings("deprecation") + public void dispatchInputMethodEvent(int id, + AttributedCharacterIterator text, + int committedCharacterCount, + TextHitInfo caret, + TextHitInfo visiblePosition) { + if (client == null) { + return; + } + //???AWT + /* + InputMethodEvent ime = new InputMethodEvent(client, id, text, + committedCharacterCount, + caret, visiblePosition); + + + if ((client.getInputMethodRequests() != null) && + !IMManager.belowTheSpot()) { + + client.dispatchEvent(ime); + } else { + + // show/hide composition window if necessary + if (committedCharacterCount < text.getEndIndex()) { + IMManager.showCompositionWindow(getCompositionWindow()); + } else { + getCompositionWindow().hide(); + } + composeWindow.getActiveClient().dispatchEvent(ime); + } + */ + + } + + public void enableClientWindowNotification(InputMethod inputMethod, + boolean enable) { + if (enable) { + notifyIM.add(inputMethod); + //???AWT + /* + if (client != null) { + notifyClientWindowChange(IMManager.getWindow(client).getBounds()); + } else { + pendingClientNotify = true; + } + */ + } else { + notifyIM.remove(inputMethod); + } + + } + + public AttributedCharacterIterator cancelLatestCommittedText( + Attribute[] attributes) { + return getIMRequests().cancelLatestCommittedText(attributes); + } + + public AttributedCharacterIterator getCommittedText(int beginIndex, + int endIndex, + Attribute[] attributes) { + return getIMRequests().getCommittedText(beginIndex, endIndex, + attributes); + } + + public int getCommittedTextLength() { + return getIMRequests().getCommittedTextLength(); + } + + public int getInsertPositionOffset() { + return getIMRequests().getInsertPositionOffset(); + } + + public TextHitInfo getLocationOffset(int x, int y) { + InputMethodRequests imr = getStyleIMRequests(); + if (imr != null) { + return imr.getLocationOffset(x, y); + } + return null; + } + + public AttributedCharacterIterator getSelectedText(Attribute[] attributes) { + return getIMRequests().getSelectedText(attributes); + } + + public Rectangle getTextLocation(TextHitInfo offset) { + return getStyleIMRequests().getTextLocation(offset); + } + + /** + * To be called by AWT when client Window's bounds/visibility/state + * change + */ + public void notifyClientWindowChange(Rectangle bounds) { + if (notifyIM.contains(inputMethod)) { + inputMethod.notifyClientWindowChange(bounds); + } + pendingClientNotify = false; + } + + public final InputMethod getInputMethod() { + return inputMethod; + } + + public final Component getClient() { + return client; + } + + public final NativeIM getNativeIM() { + return nativeIM; + } +} diff --git a/awt/org/apache/harmony/awt/internal/nls/Messages.java b/awt/org/apache/harmony/awt/internal/nls/Messages.java new file mode 100644 index 000000000..c340358db --- /dev/null +++ b/awt/org/apache/harmony/awt/internal/nls/Messages.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* + * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL. + * All changes made to this file manually will be overwritten + * if this tool runs again. Better make changes in the template file. + */ + +package org.apache.harmony.awt.internal.nls; + + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +// BEGIN android-deleted +/* + * For Android, this module is a separate library and not part of the + * boot classpath, so its resources won't be found on the boot classpath + * as is assumed by MsgHelp.getString(). We instead use a local MsgHelp + * which bottoms out in a call to the useful part of its lower-level + * namesake. + */ +//import org.apache.harmony.kernel.vm.VM; +//import org.apache.harmony.luni.util.MsgHelp; +// END android-deleted + +/** + * This class retrieves strings from a resource bundle and returns them, + * formatting them with MessageFormat when required. + *

    + * It is used by the system classes to provide national language support, by + * looking up messages in the + * org.apache.harmony.awt.internal.nls.messages + * + * resource bundle. Note that if this file is not available, or an invalid key + * is looked up, or resource bundle support is not available, the key itself + * will be returned as the associated message. This means that the KEY + * should a reasonable human-readable (english) string. + * + */ +public class Messages { + + // BEGIN android-deleted + //private static final String sResource = + // "org.apache.harmony.awt.internal.nls.messages"; + // END android-deleted + + /** + * Retrieves a message which has no arguments. + * + * @param msg + * String the key to look up. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg) { + // BEGIN android-changed + return MsgHelp.getString(msg); + // END android-changed + } + + /** + * Retrieves a message which takes 1 argument. + * + * @param msg + * String the key to look up. + * @param arg + * Object the object to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object arg) { + return getString(msg, new Object[] { arg }); + } + + /** + * Retrieves a message which takes 1 integer argument. + * + * @param msg + * String the key to look up. + * @param arg + * int the integer to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, int arg) { + return getString(msg, new Object[] { Integer.toString(arg) }); + } + + /** + * Retrieves a message which takes 1 character argument. + * + * @param msg + * String the key to look up. + * @param arg + * char the character to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, char arg) { + return getString(msg, new Object[] { String.valueOf(arg) }); + } + + /** + * Retrieves a message which takes 2 arguments. + * + * @param msg + * String the key to look up. + * @param arg1 + * Object an object to insert in the formatted output. + * @param arg2 + * Object another object to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object arg1, Object arg2) { + return getString(msg, new Object[] { arg1, arg2 }); + } + + /** + * Retrieves a message which takes several arguments. + * + * @param msg + * String the key to look up. + * @param args + * Object[] the objects to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object[] args) { + // BEGIN android-changed + return MsgHelp.getString(msg, args); + // END android-changed + } + + // BEGIN android-note + // Duplicate code was dropped in favor of using MsgHelp. + // END android-note +} diff --git a/awt/org/apache/harmony/awt/internal/nls/MsgHelp.java b/awt/org/apache/harmony/awt/internal/nls/MsgHelp.java new file mode 100644 index 000000000..b57fe1166 --- /dev/null +++ b/awt/org/apache/harmony/awt/internal/nls/MsgHelp.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* + * This implementation is based on the class of the same name in + * org.apache.harmony.luni.util. + */ + +package org.apache.harmony.awt.internal.nls; + +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Logger; +import java.util.Locale; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; +import java.util.MissingResourceException; + +/** + * This class contains helper methods for loading resource bundles and + * formatting external message strings. + */ +public final class MsgHelp { + /** name of the resource for this class */ + private static final String RESOURCE_NAME = + "/org/apache/harmony/awt/internal/nls/messages.properties"; + + /** the resource bundle for this class */ + private static final ResourceBundle THE_BUNDLE; + + static { + ResourceBundle rb = null; + + try { + InputStream in = MsgHelp.class.getResourceAsStream( + RESOURCE_NAME); + rb = new PropertyResourceBundle(in); + } catch (IOException ex) { + Logger.global.warning("Couldn't read resource bundle: " + + ex); + } catch (RuntimeException ex) { + // Shouldn't happen, but deal at least somewhat gracefully. + Logger.global.warning("Couldn't find resource bundle: " + + ex); + } + + THE_BUNDLE = rb; + } + + public static String getString(String msg) { + if (THE_BUNDLE == null) { + return msg; + } + try { + return THE_BUNDLE.getString(msg); + } catch (MissingResourceException e) { + return "Missing message: " + msg; + } + } + + static public String getString(String msg, Object[] args) { + String format = msg; + if (THE_BUNDLE != null) { + try { + format = THE_BUNDLE.getString(msg); + } catch (MissingResourceException e) { + } + } + + return org.apache.harmony.luni.util.MsgHelp.format(format, args); + } +} diff --git a/awt/org/apache/harmony/awt/state/MenuItemState.java b/awt/org/apache/harmony/awt/state/MenuItemState.java new file mode 100644 index 000000000..b13e50bf0 --- /dev/null +++ b/awt/org/apache/harmony/awt/state/MenuItemState.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt.state; + +import java.awt.Dimension; +import java.awt.Rectangle; + +/** + * State of menu item + */ + +public interface MenuItemState { + + String getText(); + Rectangle getTextBounds(); + void setTextBounds(int x, int y, int w, int h); + + String getShortcut(); + Rectangle getShortcutBounds(); + void setShortcutBounds(int x, int y, int w, int h); + + Rectangle getItemBounds(); + void setItemBounds(int x, int y, int w, int h); + + boolean isMenu(); + boolean isChecked(); + boolean isEnabled(); + + boolean isCheckBox(); + boolean isSeparator(); + + Dimension getMenuSize(); +} diff --git a/awt/org/apache/harmony/awt/state/MenuState.java b/awt/org/apache/harmony/awt/state/MenuState.java new file mode 100644 index 000000000..564a49a08 --- /dev/null +++ b/awt/org/apache/harmony/awt/state/MenuState.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt.state; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Point; + +/** + * State of pop-up or drop-down menu + */ + +public interface MenuState { + int getWidth(); + int getHeight(); + Point getLocation(); + + void setSize(int w, int h); + + Font getFont(); + boolean isFontSet(); + FontMetrics getFontMetrics(Font f); + + int getItemCount(); + int getSelectedItemIndex(); + + MenuItemState getItem(int index); +} diff --git a/awt/org/apache/harmony/awt/state/State.java b/awt/org/apache/harmony/awt/state/State.java new file mode 100644 index 000000000..4b8706d13 --- /dev/null +++ b/awt/org/apache/harmony/awt/state/State.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt.state; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Rectangle; + +/** + * State of the component + */ +public interface State { + + boolean isEnabled(); + boolean isVisible(); + boolean isFocused(); + + Font getFont(); + boolean isFontSet(); + FontMetrics getFontMetrics(); + + Color getBackground(); + boolean isBackgroundSet(); + + Color getTextColor(); + boolean isTextColorSet(); + + Rectangle getBounds(); + Dimension getSize(); + + Dimension getDefaultMinimumSize(); + void setDefaultMinimumSize(Dimension size); + + long getWindowId(); +} diff --git a/awt/org/apache/harmony/awt/wtk/CreationParams.java b/awt/org/apache/harmony/awt/wtk/CreationParams.java new file mode 100644 index 000000000..63c581df2 --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/CreationParams.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +/** + * This class describes cross-platform NativeWindow creation params + * See also WindowFactory.createWindow + */ +public class CreationParams { + /** + * Initial state is maximized verticaly + */ + public final long MAXIMIZED_VERT = 1; + /** + * Initial state is maximized horizontaly + */ + public final long MAXIMIZED_HORIZ = 2; + /** + * Initial state is maximized both + * horizontaly and verticaly + */ + public final long MAXIMIZED = 3; + + /** + * The top-level window that has all possible decorations, + * has no owner and is displayed in taskbar + */ + public final static int DECOR_TYPE_FRAME = 1; + /** + * The dialog window + */ + public final static int DECOR_TYPE_DIALOG = 2; + /** + * The transient undecorated pop-up window + */ + public final static int DECOR_TYPE_POPUP = 3; + /** + * The undecoraded pop-up window + */ + public final static int DECOR_TYPE_UNDECOR = 4; + /** + * Non-MDI child window + */ + public final static int DECOR_TYPE_NONE = 0; + + /** + * Initial x. + */ + public int x = 0; + /** + * Initial y. + */ + public int y = 0; + /** + * Initial width. + */ + public int w = 1; + /** + * Initial height. + */ + public int h = 1; + /** + * The decoration type of the top-level window. The possible values are: + * DECOR_TYPE_FRAME, DECOR_TYPE_DIALOG, DECOR_TYPE_POPUP and DECOR_TYPE_UNDECOR + */ + public int decorType = DECOR_TYPE_NONE; + /** + * Window is child of parent, otherwise it's + * toplevel(child of desktop) window owned by parent. + */ + public boolean child = false; + /** + * Window is resizable + */ + public boolean resizable = true; + /** + * The window has no decorations + */ + public boolean undecorated = false; + /** + * Initial visibility state. + */ + public boolean visible = false; + /** + * Window is ALWAYS topmost in Z order. + */ + public boolean topmost = false; + /** + * Window is disabled. + */ + public boolean disabled = false; + /** + * Window initially iconified. + */ + public boolean iconified = false; + /** + * Bitwise OR of MAXIMIZED_* constants. + * Means if window is initially maximized. + */ + public int maximizedState = 0; + /** + * Tells that window position should be determined by native windowing system + */ + public boolean locationByPlatform = false; + /** + * Id of parent or owner window, see child field + * For non-child window without owner equals 0. + */ + public long parentId = 0; + /** + * Name wich is displayed on titlebar, taskbar and visible + * for system requests. + */ + public String name = null; +} \ No newline at end of file diff --git a/awt/org/apache/harmony/awt/wtk/CursorFactory.java b/awt/org/apache/harmony/awt/wtk/CursorFactory.java new file mode 100644 index 000000000..35e7d33bb --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/CursorFactory.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.Dimension; +import java.awt.Image; + +/** + * Provides factory for NativeCursor + */ +public abstract class CursorFactory { + protected NativeCursor[] systemCursors = { + null, null, null, null, + null, null, null, null, + null, null, null, null, + null, null, + }; + /** + * Creates and returns NativeCursor for predefined + * Java Cursor + * + * @param type - type of predefined Java Cursor + * @return created cursor + */ + public abstract NativeCursor createCursor(int type); + + /** + * Gets a cached instance of system(predefined) native cursor + * or creates a new one. This is a platform-independent method. + * + * @param type - type of predefined Java Cursor + * @return created cursor + */ + public NativeCursor getCursor(int type) { + if (type >= 0 && type < systemCursors.length) { + NativeCursor cursor = systemCursors[type]; + if (cursor == null) { + cursor = createCursor(type); + systemCursors[type] = cursor; + } + return cursor; + } + return null; + } + /** + * Creates and returns custom NativeCursor from image + * + * @param img - image(source) to create cursor from + * @param xHotSpot - x coordinate of the hotspot relative to the source's origin + * @param yHotSpot - y coordinate of the hotspot relative to the source's origin + * @return created cursor + */ + public abstract NativeCursor createCustomCursor(Image img, int xHotSpot, int yHotSpot); + + /** + * Query native system for the best cursor size closest to specified dimensions + * @param prefWidth - preferred width + * @param prefHeight - preferred height + * @return closest supported dimensions to ones specified + */ + public abstract Dimension getBestCursorSize(int prefWidth, int prefHeight); + + /** + * @return maximum number of colors supported by custom cursors + */ + public abstract int getMaximumCursorColors(); +} diff --git a/awt/org/apache/harmony/awt/wtk/GraphicsFactory.java b/awt/org/apache/harmony/awt/wtk/GraphicsFactory.java new file mode 100644 index 000000000..0d7c84f7b --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/GraphicsFactory.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov, Alexey A. Petrenko, Oleg V. Khaschansky + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.peer.FontPeer; +import org.apache.harmony.awt.gl.MultiRectArea; +import org.apache.harmony.awt.gl.font.FontManager; + +import android.graphics.Canvas; +import android.graphics.Paint; + + +/** + * GraphicsFactory interface defines methods for Graphics2D + * and font stuff instances factories. + */ +public interface GraphicsFactory { + static final FontMetrics cacheFM[] = new FontMetrics[10]; + + /** + * This method creates Graphics2D instance for specified native window. + * + * @param win Native window to draw + * @param translateX Translation along X axis + * @param translateY Translation along Y axis + * @param clip Clipping area for a new Graphics2D instance + * @return New Graphics2D instance for specified native window + * @deprecated + */ + @Deprecated + Graphics2D getGraphics2D(NativeWindow win, int translateX, int translateY, MultiRectArea clip); + + /** + * This method creates Graphics2D instance for specified native window. + * + * @param win Native window to draw + * @param translateX Translation along X axis + * @param translateY Translation along Y axis + * @param width Width of drawing area + * @param height Height of drawing area + * @return New Graphics2D instance for specified native window + */ + Graphics2D getGraphics2D(NativeWindow win, int translateX, int translateY, int width, int height); + // ???AWT: not standard harmony + Graphics2D getGraphics2D(Canvas c, Paint p); + + /** + * Creates instance of GraphicsEnvironment for specified WindowFactory + * + * @param wf WindowFactory + * @return New instance of GraphicsEnvironment + */ + GraphicsEnvironment createGraphicsEnvironment(WindowFactory wf); + + // Font methods + FontMetrics getFontMetrics(Font font); + FontManager getFontManager(); + FontPeer getFontPeer(Font font); + Font embedFont(String fontFilePath); +} diff --git a/awt/org/apache/harmony/awt/wtk/KeyInfo.java b/awt/org/apache/harmony/awt/wtk/KeyInfo.java new file mode 100644 index 000000000..1f8a29aa0 --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/KeyInfo.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.event.KeyEvent; + +/** + * Keystroke information + */ + +public final class KeyInfo { + + public int vKey; + public int keyLocation; + public final StringBuffer keyChars; + + public static final int DEFAULT_VKEY = KeyEvent.VK_UNDEFINED; + public static final int DEFAULT_LOCATION = KeyEvent.KEY_LOCATION_STANDARD; + + public KeyInfo() { + vKey = DEFAULT_VKEY; + keyLocation = DEFAULT_LOCATION; + keyChars = new StringBuffer(); + } + + public void setKeyChars(char ch) { + keyChars.setLength(0); + keyChars.append(ch); + } + + public void setKeyChars(StringBuffer sb) { + keyChars.setLength(0); + keyChars.append(sb); + } +} diff --git a/awt/org/apache/harmony/awt/wtk/NativeCursor.java b/awt/org/apache/harmony/awt/wtk/NativeCursor.java new file mode 100644 index 000000000..2c6eb1ef6 --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/NativeCursor.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +/** + * The interface provides access to platform dependent functionality + * for the class java.awt.Cursor. + */ +public interface NativeCursor { + /** + * Sets the current cursor shape + * to this cursor when a pointer is inside + * @param winID - window(currently used only on X11) + */ + void setCursor(long winID); + /** + * Destroys the native resource associated with + * this cursor + */ + void destroyCursor(); + + /** + * @return Native handle associated with this cursor + */ + long getId(); + +} diff --git a/awt/org/apache/harmony/awt/wtk/NativeEvent.java b/awt/org/apache/harmony/awt/wtk/NativeEvent.java new file mode 100644 index 000000000..1471c1a72 --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/NativeEvent.java @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Mikhail Danilov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.Point; +import java.awt.event.KeyEvent; + +import org.apache.harmony.awt.gl.MultiRectArea; + + +/** + * The interface describing cross-platform translation of system + * messages. + * + *

    Some messages can appear only on specific platform, + * but they still can have cross-platform interpretation if the + * application should be aware of them and can react using + * cross-platform API. + * + */ +public abstract class NativeEvent { + + /** + * Message has no common cross-platform + * interpretation and should be skipped. + */ + public static final int ID_PLATFORM = 0; + + /** + * Window bounds have changed. + */ + public static final int ID_BOUNDS_CHANGED = -1; + + /** + * Window decoration size has changed. + */ + public static final int ID_INSETS_CHANGED = -2; + + /** + * Window was just created (WM_CREATE on Windows) + */ + public static final int ID_CREATED = -3; + + /** + * Mouse grab was canceled by the native system + */ + public static final int ID_MOUSE_GRAB_CANCELED = -4; + + /** + * System color scheme or visual theme was changed + */ + public static final int ID_THEME_CHANGED = -5; + + protected long windowId; + protected int eventId; + protected long otherWindowId; + + protected Point screenPos; + protected Point localPos; + protected Rectangle windowRect; + + protected int modifiers; + protected int mouseButton; + protected int wheelRotation; + + protected KeyInfo keyInfo = new KeyInfo(); + + protected int windowState = -1; + protected long time; + + /** + * Returns the system window id of the event recipient. + * @return HWND on Windows, xwindnow on X + */ + public long getWindowId() { + return windowId; + } + + /** + * Returns cross-platform event id + * should be one of ID_* constants or + * id constants from java.awt.AWTEvent subclasess + * @return cross-platform event id + */ + public int getEventId() { + return eventId; + } + + /** + * Returns the position of cursor when event occured relative to + * top-left corner of recipient window + * @return position of cursor in local coordinates + */ + public Point getLocalPos() { + return localPos; + } + + /** + * Returns the position of cursor when event occured + * in screen coordinates. + * @return position of cursor in screen coordinates + */ + public Point getScreenPos() { + return screenPos; + } + + /** + * The recipient window bounds when the event occured + * @return window bounds + */ + public Rectangle getWindowRect() { + return windowRect; + } + + /** + * Returns the state of keyboard and mouse buttons when the event + * occured if event from mouse or keyboard, for other events can + * return junk values. The value is bitwise OR of + * java.awt.event.InputEvent *_DOWN constants. + * + * Method is aware of system mouse button swap for left-hand + * mouse and return swapped values. + * @return bitwise OR of java.awt.event.InputEvent *_DOWN constants + */ + public int getInputModifiers() { + return modifiers; + } + + /** + * Returns the iconified/maximized state of recipient window if + * event is state related, for other events can junk values. + * The value has the same meaning as Frame.getExtendedState + * It's bitwise OR of ICONIFIED, MAXIMIZED_HORIZ, MAXIMIZED_VERT + * @return bitwise OR of ICONIFIED, MAXIMIZED_HORIZ, MAXIMIZED_VERT + */ + public int getWindowState() { + return windowState; + } + + /** + * The same meaning as java.awt.event.getKeyCode + * @return java.awt.event VK_* constant + */ + public int getVKey() { + return (keyInfo != null) ? keyInfo.vKey : KeyInfo.DEFAULT_VKEY; + } + + /** + * The same meaning as java.awt.event.getKeyLocation + * @return java.awt.event KEY_LOCATION_* constant + */ + public int getKeyLocation() { + return (keyInfo != null) ? keyInfo.keyLocation : KeyInfo.DEFAULT_LOCATION; + } + + /** + * Return the string of characters associated with the event + * Has meaning only for KEY_PRESSED as should be translated to + * serie of KEY_TYPED events. For dead keys and input methods + * one key press can generate multiple key chars. + * @return string of characters + */ + public StringBuffer getKeyChars() { + if (keyInfo == null) { + return null; + } + if (keyInfo.vKey == KeyEvent.VK_ENTER) { + keyInfo.keyChars.setLength(0); + keyInfo.setKeyChars('\n'); + } + return keyInfo.keyChars; + } + + public char getLastChar() { + if (keyInfo == null || keyInfo.keyChars.length() == 0) { + return KeyEvent.CHAR_UNDEFINED; + } + return keyInfo.keyChars.charAt(keyInfo.keyChars.length()-1); + } + + /** + * Returns the number of mouse button which changed it's state, + * otherwise 0. + * Left button is 1, middle button is 2, right button is 3. + * + * Method is aware of system mouse button swap for left-hand + * mouse and return swapped values. + * @return mouse button number + */ + public int getMouseButton() { + return mouseButton; + } + + /** + * Returns time when the message was received + * @return time in milliseconds + */ + public long getTime() { + return time; + } + + /** + * For the focus event contains the oposite window. + * This means it lost focus if recipient gains it, + * or will gain focus if recipient looses it. + * @return HWND on Windows, xwindnow on X + */ + public long getOtherWindowId() { + return otherWindowId; + } + + /** + * Returns the "dirty" area of the window as set of non-intersecting + * rectangles. This area is to be painted. + * @return non-empty array of null if empty + */ + public abstract MultiRectArea getClipRects(); + + /** + * Returns the "dirty" area of the window as one rectangle. + * This area is to be painted. + * @return non-null Rectangle + */ + public abstract Rectangle getClipBounds(); + + /** + * Returns the window insets. Insets is area which belongs to + * window somehow but is outside of it's client area, + * it usually contains system provided border and titlebar. + * @return non-null java.awt.Insets + */ + public abstract Insets getInsets(); + + /** + * Returns true if event is popup menu trigger. + * @return boolean flag + */ + public abstract boolean getTrigger(); + + /** + * Returns the number of "clicks" the mouse wheel was rotated. + * @return negative values if the mouse wheel was rotated up/away from the user, + * and positive values if the mouse wheel was rotated down/ towards the user + */ + public int getWheelRotation() { + return wheelRotation; + } +} diff --git a/awt/org/apache/harmony/awt/wtk/NativeEventQueue.java b/awt/org/apache/harmony/awt/wtk/NativeEventQueue.java new file mode 100644 index 000000000..0738cd138 --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/NativeEventQueue.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Mikhail Danilov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.util.LinkedList; + + +/** + * Describes the cross-platform native event queue interface + * + *

    The implementation constructor should remember thread it was + * created. All other methods would be called obly from this thread, + * except awake(). + */ +public abstract class NativeEventQueue { + + private ShutdownWatchdog shutdownWatchdog; + private class EventMonitor {} + private final Object eventMonitor = new EventMonitor(); + private final LinkedList eventQueue = new LinkedList(); + + public static abstract class Task { + public volatile Object returnValue; + + public abstract void perform(); + } + + /** + * Blocks current thread until native event queue is not empty + * or awaken from other thread by awake(). + * + *

    Should be called only on tread which + * will process native events. + * + * @return if event loop should be stopped + */ + public abstract boolean waitEvent(); + + /** + * Determines whether or not the native event queue is empty. + * An queue is empty if it contains no messages waiting. + * + * @return true if the queue is empty; false otherwise + */ + public boolean isEmpty() { + synchronized(eventQueue) { + return eventQueue.isEmpty(); + } + } + + public NativeEvent getNextEvent() { + synchronized (eventQueue) { + if (eventQueue.isEmpty()) { + shutdownWatchdog.setNativeQueueEmpty(true); + return null; + } + return eventQueue.remove(0); + } + } + + protected void addEvent(NativeEvent event) { + synchronized (eventQueue) { + eventQueue.add(event); + shutdownWatchdog.setNativeQueueEmpty(false); + } + synchronized (eventMonitor) { + eventMonitor.notify(); + } + } + + public final Object getEventMonitor() { + return eventMonitor; + } + + public abstract void awake(); + + /** + * Gets AWT system window ID. + * + * @return AWT system window ID + */ + public abstract long getJavaWindow(); + + /** + * Add NativeEvent to the queue + */ + public abstract void dispatchEvent(); + + public abstract void performTask(Task task); + + public abstract void performLater(Task task); + + public final void setShutdownWatchdog(ShutdownWatchdog watchdog) { + synchronized (eventQueue) { + shutdownWatchdog = watchdog; + } + } + +} diff --git a/awt/org/apache/harmony/awt/wtk/NativeEventThread.java b/awt/org/apache/harmony/awt/wtk/NativeEventThread.java new file mode 100644 index 000000000..d50add4e3 --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/NativeEventThread.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + + +/** + * NativeEventThread + */ +public class NativeEventThread extends Thread { + + public interface Init { + WTK init(); + } + + NativeEventQueue nativeQueue; + Init init; + + private WTK wtk; + + public NativeEventThread() { + super("AWT-NativeEventThread"); //$NON-NLS-1$ + setDaemon(true); + } + + @Override + public void run() { + synchronized (this) { + try { + wtk = init.init(); + nativeQueue = wtk.getNativeEventQueue(); + } finally { + notifyAll(); + } + } + + runModalLoop(); + } + + void runModalLoop() { + while (nativeQueue.waitEvent()) { + nativeQueue.dispatchEvent(); + } + } + + public void start(Init init) { + synchronized (this) { + this.init = init; + super.start(); + try { + wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + public WTK getWTK() { + return wtk; + } +} diff --git a/awt/org/apache/harmony/awt/wtk/NativeIM.java b/awt/org/apache/harmony/awt/wtk/NativeIM.java new file mode 100644 index 000000000..1626f4a9a --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/NativeIM.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.im.spi.InputMethod; +import java.awt.im.spi.InputMethodContext; +import java.awt.im.spi.InputMethodDescriptor; +import java.lang.Character.Subset; +import java.util.Locale; + +/** + * A cross-platform interface for native input + * method sub-system functionality. + */ +public abstract class NativeIM implements InputMethod, InputMethodDescriptor { + protected InputMethodContext imc; + + public void activate() { + + } + + public void deactivate(boolean isTemporary) { + + } + + public void dispatchEvent(AWTEvent event) { + + } + + public void dispose() { + + } + + public void endComposition() { + + } + + public Object getControlObject() { + return null; + } + + public Locale getLocale() { + return null; + } + + public void hideWindows() { + + } + + public boolean isCompositionEnabled() { + return false; + } + + public void notifyClientWindowChange(Rectangle bounds) { + + } + + public void reconvert() { + + } + + public void removeNotify() { + + } + + public void setCharacterSubsets(Subset[] subsets) { + + } + + public void setCompositionEnabled(boolean enable) { + + } + + public void setInputMethodContext(InputMethodContext context) { + imc = context; + } + + public boolean setLocale(Locale locale) { + return false; + } + + public Locale[] getAvailableLocales() throws AWTException { + return new Locale[]{Locale.getDefault(), Locale.ENGLISH}; + //return new Locale[]{Locale.getDefault(), Locale.US}; + } + + public InputMethod createInputMethod() throws Exception { + return this; + } + + public String getInputMethodDisplayName(Locale inputLocale, + Locale displayLanguage) { + return "System input methods"; //$NON-NLS-1$ + } + + public Image getInputMethodIcon(Locale inputLocale) { + return null; + } + + public boolean hasDynamicLocaleList() { + return false; + } + + public abstract void disableIME(); + +// public abstract void disableIME(long id); + +} diff --git a/awt/org/apache/harmony/awt/wtk/NativeMouseInfo.java b/awt/org/apache/harmony/awt/wtk/NativeMouseInfo.java new file mode 100644 index 000000000..06969756b --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/NativeMouseInfo.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.Point; + +/** + * The interface provides access to platform dependent functionality + * for classes java.awt.PointerInfo & java.awt.MouseInfo. + */ +public interface NativeMouseInfo { + + /** + * Returns the Point that represents + * the coordinates of the pointer on the screen. + */ + Point getLocation(); + + /** + * Returns the number of buttons on the mouse. + * If no mouse is installed returns -1. + */ + int getNumberOfButtons(); +} diff --git a/awt/org/apache/harmony/awt/wtk/NativeRobot.java b/awt/org/apache/harmony/awt/wtk/NativeRobot.java new file mode 100644 index 000000000..0b354d05b --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/NativeRobot.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Dmitry A. Durnev + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.Color; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +/** + * A cross-platform interface for java.awt.Robot implementation + */ +public interface NativeRobot { + + /** + * @see java.awt.Robot#createScreenCapture(Rectangle) + * @param screenRect rectangle to capture in screen coordinates + * @return the captured image or null if + * capture failed. + */ + BufferedImage createScreenCapture(Rectangle screenRect); + + /** + * @see java.awt.Robot#getPixelColor(int, int) + */ + Color getPixel(int x, int y); + + /** + * Generate a native system keyboard input event. + * @param keycode A Java virtual key code + * @param press A key is pressed if true, released otherwise + * @see java.awt.Robot#keyPress(int) + * @throws IllegalArgumentException if keycode is invalid in the native system + */ + void keyEvent(int keycode, boolean press); + + /** + * Generate a native system mouse button(s) press or release event. + * @param buttons A mask of Java mouse button flags + * @param press buttons are pressed if true, released otherwise + * @see java.awt.Robot#mousePress(int) + */ + void mouseButton(int buttons, boolean press); + + /** + * Generate a native system mouse motion event. + * + * @see java.awt.Robot#mouseMove(int, int) + */ + void mouseMove(int x, int y); + + /** + * Generate a native system mouse wheel event. + * + * @see java.awt.Robot#mouseWheel(int) + */ + void mouseWheel(int wheelAmt); +} diff --git a/awt/org/apache/harmony/awt/wtk/NativeWindow.java b/awt/org/apache/harmony/awt/wtk/NativeWindow.java new file mode 100644 index 000000000..73fd6c00b --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/NativeWindow.java @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Mikhail Danilov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.Image; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; + +import org.apache.harmony.awt.gl.MultiRectArea; + + +/** + * Provides cross-platform way to manipulate native window. + * + * Results of methods are reported through native messages. + */ +public interface NativeWindow { + /** + * Returns system id of the associated window + * @return HWND on Windows, xwindow on X + */ + long getId(); + + /** + * Shows/hides window + * @param v - new visibility + */ + void setVisible(boolean v); + + /** + * Means only size should be changed + */ + static final int BOUNDS_NOMOVE = 1; + + /** + * Means only position should be changed + */ + static final int BOUNDS_NOSIZE = 2; + + /** + * Tries to set desired window bounds. It's not gurantied the + * property will have the desired value. The value change + * should be reported by system event (as for other properties). + * + *

    If child, position is relative to parent window. + * @param x - desired x + * @param y - desired y + * @param w - desired width + * @param h - desired height + * @param boundsMask - bitwise OR of BOUNDS_* constants. + * Governs the new bounds interpretation. + */ + void setBounds(int x, int y, int w, int h, int boundsMask); + + /** + * Returns last notified window bounds. This means the last bounds + * reported by system event. + * + *

    If child, position is relative to parent window. + * @return last notified window bounds + */ + Rectangle getBounds(); + + /** + * Returns last notified insets. This means the last insets + * reported by system event. Insets are margins around client area + * ocupied by system provided decor, ususally border and titlebar. + * @return last notified insets + */ + Insets getInsets(); + + /** + * Enables/disables processing of input (key, mouse) event + * by window. If disabled input events are ignored. + * @param value - if enabled + */ + void setEnabled(boolean value); + + /** + * Sets the "focusable" window state. + * @param value - if true makes window focusable + */ + void setFocusable(boolean value); + + /** + * + * @return current focusable window state + */ + boolean isFocusable(); + + /** + * Tries to set application input focus to the window or clear + * current focus from focused window. + * + *

    For toplevel windows it's not gurantied focus will land in + * desired window even if function returns true. Focus traversal should be tracked + * by processing system events. + * + * @param focus - if true sets focus, else clears focus + * @return if success + */ + boolean setFocus(boolean focus); + + /** + * Destroys the asscoiated window. + * Attempts to use it thereafter can result in + * unpredictable bechavior. + */ + void dispose(); + + /** + * Changes window Z-order to place this window under, If w is null + * places places this window on the top. Z-order is per parent. + * Toplevels a children of desktop in terms of Z-order. + * @param w - window to place under. + */ + void placeAfter(NativeWindow w); + + /** + * Places window on top of Z-order + */ + void toFront(); + + /** + * Places window on bottom of Z-order + */ + void toBack(); + + /** + * Makes the window resizable/not resizable by user + * @param value - if resizable + */ + void setResizable(boolean value); + + /** + * Sets the window caption + * @param title - caption text + */ + void setTitle(String title); + + /** + * Activate the mouse event capturing + */ + void grabMouse(); + + /** + * Deactivate mouse event capturing + */ + void ungrabMouse(); + + /** + * Set extended state for top-level window. + * + * @param state - new state, bitmask of ICONIFIED, MAXIMIZED_BOTH, etc. + */ + void setState(int state); + + /** + * Set the image to be displayed in the minimized icon for + * top-level [decorated] window. + * @param image the icon image to be displayed + */ + void setIconImage(Image image); + + /** + * Makes window top-most if value is true, + * non-topmost(normal) otherwise. + */ + void setAlwaysOnTop(boolean value); + + /** + * Set desired [top-level] window bounds when being in maximized state. + * Fields set to Integer.MAX_VALUE are ignored[system-supplied values are + * used instead] + */ + void setMaximizedBounds(Rectangle bounds); + + /** + * Get absolute position on the screen + */ + Point getScreenPos(); + + /** + * Set a window "packed" flag: + * the flag indicates that if insets change + * client area shouldn't be resized, but frame + * must be resized instead + */ + void setPacked(boolean packed); + + /** + * Make window an "input method window" by setting + * special window style, e. g. small title bar, no + * close, minimize/maximize buttons. For internal + * use by input method framework. + * + */ + void setIMStyle(); + + MultiRectArea getObscuredRegion(Rectangle part); +} diff --git a/awt/org/apache/harmony/awt/wtk/ShutdownThread.java b/awt/org/apache/harmony/awt/wtk/ShutdownThread.java new file mode 100644 index 000000000..701eb46ca --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/ShutdownThread.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Michael Danilov, Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import org.apache.harmony.awt.internal.nls.Messages; + +public final class ShutdownThread extends Thread { + + public static final class Watchdog { + } + + public ShutdownThread() { + setName("AWT-Shutdown"); //$NON-NLS-1$ + setDaemon(false); + } + + private boolean shouldStop = false; + + @Override + public void run() { + synchronized (this) { + notifyAll(); // synchronize the startup + + while (true) { + try { + wait(); + } catch (InterruptedException e) { + } + + if (shouldStop) { + notifyAll(); // synchronize the shutdown + return; + } + } + } + } + + @Override + public void start() { + synchronized (this) { + super.start(); + try { + wait(); + } catch (InterruptedException e) { + // awt.26=Shutdown thread was interrupted while starting + throw new RuntimeException( + Messages.getString("awt.26")); //$NON-NLS-1$ + } + } + } + + public void shutdown() { + synchronized (this) { + shouldStop = true; + notifyAll(); + try { + wait(); + } catch (InterruptedException e) { + // awt.27=Shutdown thread was interrupted while stopping + throw new RuntimeException( + Messages.getString("awt.27")); //$NON-NLS-1$ + } + } + } +} diff --git a/awt/org/apache/harmony/awt/wtk/ShutdownWatchdog.java b/awt/org/apache/harmony/awt/wtk/ShutdownWatchdog.java new file mode 100644 index 000000000..6efa519fc --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/ShutdownWatchdog.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +/** + * Shutdown Watchdog + */ +public final class ShutdownWatchdog { + + private boolean nativeQueueEmpty = true; + private boolean awtQueueEmpty = true; + private boolean windowListEmpty = true; + + private boolean forcedShutdown = false; + + private ShutdownThread thread; + + public synchronized void setNativeQueueEmpty(boolean empty) { + nativeQueueEmpty = empty; + checkShutdown(); + } + + public synchronized void setAwtQueueEmpty(boolean empty) { + awtQueueEmpty = empty; + checkShutdown(); + } + + public synchronized void setWindowListEmpty(boolean empty) { + windowListEmpty = empty; + checkShutdown(); + } + + public synchronized void forceShutdown() { + forcedShutdown = true; + shutdown(); + } + + public synchronized void start() { + keepAlive(); + } + + private void checkShutdown() { + if (canShutdown()) { + shutdown(); + } else { + keepAlive(); + } + } + + private boolean canShutdown() { + return (nativeQueueEmpty && awtQueueEmpty && windowListEmpty) || + forcedShutdown; + } + + private void keepAlive() { + if (thread == null) { + thread = new ShutdownThread(); + thread.start(); + } + } + + private void shutdown() { + if (thread != null) { + thread.shutdown(); + thread = null; + } + } +} diff --git a/awt/org/apache/harmony/awt/wtk/Synchronizer.java b/awt/org/apache/harmony/awt/wtk/Synchronizer.java new file mode 100644 index 000000000..3eeaa0b07 --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/Synchronizer.java @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Mikhail Danilov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.util.Hashtable; +import java.util.LinkedList; + +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * Class synchronizer is to protect AWT state integrity in multithreading environment. + * It is supposed to have a child class per native platform. + * The only instance is created on the first use of one of the core AWT classes. + * Registers WTK on the dispatch thread startup. + * It is just a special kind of mutex. + * + */ + +public class Synchronizer { + //TODO: think about java.util.concurrent use for faster blocking/awaking operations + //TODO: think about all synchronized methods. Is there need to synchronize everything? + + /** + * This field holds the counter of lock operation. + * To free synchronizer unlock method must be called $acquestCounter times. + * Equals to 0 when synchronizer is free. + */ + protected int acquestCounter; + + /** + * This field holds the owner of synchronizer. + * Owner of synchronizer is a last thread that successfully locked synchronizer and + * still havn't freed it. Equals to null when synchronizer is free. + */ + protected Thread owner; + + /** + * This field holds the wait queue. + * Wait queue is a queue where thread wait for synchronizer access. + * Empty when synchronizer is free. + */ + protected final LinkedList waitQueue = new LinkedList(); + + /** + * The event dispatch thread + */ + protected Thread dispatchThread; + + private final Hashtable storedStates = new Hashtable(); + + /** + * Acquire the lock for this synchronizer. Nested lock is supported. + * If the mutex is already locked by another thread, the current thread will be put + * into wait queue until the lock becomes available. + * All user threads are served in FIFO order. Dispatch thread has higher priority. + * Supposed to be used in Toolkit.lockAWT() only. + */ + public void lock() { + synchronized (this) { + Thread curThread = Thread.currentThread(); + + if (acquestCounter == 0) { + acquestCounter = 1; + owner = curThread; + } else { + if (owner == curThread) { + acquestCounter++; + } else { + if (curThread == dispatchThread) { + waitQueue.addFirst(curThread); + } else { + waitQueue.addLast(curThread); + } + try { + wait(); + } catch (InterruptedException e) { + if (owner != curThread) { + waitQueue.remove(curThread); + // awt.1F=Waiting for resource access thread interrupted not from unlock method. + throw new RuntimeException(Messages + .getString("awt.1F")); //$NON-NLS-1$ + } + } + } + } + } + } + + /** + * Release the lock for this synchronizer. + * If wait queue is not empty the first waiting thread acquires the lock. + * Supposed to be used in Toolkit.unlockAWT() only. + */ + public void unlock() { + synchronized (this) { + if (acquestCounter == 0) { + // awt.20=Can't unlock not locked resource. + throw new RuntimeException(Messages.getString("awt.20")); //$NON-NLS-1$ + } + if (owner != Thread.currentThread()) { + // awt.21=Not owner can't unlock resource. + throw new RuntimeException(Messages.getString("awt.21")); //$NON-NLS-1$ + } + + acquestCounter--; + if (acquestCounter == 0) { + if (waitQueue.size() > 0) { + acquestCounter = 1; + owner = waitQueue.removeFirst(); + owner.interrupt(); + } else { + owner = null; + } + } + } + } + + /** + * Stores state of this synchronizer and frees it. + * Supposed to be used in Toolkit.unsafeInvokeAndWaitUnderAWTLock() only in pair with + * lockAndRestoreState(). + * Do not call it directly. + */ + public void storeStateAndFree() { + synchronized (this) { + Thread curThread = Thread.currentThread(); + + if (owner != curThread) { + // awt.22=Not owner can't free resource. + throw new RuntimeException(Messages.getString("awt.22")); //$NON-NLS-1$ + } + if (storedStates.containsKey(curThread)) { + // awt.23=One thread can't store state several times in a row. + throw new RuntimeException(Messages.getString("awt.23")); //$NON-NLS-1$ + } + + storedStates.put(curThread, new Integer(acquestCounter)); + acquestCounter = 1; + unlock(); + } + } + + /** + * Locks this synchronizer and restores it's state. + * Supposed to be used in Toolkit.unsafeInvokeAndWaitUnderAWTLock() only in pair with + * storeStateAndFree(). + * Do not call it directly. + */ + public void lockAndRestoreState() { + synchronized (this) { + Thread curThread = Thread.currentThread(); + + if (owner == curThread) { + // awt.24=Owner can't overwrite resource state. Lock operations may be lost. + throw new RuntimeException( + Messages.getString("awt.24")); //$NON-NLS-1$ + } + if (!storedStates.containsKey(curThread)) { + // awt.25=No state stored for current thread. + throw new RuntimeException(Messages.getString("awt.25")); //$NON-NLS-1$ + } + + lock(); + acquestCounter = storedStates.get(curThread).intValue(); + storedStates.remove(curThread); + } + } + + /** + * Sets references to WTK and event dispatch thread. + * Called on toolkit startup. + * + * @param wtk - reference to WTK instance + * @param dispatchThread - reference to event dispatch thread + */ + public void setEnvironment(WTK wtk, Thread dispatchThread) { + synchronized (this) { + this.dispatchThread = dispatchThread; + } + } + +} diff --git a/awt/org/apache/harmony/awt/wtk/SystemProperties.java b/awt/org/apache/harmony/awt/wtk/SystemProperties.java new file mode 100644 index 000000000..6b59f0e70 --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/SystemProperties.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.Font; +import java.awt.font.TextAttribute; +import java.awt.im.InputMethodHighlight; +import java.util.Map; + +/** + * NativeProperties + */ + +public interface SystemProperties { + + /** + * Get current value of a system color + * @param index - one of java.awt.SystemColor constants + * @return ARGB value of requested system color + */ + int getSystemColorARGB(int index); + + /** + * Get default font for GUI elements such as menus and buttons + * @return the font object + */ + Font getDefaultFont(); + + /** + * Fill the given Map with system properties + */ + void init(Map desktopProperties); + + /** + * Fills the given map with system-dependent visual text + * attributes for the abstract description + * of the given input method highlight + * @see java.awt.Toolkit.mapInputMethodHighlight() + */ + void mapInputMethodHighlight(InputMethodHighlight highlight, Map map); +} diff --git a/awt/org/apache/harmony/awt/wtk/WTK.java b/awt/org/apache/harmony/awt/wtk/WTK.java new file mode 100644 index 000000000..4162fbd52 --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/WTK.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Pavel Dolgov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.GraphicsDevice; + + +public abstract class WTK { + + public abstract GraphicsFactory getGraphicsFactory(); + public abstract NativeEventQueue getNativeEventQueue(); + public abstract WindowFactory getWindowFactory(); + + /** + * Returns platform specific implementation of the interface + * org.apache.harmony.awt.wtk.CursorFactory. + * @return implementation of CursorFactory + */ + public abstract CursorFactory getCursorFactory(); + + /** + * Returns platform specific implementation of the interface + * org.apache.harmony.awt.wtk.NativeMouseInfo. + * @return implementation of NativeMouseInfo + */ + public abstract NativeMouseInfo getNativeMouseInfo(); + + public abstract SystemProperties getSystemProperties(); + + /** + * Returns platform specific implementation of the interface + * org.apache.harmony.awt.wtk.NativeRobot. + * @return implementation of NativeRobot + */ + public abstract NativeRobot getNativeRobot(GraphicsDevice screen); + + /** + * Returns platform specific implementation of the abstract + * class org.apache.harmony.awt.wtk.NativeIM. + * @return implementation of NativeIM + */ + public abstract NativeIM getNativeIM(); +} diff --git a/awt/org/apache/harmony/awt/wtk/WindowFactory.java b/awt/org/apache/harmony/awt/wtk/WindowFactory.java new file mode 100644 index 000000000..23604dad4 --- /dev/null +++ b/awt/org/apache/harmony/awt/wtk/WindowFactory.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Mikhail Danilov + * @version $Revision$ + */ +package org.apache.harmony.awt.wtk; + +import java.awt.Dimension; +import java.awt.Point; + +/** + * Provides factory for NativeWindow + */ +public interface WindowFactory { + /** + * Creates and returns NativeWindow with desired + * creation params + * + * @param p - initial window properties + * @return created window + */ + NativeWindow createWindow(CreationParams p); + /** + * Create NativeWindow instance connected to existing native resource + * @param nativeWindowId - id of existing window + * @return created NativeWindow instance + */ + NativeWindow attachWindow(long nativeWindowId); + /** + * Returns NativeWindow instance if created by this instance of + * WindowFactory, otherwise null + * + * @param id - HWND on Windows xwindow on X + * @return NativeWindow or null if unknown + */ + NativeWindow getWindowById(long id); + /** + * Returns NativeWindow instance of the top-level window + * that contains a specified point and was + * created by this instance of WindowFactory + * @param p - Point to check + * @return NativeWindow or null if the point is + * not within a window created by this WindowFactory + */ + NativeWindow getWindowFromPoint(Point p); + + /** + * Returns whether native system supports the state for windows. + * This method tells whether the UI concept of, say, maximization or iconification is supported. + * It will always return false for "compound" states like Frame.ICONIFIED|Frame.MAXIMIZED_VERT. + * In other words, the rule of thumb is that only queries with a single frame state + * constant as an argument are meaningful. + * + * @param state - one of named frame state constants. + * @return true is this frame state is supported by this Toolkit implementation, false otherwise. + */ + boolean isWindowStateSupported(int state); + + /** + * @see org.apache.harmony.awt.ComponentInternals + */ + void setCaretPosition(int x, int y); + + /** + * Request size of arbitrary native window + * @param id - window ID + * @return window size + */ + Dimension getWindowSizeById(long id); +} \ No newline at end of file diff --git a/awt/org/apache/harmony/beans/internal/nls/Messages.java b/awt/org/apache/harmony/beans/internal/nls/Messages.java new file mode 100644 index 000000000..51e8168fa --- /dev/null +++ b/awt/org/apache/harmony/beans/internal/nls/Messages.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* + * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL. + * All changes made to this file manually will be overwritten + * if this tool runs again. Better make changes in the template file. + */ + +package org.apache.harmony.beans.internal.nls; + + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +// BEGIN android-deleted +/* + * For Android, this module is a separate library and not part of the + * boot classpath, so its resources won't be found on the boot classpath + * as is assumed by MsgHelp.getString(). We instead use a local MsgHelp + * which bottoms out in a call to the useful part of its lower-level + * namesake. + */ +//import org.apache.harmony.kernel.vm.VM; +//import org.apache.harmony.luni.util.MsgHelp; +// END android-deleted + +/** + * This class retrieves strings from a resource bundle and returns them, + * formatting them with MessageFormat when required. + *

    + * It is used by the system classes to provide national language support, by + * looking up messages in the + * org.apache.harmony.beans.internal.nls.messages + * + * resource bundle. Note that if this file is not available, or an invalid key + * is looked up, or resource bundle support is not available, the key itself + * will be returned as the associated message. This means that the KEY + * should a reasonable human-readable (english) string. + * + */ +public class Messages { + + // BEGIN android-deleted + // private static final String sResource = + // "org.apache.harmony.beans.internal.nls.messages"; //$NON-NLS-1$ + // END android-deleted + + /** + * Retrieves a message which has no arguments. + * + * @param msg + * String the key to look up. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg) { + // BEGIN android-changed + return MsgHelp.getString(msg); + // END android-changed + } + + /** + * Retrieves a message which takes 1 argument. + * + * @param msg + * String the key to look up. + * @param arg + * Object the object to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object arg) { + return getString(msg, new Object[] { arg }); + } + + /** + * Retrieves a message which takes 1 integer argument. + * + * @param msg + * String the key to look up. + * @param arg + * int the integer to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, int arg) { + return getString(msg, new Object[] { Integer.toString(arg) }); + } + + /** + * Retrieves a message which takes 1 character argument. + * + * @param msg + * String the key to look up. + * @param arg + * char the character to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, char arg) { + return getString(msg, new Object[] { String.valueOf(arg) }); + } + + /** + * Retrieves a message which takes 2 arguments. + * + * @param msg + * String the key to look up. + * @param arg1 + * Object an object to insert in the formatted output. + * @param arg2 + * Object another object to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object arg1, Object arg2) { + return getString(msg, new Object[] { arg1, arg2 }); + } + + /** + * Retrieves a message which takes several arguments. + * + * @param msg + * String the key to look up. + * @param args + * Object[] the objects to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object[] args) { + // BEGIN android-changed + return MsgHelp.getString(msg, args); + // END android-changed + } + + // BEGIN android-note + // Duplicate code was dropped in favor of using MsgHelp. + // END android-note +} diff --git a/awt/org/apache/harmony/beans/internal/nls/MsgHelp.java b/awt/org/apache/harmony/beans/internal/nls/MsgHelp.java new file mode 100644 index 000000000..68faabfa3 --- /dev/null +++ b/awt/org/apache/harmony/beans/internal/nls/MsgHelp.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* + * This implementation is based on the class of the same name in + * org.apache.harmony.luni.util. + */ + +package org.apache.harmony.beans.internal.nls; + +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Logger; +import java.util.Locale; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; +import java.util.MissingResourceException; + +/** + * This class contains helper methods for loading resource bundles and + * formatting external message strings. + */ +public final class MsgHelp { + /** name of the resource for this class */ + private static final String RESOURCE_NAME = + "/org/apache/harmony/beans/internal/nls/messages.properties"; + + /** the resource bundle for this class */ + private static final ResourceBundle THE_BUNDLE; + + static { + ResourceBundle rb = null; + + try { + InputStream in = MsgHelp.class.getResourceAsStream( + RESOURCE_NAME); + rb = new PropertyResourceBundle(in); + } catch (IOException ex) { + Logger.global.warning("Couldn't read resource bundle: " + + ex); + } catch (RuntimeException ex) { + // Shouldn't happen, but deal at least somewhat gracefully. + Logger.global.warning("Couldn't find resource bundle: " + + ex); + } + + THE_BUNDLE = rb; + } + + public static String getString(String msg) { + if (THE_BUNDLE == null) { + return msg; + } + try { + return THE_BUNDLE.getString(msg); + } catch (MissingResourceException e) { + return "Missing message: " + msg; + } + } + + static public String getString(String msg, Object[] args) { + String format = msg; + if (THE_BUNDLE != null) { + try { + format = THE_BUNDLE.getString(msg); + } catch (MissingResourceException e) { + } + } + + return org.apache.harmony.luni.util.MsgHelp.format(format, args); + } +} diff --git a/awt/org/apache/harmony/x/imageio/internal/nls/Messages.java b/awt/org/apache/harmony/x/imageio/internal/nls/Messages.java new file mode 100644 index 000000000..498e1bbe6 --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/internal/nls/Messages.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* + * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL. + * All changes made to this file manually will be overwritten + * if this tool runs again. Better make changes in the template file. + */ + +package org.apache.harmony.x.imageio.internal.nls; + +import org.apache.harmony.luni.util.MsgHelp; + +/** + * This class retrieves strings from a resource bundle and returns them, + * formatting them with MessageFormat when required. + *

    + * It is used by the system classes to provide national language support, by + * looking up messages in the + * org.apache.harmony.x.imageio.internal.nls.messages + * + * resource bundle. Note that if this file is not available, or an invalid key + * is looked up, or resource bundle support is not available, the key itself + * will be returned as the associated message. This means that the KEY + * should a reasonable human-readable (english) string. + * + */ +public class Messages { + + private static final String sResource = + "org.apache.harmony.x.imageio.internal.nls.messages"; //$NON-NLS-1$ + + /** + * Retrieves a message which has no arguments. + * + * @param msg + * String the key to look up. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg) { + return MsgHelp.getString(sResource, msg); + } + + /** + * Retrieves a message which takes 1 argument. + * + * @param msg + * String the key to look up. + * @param arg + * Object the object to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object arg) { + return getString(msg, new Object[] { arg }); + } + + /** + * Retrieves a message which takes 1 integer argument. + * + * @param msg + * String the key to look up. + * @param arg + * int the integer to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, int arg) { + return getString(msg, new Object[] { Integer.toString(arg) }); + } + + /** + * Retrieves a message which takes 1 character argument. + * + * @param msg + * String the key to look up. + * @param arg + * char the character to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, char arg) { + return getString(msg, new Object[] { String.valueOf(arg) }); + } + + /** + * Retrieves a message which takes 2 arguments. + * + * @param msg + * String the key to look up. + * @param arg1 + * Object an object to insert in the formatted output. + * @param arg2 + * Object another object to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object arg1, Object arg2) { + return getString(msg, new Object[] { arg1, arg2 }); + } + + /** + * Retrieves a message which takes several arguments. + * + * @param msg + * String the key to look up. + * @param args + * Object[] the objects to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object[] args) { + return MsgHelp.getString(sResource, msg, args); + } +} diff --git a/awt/org/apache/harmony/x/imageio/internal/nls/messages.properties b/awt/org/apache/harmony/x/imageio/internal/nls/messages.properties new file mode 100644 index 000000000..8a49dd8fd --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/internal/nls/messages.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +# messages for EN locale +imageio.1=Wrong bitDepth-numBands composition \ No newline at end of file diff --git a/awt/org/apache/harmony/x/imageio/metadata/IIOMetadataUtils.java b/awt/org/apache/harmony/x/imageio/metadata/IIOMetadataUtils.java new file mode 100644 index 000000000..caeefdd5c --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/metadata/IIOMetadataUtils.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.harmony.x.imageio.metadata; + +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import javax.imageio.metadata.IIOMetadataFormat; +import javax.imageio.metadata.IIOMetadataFormatImpl; + +public class IIOMetadataUtils { + private IIOMetadataUtils() {} + + public static IIOMetadataFormat instantiateMetadataFormat( + String formatName, boolean standardFormatSupported, + String nativeMetadataFormatName, String nativeMetadataFormatClassName, + String [] extraMetadataFormatNames, String [] extraMetadataFormatClassNames + ) { + if (formatName == null) { + throw new IllegalArgumentException("formatName == null!"); + } + if (formatName.equals(IIOMetadataFormatImpl.standardMetadataFormatName)) { + if (standardFormatSupported) { + return IIOMetadataFormatImpl.getStandardFormatInstance(); + } + } + + String className = null; + + if (formatName.equals(nativeMetadataFormatName)) { + className = nativeMetadataFormatClassName; + } else if (extraMetadataFormatNames != null) { + for (int i = 0; i < extraMetadataFormatNames.length; i++) { + if (formatName.equals(extraMetadataFormatNames[i])) { + className = extraMetadataFormatClassNames[i]; + break; + } + } + } + + if (className == null) { + throw new IllegalArgumentException("Unsupported format name"); + } + + // Get the context class loader and try to use it first + ClassLoader contextClassloader = AccessController.doPrivileged( + new PrivilegedAction() { + public ClassLoader run() { + return Thread.currentThread().getContextClassLoader(); + } + }); + + Class cls; + + try { + cls = Class.forName(className, true, contextClassloader); + } catch (ClassNotFoundException e) { + try { + // Use current class loader + cls = Class.forName(className); + } catch (ClassNotFoundException e1) { + throw new IllegalStateException ("Can't obtain format"); + } + } + + try { + //???AWT: + //Method getInstance = cls.getMethod("getInstance"); + //return (IIOMetadataFormat) getInstance.invoke(null); + return null; + } catch (Exception e) { + IllegalStateException e1 = new IllegalStateException("Can't obtain format"); + e1.initCause(e); // Add some details to the message + throw e1; + } + } +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/jpeg/IISDecodingImageSource.java b/awt/org/apache/harmony/x/imageio/plugins/jpeg/IISDecodingImageSource.java new file mode 100644 index 000000000..051f9065c --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/jpeg/IISDecodingImageSource.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem Rafikov + * @version $Revision: 1.2 $ + */ +package org.apache.harmony.x.imageio.plugins.jpeg; + +import javax.imageio.stream.ImageInputStream; + +import org.apache.harmony.awt.gl.image.DecodingImageSource; + +import java.io.InputStream; +import java.io.IOException; + +/** + * This allows usage of the java2d jpegdecoder with ImageInputStream in + * the JPEGImageReader. Temporary, only to make JPEGImageReader#read(..) + * working. + * + */ +public class IISDecodingImageSource extends DecodingImageSource { + + private final InputStream is; + + public IISDecodingImageSource(ImageInputStream iis) { + is = new IISToInputStreamWrapper(iis); + } + + @Override + protected boolean checkConnection() { + return true; + } + + @Override + protected InputStream getInputStream() { + return is; + } + + static class IISToInputStreamWrapper extends InputStream { + + private ImageInputStream input; + + public IISToInputStreamWrapper(ImageInputStream input) { + this.input=input; + } + + @Override + public int read() throws IOException { + return input.read(); + } + + @Override + public int read(byte[] b) throws IOException { + return input.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return input.read(b, off, len); + } + + @Override + public long skip(long n) throws IOException { + return input.skipBytes(n); + } + + @Override + public boolean markSupported() { + return true; // This is orig + + // ???AWT: FIXME + // This is an error in Harmony. Not all input streams + // have mark support and it is not ok to just return true. + // There should be an input.markSupported(). However, if + // this call returns false, nothing works anymore. + + // The backside is that BitmapFactory uses a call to markSupport() + // to find out if it needs to warp the stream in a + // BufferedInputStream to get mark support, and this fails! + + // Currently, the hack is in BitmapFactory, where we always + // wrap the stream in a BufferedInputStream. + } + + @Override + public void mark(int readlimit) { + input.mark(); + } + + @Override + public void reset() throws IOException { + input.reset(); + } + + @Override + public void close() throws IOException { + input.close(); + } + } +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGConsts.java b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGConsts.java new file mode 100644 index 000000000..067a82505 --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGConsts.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.2 $ + */ +package org.apache.harmony.x.imageio.plugins.jpeg; + +public class JPEGConsts { + + private JPEGConsts() {} + + public static final int SOI = 0xD8; + + //-- IJG (Independed JPEG Group) color spaces + public static final int JCS_UNKNOW = 0; + public static final int JCS_GRAYSCALE = 1; + public static final int JCS_RGB = 2; + public static final int JCS_YCbCr = 3; + public static final int JCS_CMYK = 4; + public static final int JCS_YCC = 5; + public static final int JCS_RGBA = 6; + public static final int JCS_YCbCrA = 7; + public static final int JCS_YCCA = 10; + public static final int JCS_YCCK = 11; + + public static int[][] BAND_OFFSETS = {{}, {0}, {0, 1}, {0, 1, 2}, {0, 1, 2, 3}}; + + public static final float DEFAULT_JPEG_COMPRESSION_QUALITY = 0.75f; +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageReader.java b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageReader.java new file mode 100644 index 000000000..110ed23ac --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageReader.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.4 $ + */ +package org.apache.harmony.x.imageio.plugins.jpeg; + + +import javax.imageio.ImageReader; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.plugins.jpeg.JPEGImageReadParam; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.spi.ImageReaderSpi; + +import org.apache.harmony.awt.gl.image.DecodingImageSource; +import org.apache.harmony.awt.gl.image.OffscreenImage; + +import java.io.IOException; +import java.util.Iterator; +import java.awt.image.BufferedImage; + +/** + * This implementation uses org.apache.harmony.awt.gl.image.JpegDecoder to read + * an image. The only implemented method is read(..); + * + * TODO: Implements generic decoder to be used by javad2 and imageio + * + * @see org.apache.harmony.awt.gl.image.JpegDecoder + * @see org.apache.harmony.x.imageio.plugins.jpeg.IISDecodingImageSource + */ +public class JPEGImageReader extends ImageReader { + + ImageInputStream iis; + + public JPEGImageReader(ImageReaderSpi imageReaderSpi) { + super(imageReaderSpi); + } + + @Override + public int getHeight(int i) throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public int getWidth(int i) throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public int getNumImages(boolean b) throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public Iterator getImageTypes(int i) throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public IIOMetadata getStreamMetadata() throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public IIOMetadata getImageMetadata(int i) throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public BufferedImage read(int i, ImageReadParam imageReadParam) throws IOException { + if (iis == null) { + throw new IllegalArgumentException("input stream == null"); + } + + DecodingImageSource source = new IISDecodingImageSource(iis); + OffscreenImage image = new OffscreenImage(source); + source.addConsumer(image); + source.load(); + // The interrupted flag should be cleared because ImageDecoder interrupts + // current thread while decoding. The same technique is used in + // ImageLoader#run(). Another solution can be to create + // a separate decoding thread. However, decoder keeps its own pool + // of threads so creating a new thread will be just a waste of resources. + Thread.interrupted(); + return image.getBufferedImage(); + } + + @Override + public BufferedImage read(int i) throws IOException { + return read(i, null); + } + + @Override + public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) { + super.setInput(input, seekForwardOnly, ignoreMetadata); + iis = (ImageInputStream) input; + } + + @Override + public ImageReadParam getDefaultReadParam() { + return new JPEGImageReadParam(); + } +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageReaderSpi.java b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageReaderSpi.java new file mode 100644 index 000000000..c719ce77a --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageReaderSpi.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ +package org.apache.harmony.x.imageio.plugins.jpeg; + +import java.io.IOException; +import java.util.Locale; +import javax.imageio.ImageReader; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.spi.ServiceRegistry; +import javax.imageio.stream.ImageInputStream; + +public class JPEGImageReaderSpi extends ImageReaderSpi { + + public JPEGImageReaderSpi() { + super(JPEGSpiConsts.vendorName, JPEGSpiConsts.version, + JPEGSpiConsts.names, JPEGSpiConsts.suffixes, + JPEGSpiConsts.MIMETypes, JPEGSpiConsts.readerClassName, + STANDARD_INPUT_TYPE, JPEGSpiConsts.writerSpiNames, + JPEGSpiConsts.supportsStandardStreamMetadataFormat, + JPEGSpiConsts.nativeStreamMetadataFormatName, + JPEGSpiConsts.nativeStreamMetadataFormatClassName, + JPEGSpiConsts.extraStreamMetadataFormatNames, + JPEGSpiConsts.extraStreamMetadataFormatClassNames, + JPEGSpiConsts.supportsStandardImageMetadataFormat, + JPEGSpiConsts.nativeImageMetadataFormatName, + JPEGSpiConsts.nativeImageMetadataFormatClassName, + JPEGSpiConsts.extraImageMetadataFormatNames, + JPEGSpiConsts.extraImageMetadataFormatClassNames); + } + + + @Override + public boolean canDecodeInput(Object source) throws IOException { + ImageInputStream markable = (ImageInputStream) source; + try { + markable.mark(); + + byte[] signature = new byte[3]; + markable.seek(0); + markable.read(signature, 0, 3); + markable.reset(); + + if ((signature[0] & 0xFF) == 0xFF && + (signature[1] & 0xFF) == JPEGConsts.SOI && + (signature[2] & 0xFF) == 0xFF) { // JPEG + return true; + } + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + @Override + public ImageReader createReaderInstance(Object extension) throws IOException { + return new JPEGImageReader(this); + } + + @Override + public String getDescription(Locale locale) { + return "DRL JPEG decoder"; + } + + @Override + public void onRegistration(ServiceRegistry registry, Class category) { + // super.onRegistration(registry, category); + } +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageWriter.java b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageWriter.java new file mode 100644 index 000000000..ae3e87639 --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageWriter.java @@ -0,0 +1,402 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ +package org.apache.harmony.x.imageio.plugins.jpeg; + +import com.android.internal.awt.ImageOutputStreamWrapper; + +import javax.imageio.ImageWriter; +import javax.imageio.IIOImage; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.plugins.jpeg.JPEGImageWriteParam; +import javax.imageio.stream.ImageOutputStream; +import javax.imageio.spi.ImageWriterSpi; +import javax.imageio.metadata.IIOMetadata; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap.CompressFormat; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.awt.image.*; +import java.awt.*; +import java.awt.color.ColorSpace; + +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ +public class JPEGImageWriter extends ImageWriter { + + // /* ???AWT: Debugging + private static final boolean DEBUG = false; + private static Bitmap bm; + public static Bitmap getBitmap() { + return bm; + } + private static BufferedImage bufImg; + public static BufferedImage getBufImage() { + return bufImg; + } + static private RenderedImage renImg; + static public RenderedImage getRenImage() { + return renImg; + } + // */ + + private long cinfo; + private RenderedImage image; + private Raster sourceRaster; + private WritableRaster scanRaster; + private int srcXOff = 0; + private int srcYOff = 0; + private int srcWidth; + private int srcHeight; + + //-- y step for image subsampling + private int deltaY = 1; + //-- x step for image subsampling + private int deltaX = 1; + + private ImageOutputStream ios; + + public JPEGImageWriter(ImageWriterSpi imageWriterSpi) { + super(imageWriterSpi); + //???AWT: cinfo = initCompressionObj(); + cinfo = System.currentTimeMillis(); + } + + static { + //???AWT + /* + System.loadLibrary("jpegencoder"); + initWriterIds(ImageOutputStream.class); + */ + } + + @Override + public void write(IIOMetadata iioMetadata, IIOImage iioImage, ImageWriteParam param) + throws IOException { + + if (ios == null) { + throw new IllegalArgumentException("ios == null"); + } + if (iioImage == null) { + throw new IllegalArgumentException("Image equals null"); + } + + RenderedImage img = null; + if (!iioImage.hasRaster()) { + img = iioImage.getRenderedImage(); + if (img instanceof BufferedImage) { + sourceRaster = ((BufferedImage) img).getRaster(); + } else { + sourceRaster = img.getData(); + } + } else { + sourceRaster = iioImage.getRaster(); + } + + // AWT???: Debugging + if (DEBUG) { + if( img==null ) { + System.out.println("****J: Image is NULL"); + } else { + renImg = img; + bufImg = (BufferedImage)img; + } + } + + int numBands = sourceRaster.getNumBands(); + int sourceIJGCs = img == null ? JPEGConsts.JCS_UNKNOW : getSourceCSType(img); + + srcWidth = sourceRaster.getWidth(); + srcHeight = sourceRaster.getHeight(); + + int destWidth = srcWidth; + int destHeight = srcHeight; + + boolean progressive = false; + + if (param != null) { + Rectangle reg = param.getSourceRegion(); + if (reg != null) { + srcXOff = reg.x; + srcYOff = reg.y; + + srcWidth = reg.width + srcXOff > srcWidth + ? srcWidth - srcXOff + : reg.width; + srcHeight = reg.height + srcYOff > srcHeight + ? srcHeight - srcYOff + : reg.height; + } + + //-- TODO uncomment when JPEGImageWriteParam be implemented + //-- Only default progressive mode yet + // progressive = param.getProgressiveMode() == ImageWriteParam.MODE_DEFAULT; + + //-- def is 1 + deltaX = param.getSourceXSubsampling(); + deltaY = param.getSourceYSubsampling(); + + //-- def is 0 + int offsetX = param.getSubsamplingXOffset(); + int offsetY = param.getSubsamplingYOffset(); + + srcXOff += offsetX; + srcYOff += offsetY; + srcWidth -= offsetX; + srcHeight -= offsetY; + + destWidth = (srcWidth + deltaX - 1) / deltaX; + destHeight = (srcHeight + deltaY - 1) / deltaY; + } + + //-- default DQTs (see JPEGQTable java doc and JPEG spec K1 & K2 tables) + //-- at http://www.w3.org/Graphics/JPEG/itu-t81.pdf + //-- Only figuring out how to set DQT in IJG library for future metadata + //-- support. IJG def tables are the same. + //JPEGQTable[] dqt = new JPEGQTable[2]; +// int[][] dqt = null; +// int[][] dqt = new int[2][]; +// dqt[0] = JPEGQTable.K1Div2Luminance.getTable(); +// dqt[1] = JPEGQTable.K2Div2Chrominance.getTable(); + + //???AWT: I think we don't need this amymore + /* + //-- using default color space + //-- TODO: Take destination cs from param or use default if there is no cs + int destIJGCs = img == null ? JPEGConsts.JCS_UNKNOW : getDestinationCSType(img); + + DataBufferByte dbuffer = new DataBufferByte(numBands * srcWidth); + + scanRaster = Raster.createInterleavedRaster(dbuffer, srcWidth, 1, + numBands * srcWidth, numBands, JPEGConsts.BAND_OFFSETS[numBands], null); + + encode(dbuffer.getData(), srcWidth, destWidth, destHeight, deltaX, + sourceIJGCs, destIJGCs, numBands, progressive, + null, cinfo); + */ + + SampleModel model = sourceRaster.getSampleModel(); + + if (model instanceof SinglePixelPackedSampleModel) { + DataBufferInt ibuf = (DataBufferInt)sourceRaster.getDataBuffer(); + int[] pixels = ibuf.getData(); + + // Create a bitmap with the pixel + bm = Bitmap.createBitmap(pixels, srcWidth, srcHeight, Bitmap.Config.ARGB_8888); + + // Use Bitmap.compress() to write the image + ImageOutputStreamWrapper iosw = new ImageOutputStreamWrapper(ios); + bm.compress(CompressFormat.JPEG, 100, iosw); + } else { + // ???AWT: Add support for other color models + throw new RuntimeException("Color model not supported yet"); + } + + } + + @Override + public void dispose() { + super.dispose(); + if (cinfo != 0) { + //???AWT: dispose(cinfo); + cinfo = 0; + ios = null; + } + } + + + public IIOMetadata getDefaultStreamMetadata(ImageWriteParam imageWriteParam) { + throw new UnsupportedOperationException("not supported yet"); + } + + public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageTypeSpecifier, ImageWriteParam imageWriteParam) { + throw new UnsupportedOperationException("not supported yet"); + } + + @Override + public IIOMetadata convertStreamMetadata(IIOMetadata iioMetadata, ImageWriteParam imageWriteParam) { + throw new UnsupportedOperationException("not supported yet"); + } + + @Override + public IIOMetadata convertImageMetadata(IIOMetadata iioMetadata, ImageTypeSpecifier imageTypeSpecifier, ImageWriteParam imageWriteParam) { + throw new UnsupportedOperationException("not supported yet"); + } + + @Override + public void setOutput(Object output) { + super.setOutput(output); + ios = (ImageOutputStream) output; + //???AWT: setIOS(ios, cinfo); + sourceRaster = null; + scanRaster = null; + srcXOff = 0; + srcYOff = 0; + srcWidth = 0; + srcHeight = 0; + deltaY = 1; + } + + /** + * Frees resources + * @param structPointer + */ + //???AWT: private native void dispose(long structPointer); + + /** + * Inits methods Ids for native to java callbacks + * @param iosClass + */ + //???AWT: private native static void initWriterIds(Class iosClass); + + /** + * Inits compression objects + * @return pointer to the native structure + */ + //???AWT: private native long initCompressionObj(); + + /** + * Sets image output stream in IJG layer + * @param stream + */ + //???AWT: private native void setIOS(ImageOutputStream stream, long structPointer); + + /** + * Runs encoding process. + * + * @param data image data buffer to encode + * @param srcWidth - source width + * @param width - destination width + * @param height destination height + * @param deltaX - x subsampling step + * @param inColorSpace - original color space + * @param outColorSpace - destination color space + * @param numBands - number of bands + * @param cinfo - native handler + * @return + */ + //???AWT: + /* + private native boolean encode(byte[] data, int srcWidth, + int width, int height, int deltaX, + int inColorSpace, int outColorSpace, + int numBands, boolean progressive, + int[][] dqt, + long cinfo); + */ + + /** + * Callback for getting a next scanline + * @param scanline scan line number + */ + @SuppressWarnings("unused") + private void getScanLine(int scanline) { + //-- TODO: processImageProgress in ImageWriter + Raster child = sourceRaster.createChild(srcXOff, + srcYOff + scanline * deltaY, srcWidth, 1, 0, 0, null); + + scanRaster.setRect(child); + } + + /** + * Maps color space types to IJG color spaces + * @param image + * @return + */ + private int getSourceCSType(RenderedImage image) { + int type = JPEGConsts.JCS_UNKNOW; + ColorModel cm = image.getColorModel(); + + if (null == cm) { + return type; + } + + if (cm instanceof IndexColorModel) { + throw new UnsupportedOperationException("IndexColorModel is not supported yet"); + } + + boolean hasAlpha = cm.hasAlpha(); + ColorSpace cs = cm.getColorSpace(); + switch(cs.getType()) { + case ColorSpace.TYPE_GRAY: + type = JPEGConsts.JCS_GRAYSCALE; + break; + case ColorSpace.TYPE_RGB: + type = hasAlpha ? JPEGConsts.JCS_RGBA : JPEGConsts.JCS_RGB; + break; + case ColorSpace.TYPE_YCbCr: + type = hasAlpha ? JPEGConsts.JCS_YCbCrA : JPEGConsts.JCS_YCbCr; + break; + case ColorSpace.TYPE_3CLR: + type = hasAlpha ? JPEGConsts.JCS_YCCA : JPEGConsts.JCS_YCC; + break; + case ColorSpace.TYPE_CMYK: + type = JPEGConsts.JCS_CMYK; + break; + } + return type; + } + + /** + * Returns destination color space. + * (YCbCr[A] for RGB) + * + * @param image + * @return + */ + private int getDestinationCSType(RenderedImage image) { + int type = JPEGConsts.JCS_UNKNOW; + ColorModel cm = image.getColorModel(); + if (null != cm) { + boolean hasAlpha = cm.hasAlpha(); + ColorSpace cs = cm.getColorSpace(); + + switch(cs.getType()) { + case ColorSpace.TYPE_GRAY: + type = JPEGConsts.JCS_GRAYSCALE; + break; + case ColorSpace.TYPE_RGB: + type = hasAlpha ? JPEGConsts.JCS_YCbCrA : JPEGConsts.JCS_YCbCr; + break; + case ColorSpace.TYPE_YCbCr: + type = hasAlpha ? JPEGConsts.JCS_YCbCrA : JPEGConsts.JCS_YCbCr; + break; + case ColorSpace.TYPE_3CLR: + type = hasAlpha ? JPEGConsts.JCS_YCCA : JPEGConsts.JCS_YCC; + break; + case ColorSpace.TYPE_CMYK: + type = JPEGConsts.JCS_CMYK; + break; + } + } + return type; + } + + public ImageWriteParam getDefaultWriteParam() { + return new JPEGImageWriteParam(getLocale()); + } +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageWriterSpi.java b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageWriterSpi.java new file mode 100644 index 000000000..b7990e088 --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGImageWriterSpi.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.3 $ + */ +package org.apache.harmony.x.imageio.plugins.jpeg; + +import javax.imageio.spi.ImageWriterSpi; +import javax.imageio.ImageWriter; +import javax.imageio.ImageTypeSpecifier; +import java.io.IOException; +import java.util.Locale; + +public class JPEGImageWriterSpi extends ImageWriterSpi { + + public JPEGImageWriterSpi() { + super(JPEGSpiConsts.vendorName, JPEGSpiConsts.version, + JPEGSpiConsts.names, JPEGSpiConsts.suffixes, JPEGSpiConsts.MIMETypes, + JPEGSpiConsts.writerClassName, STANDARD_OUTPUT_TYPE, + JPEGSpiConsts.readerSpiNames, JPEGSpiConsts.supportsStandardStreamMetadataFormat /*TODO: support st. metadata format*/, + JPEGSpiConsts.nativeStreamMetadataFormatName, JPEGSpiConsts.nativeStreamMetadataFormatClassName, + JPEGSpiConsts.extraStreamMetadataFormatNames, JPEGSpiConsts.extraStreamMetadataFormatClassNames, + JPEGSpiConsts.supportsStandardImageMetadataFormat, JPEGSpiConsts.nativeImageMetadataFormatName, JPEGSpiConsts.nativeImageMetadataFormatClassName, + JPEGSpiConsts.extraImageMetadataFormatNames, JPEGSpiConsts.extraImageMetadataFormatClassNames); + } + + @Override + public boolean canEncodeImage(ImageTypeSpecifier imageTypeSpecifier) { + return true; + } + + @Override + public ImageWriter createWriterInstance(Object o) throws IOException { + return new JPEGImageWriter(this); + } + + @Override + public String getDescription(Locale locale) { + return "DRL JPEG Encoder"; + } +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGSpiConsts.java b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGSpiConsts.java new file mode 100644 index 000000000..c3b4a506f --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/jpeg/JPEGSpiConsts.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.2 $ + */ +package org.apache.harmony.x.imageio.plugins.jpeg; + +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.2 $ + */ +public class JPEGSpiConsts { + private JPEGSpiConsts() {} + + public static final String vendorName = "Intel Corporation"; + public static final String version = "0.1 beta"; + + static final String readerClassName = "org.apache.harmony.x.imageio.plugins.jpeg.JPEGImageReader"; + static final String writerClassName = "org.apache.harmony.x.imageio.plugins.jpeg.JPEGImageWriter"; + + static final String[] names = {"jpeg", "jpg", "JPEG", "JPG"}; + static final String[] suffixes = {"jpeg", "jpg"}; + static final String[] MIMETypes = {"image/jpeg"}; + + static final String[] writerSpiNames = {"org.apache.harmony.x.imageio.plugins.jpeg.JPEGImageWriterSpi"}; + static final String[] readerSpiNames = {"org.apache.harmony.x.imageio.plugins.jpeg.JPEGImageReaderSpi"}; + + //-- TODO fill this stuff with correct data + static final boolean supportsStandardStreamMetadataFormat = false; + static final String nativeStreamMetadataFormatName = null; + static final String nativeStreamMetadataFormatClassName = null; + static final String[] extraStreamMetadataFormatNames = null; + static final String[] extraStreamMetadataFormatClassNames = null; + static final boolean supportsStandardImageMetadataFormat = false; + static final String nativeImageMetadataFormatName = + "org.apache.harmony.x.imageio.plugins.jpeg.MyFormatMetadata_1.0"; + static final String nativeImageMetadataFormatClassName = + "org.apache.harmony.x.imageio.plugins.jpeg.MyFormatMetadata"; + static final String[] extraImageMetadataFormatNames = null; + static final String[] extraImageMetadataFormatClassNames = null; + +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageReader.java b/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageReader.java new file mode 100644 index 000000000..480041c9c --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageReader.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.harmony.x.imageio.plugins.png; + +import org.apache.harmony.awt.gl.image.DecodingImageSource; +import org.apache.harmony.awt.gl.image.OffscreenImage; +import org.apache.harmony.x.imageio.plugins.jpeg.IISDecodingImageSource; + +import javax.imageio.ImageReader; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageReadParam; +import javax.imageio.plugins.jpeg.JPEGImageReadParam; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.metadata.IIOMetadata; +import java.io.IOException; +import java.util.Iterator; +import java.awt.image.BufferedImage; + +public class PNGImageReader extends ImageReader { + ImageInputStream iis; + + public PNGImageReader(ImageReaderSpi imageReaderSpi) { + super(imageReaderSpi); + } + + public int getNumImages(boolean allowSearch) throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + public int getWidth(int imageIndex) throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + public int getHeight(int imageIndex) throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + public Iterator getImageTypes(int imageIndex) throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public IIOMetadata getStreamMetadata() throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public IIOMetadata getImageMetadata(int imageIndex) throws IOException { + //-- TODO imlement + throw new UnsupportedOperationException("not implemented yet"); + } + + @Override + public BufferedImage read(int i, ImageReadParam imageReadParam) throws IOException { + if (iis == null) { + throw new IllegalArgumentException("input stream == null"); + } + + DecodingImageSource source = new IISDecodingImageSource(iis); + OffscreenImage image = new OffscreenImage(source); + source.addConsumer(image); + source.load(); + // The interrupted flag should be cleared because ImageDecoder interrupts + // current thread while decoding (due its architecture). + Thread.interrupted(); + return image.getBufferedImage(); + } + + @Override + public BufferedImage read(int i) throws IOException { + return read(i, null); + } + + @Override + public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) { + super.setInput(input, seekForwardOnly, ignoreMetadata); + iis = (ImageInputStream) input; + } + + @Override + public ImageReadParam getDefaultReadParam() { + return new ImageReadParam(); + } +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageReaderSpi.java b/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageReaderSpi.java new file mode 100644 index 000000000..50f8b1078 --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageReaderSpi.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.harmony.x.imageio.plugins.png; + +import org.apache.harmony.x.imageio.plugins.jpeg.JPEGSpiConsts; + +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.spi.ServiceRegistry; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; +import java.util.Locale; + +public class PNGImageReaderSpi extends ImageReaderSpi { + static final String PNG_NAMES[] = new String[] {"png", "PNG"}; + static final String PNG_SUFFIXES[] = new String[] {"png"}; + static final String PNG_MIME_TYPES[] = new String[] {"image/png"}; + static final String PNG_READER_CLASS_NAME = "org.apache.harmony.x.imageio.plugins.png.PNGImageReader"; + static final String PNG_READER_SPI_NAMES[] = {"org.apache.harmony.x.imageio.plugins.png.PNGImageReaderSpi"}; + + public PNGImageReaderSpi() { + super( + JPEGSpiConsts.vendorName, JPEGSpiConsts.version, + PNG_NAMES, PNG_SUFFIXES, + PNG_MIME_TYPES, PNG_READER_CLASS_NAME, + STANDARD_INPUT_TYPE, null, + false, null, + null, null, + null, false, + null, null, + null, null + ); + } + + @Override + public boolean canDecodeInput(Object source) throws IOException { + ImageInputStream markable = (ImageInputStream) source; + markable.mark(); + + byte[] signature = new byte[8]; + markable.seek(0); + + int nBytes = markable.read(signature, 0, 8); + if(nBytes != 8) markable.read(signature, nBytes, 8-nBytes); + markable.reset(); + + // PNG signature: 137 80 78 71 13 10 26 10 + return (signature[0] & 0xFF) == 137 && + (signature[1] & 0xFF) == 80 && + (signature[2] & 0xFF) == 78 && + (signature[3] & 0xFF) == 71 && + (signature[4] & 0xFF) == 13 && + (signature[5] & 0xFF) == 10 && + (signature[6] & 0xFF) == 26 && + (signature[7] & 0xFF) == 10; + } + + @Override + public ImageReader createReaderInstance(Object extension) throws IOException { + return new PNGImageReader(this); + } + + @Override + public String getDescription(Locale locale) { + return "DRL PNG decoder"; + } + + @Override + public void onRegistration(ServiceRegistry registry, Class category) { + super.onRegistration(registry, category); + } +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriter.java b/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriter.java new file mode 100644 index 000000000..e2a8d7de6 --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriter.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Viskov Nikolay + * @version $Revision$ + */ +package org.apache.harmony.x.imageio.plugins.png; + +import com.android.internal.awt.ImageOutputStreamWrapper; + +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.awt.image.IndexColorModel; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; +import java.io.IOException; + +import javax.imageio.IIOImage; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.spi.ImageWriterSpi; +import javax.imageio.stream.ImageOutputStream; + +import org.apache.harmony.x.imageio.internal.nls.Messages; + +import org.apache.harmony.luni.util.NotImplementedException; + +import android.graphics.Bitmap; +import android.graphics.Bitmap.CompressFormat; + +public class PNGImageWriter extends ImageWriter { + + // /* ???AWT: Debugging + private static final boolean DEBUG = false; + private static Bitmap bm; + public static Bitmap getBitmap() { + return bm; + } + // */ + + private static int[][] BAND_OFFSETS = { + {}, { + 0 }, { + 0, 1 }, { + 0, 1, 2 }, { + 0, 1, 2, 3 } }; + + // Each pixel is a grayscale sample. + private static final int PNG_COLOR_TYPE_GRAY = 0; + // Each pixel is an R,G,B triple. + private static final int PNG_COLOR_TYPE_RGB = 2; + // Each pixel is a palette index, a PLTE chunk must appear. + private static final int PNG_COLOR_TYPE_PLTE = 3; + // Each pixel is a grayscale sample, followed by an alpha sample. + private static final int PNG_COLOR_TYPE_GRAY_ALPHA = 4; + // Each pixel is an R,G,B triple, followed by an alpha sample. + private static final int PNG_COLOR_TYPE_RGBA = 6; + + //???AWT: private static native void initIDs(Class iosClass); + + static { + //???AWT + /* + System.loadLibrary("pngencoder"); //$NON-NLS-1$ + initIDs(ImageOutputStream.class); + */ + } + + /* + private native int encode(byte[] input, int bytesInBuffer, int bytePixelSize, Object ios, int imageWidth, + int imageHeight, int bitDepth, int colorType, int[] palette, int i, boolean b); + */ + + protected PNGImageWriter(ImageWriterSpi iwSpi) { + super(iwSpi); + } + + @Override + public IIOMetadata convertStreamMetadata(IIOMetadata arg0, ImageWriteParam arg1) { + throw new NotImplementedException(); + } + + @Override + public IIOMetadata convertImageMetadata(IIOMetadata arg0, ImageTypeSpecifier arg1, ImageWriteParam arg2) { + throw new NotImplementedException(); + } + + @Override + public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier arg0, ImageWriteParam arg1) { + throw new NotImplementedException(); + } + + @Override + public IIOMetadata getDefaultStreamMetadata(ImageWriteParam arg0) { + throw new NotImplementedException(); + } + + @Override + public void write(IIOMetadata streamMetadata, IIOImage iioImage, ImageWriteParam param) throws IOException { + if (output == null) { + throw new IllegalStateException("Output not been set"); + } + if (iioImage == null) { + throw new IllegalArgumentException("Image equals null"); + } + // AWT???: I think this is not needed anymore + // if (iioImage.hasRaster() && !canWriteRasters()) { + // throw new UnsupportedOperationException("Can't write raster"); + //}// ImageOutputStreamImpl + + Raster sourceRaster; + RenderedImage img = null; + if (!iioImage.hasRaster()) { + img = iioImage.getRenderedImage(); + if (img instanceof BufferedImage) { + sourceRaster = ((BufferedImage) img).getRaster(); + } else { + sourceRaster = img.getData(); + } + } else { + sourceRaster = iioImage.getRaster(); + } + + SampleModel model = sourceRaster.getSampleModel(); + int srcWidth = sourceRaster.getWidth(); + int srcHeight = sourceRaster.getHeight(); + int numBands = model.getNumBands(); + + ColorModel colorModel = img.getColorModel(); + int pixelSize = colorModel.getPixelSize(); + int bytePixelSize = pixelSize / 8; + int bitDepth = pixelSize / numBands; + + // byte per band + int bpb = bitDepth > 8 ? 2 : 1; + + boolean isInterlace = true; + if (param instanceof PNGImageWriterParam) { + isInterlace = ((PNGImageWriterParam) param).getInterlace(); + } + + int colorType = PNG_COLOR_TYPE_GRAY; + int[] palette = null; + + if (colorModel instanceof IndexColorModel) { + if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8) { +// Wrong bitDepth-numBands composition + throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$ + } + if (numBands != 1) { +// Wrong bitDepth-numBands composition + throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$ + } + + IndexColorModel icm = (IndexColorModel) colorModel; + palette = new int[icm.getMapSize()]; + icm.getRGBs(palette); + colorType = PNG_COLOR_TYPE_PLTE; + } + else if (numBands == 1) { + if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 && bitDepth != 16) { +// Wrong bitDepth-numBands composition + throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$ + } + colorType = PNG_COLOR_TYPE_GRAY; + } + else if (numBands == 2) { + if (bitDepth != 8 && bitDepth != 16) { +// Wrong bitDepth-numBands composition + throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$ + } + colorType = PNG_COLOR_TYPE_GRAY_ALPHA; + } + else if (numBands == 3) { + if (bitDepth != 8 && bitDepth != 16) { +// Wrong bitDepth-numBands composition + throw new IllegalArgumentException(Messages.getString("imageio.1")); //$NON-NLS-1$ + } + colorType = PNG_COLOR_TYPE_RGB; + } + else if (numBands == 4) { + if (bitDepth != 8 && bitDepth != 16) { + //Wrong bitDepth-numBands composition + throw new IllegalArgumentException(Messages.getString("imageio.1")); //$NON-NLS-1$ + } + colorType = PNG_COLOR_TYPE_RGBA; + } + + /* ???AWT: I think this is not needed anymore + int dbufferLenght = bytePixelSize * imageHeight * imageWidth; + DataBufferByte dbuffer = new DataBufferByte(dbufferLenght); + + WritableRaster scanRaster = Raster.createInterleavedRaster(dbuffer, imageWidth, imageHeight, bpb * numBands + * imageWidth, bpb * numBands, BAND_OFFSETS[numBands], null); + + scanRaster.setRect(((BufferedImage) image).getRaster()// image.getData() + .createChild(0, 0, imageWidth, imageHeight, 0, 0, null)); + */ + + if (DEBUG) { + System.out.println("**** raster:" + sourceRaster); + System.out.println("**** model:" + model); + System.out.println("**** type:" + colorType); + } + + if (model instanceof SinglePixelPackedSampleModel) { + DataBufferInt ibuf = (DataBufferInt)sourceRaster.getDataBuffer(); + int[] pixels = ibuf.getData(); + + // Create a bitmap with the pixel + bm = Bitmap.createBitmap(pixels, srcWidth, srcHeight, Bitmap.Config.ARGB_8888); + + // Use Bitmap.compress() to write the image + ImageOutputStream ios = (ImageOutputStream) getOutput(); + ImageOutputStreamWrapper iosw = new ImageOutputStreamWrapper(ios); + bm.compress(CompressFormat.PNG, 100, iosw); + } else { + // ???AWT: Add support for other color models + throw new RuntimeException("Color model not supported yet"); + } + } + + public ImageWriteParam getDefaultWriteParam() { + return new PNGImageWriterParam(); + } +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriterParam.java b/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriterParam.java new file mode 100644 index 000000000..bf3a0003a --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriterParam.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Viskov Nikolay + * @version $Revision$ + */ +package org.apache.harmony.x.imageio.plugins.png; + +import javax.imageio.ImageWriteParam; + +public class PNGImageWriterParam extends ImageWriteParam { + + private boolean isInterlace = true; + + public PNGImageWriterParam() { + super(); + } + + public boolean getInterlace() { + return isInterlace; + } + + public void setInterlace(boolean b) { + isInterlace = b; + } + +} diff --git a/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriterSpi.java b/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriterSpi.java new file mode 100644 index 000000000..6eed14dfc --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/plugins/png/PNGImageWriterSpi.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Viskov Nikolay + * @version $Revision$ + */ +package org.apache.harmony.x.imageio.plugins.png; + +import java.awt.image.ColorModel; +import java.awt.image.DataBufferByte; +import java.awt.image.IndexColorModel; +import java.io.IOException; +import java.util.Locale; + +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriter; +import javax.imageio.spi.ImageWriterSpi; + +public class PNGImageWriterSpi extends ImageWriterSpi { + + public PNGImageWriterSpi() { + super("Intel Corporation",// vendorName + "1.0",// version + new String[] { + "png", "PNG" },// names + new String[] { + "png", "PNG" },// suffixes + new String[] { + "image/png" },// MIMETypes + "org.apache.harmony.x.imageio.plugins.png.PNGImageWriter",// writerClassName + STANDARD_OUTPUT_TYPE,// outputTypes + new String[] { + "org.apache.harmony.x.imageio.plugins.png.PNGImageWriterSpi" },// readerSpiNames + false,// supportsStandardStreamMetadataFormat + null,// nativeStreamMetadataFormatName + null,// nativeStreamMetadataFormatClassName + null,// extraStreamMetadataFormatNames + null,// extraStreamMetadataFormatClassNames + false,// supportsStandardImageMetadataFormat + null,// nativeImageMetadataFormatName + null,// nativeImageMetadataFormatClassName + null,// extraImageMetadataFormatNames + null// extraImageMetadataFormatClassNames + ); + } + + @Override + public boolean canEncodeImage(ImageTypeSpecifier type) { + boolean canEncode = true; + + int numBands = type.getSampleModel().getNumBands(); + + ColorModel colorModel = type.getColorModel(); + + int bitDepth = colorModel.getPixelSize() / numBands; + + if (colorModel instanceof IndexColorModel) { + if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8) { + canEncode = false; + } + if (numBands != 1) { + canEncode = false; + } + } + else if (numBands == 1) { + if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 && bitDepth != 16) { + canEncode = false; + } + } + else if (numBands == 2) { + if (bitDepth != 8 && bitDepth != 16) { + canEncode = false; + } + } + else if (numBands == 3) { + if (bitDepth != 8 && bitDepth != 16) { + canEncode = false; + } + } + else if (numBands == 4) { + if (bitDepth != 8 && bitDepth != 16) { + canEncode = false; + } + } + + return canEncode; + } + + @Override + public ImageWriter createWriterInstance(Object arg0) throws IOException { + return new PNGImageWriter(this); + } + + @Override + public String getDescription(Locale arg0) { + return "DRL PNG encoder"; + } + +} diff --git a/awt/org/apache/harmony/x/imageio/spi/FileIISSpi.java b/awt/org/apache/harmony/x/imageio/spi/FileIISSpi.java new file mode 100644 index 000000000..d4fdd764f --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/spi/FileIISSpi.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.2 $ + */ +package org.apache.harmony.x.imageio.spi; + +import javax.imageio.spi.ImageInputStreamSpi; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.FileImageOutputStream; +import javax.imageio.stream.FileImageInputStream; +import java.io.File; +import java.io.IOException; +import java.util.Locale; + +public class FileIISSpi extends ImageInputStreamSpi { + private static final String vendor = "Apache"; + + private static final String ver = "0.1"; + + public FileIISSpi() { + super(vendor, ver, File.class); + } + + @Override + public ImageInputStream createInputStreamInstance(Object input, boolean useCache, + File cacheDir) throws IOException { + if (File.class.isInstance(input)) { + return new FileImageInputStream((File) input); + } + throw new IllegalArgumentException("input is not an instance of java.io.File"); + } + + @Override + public String getDescription(Locale locale) { + return "File IIS Spi"; + } +} diff --git a/awt/org/apache/harmony/x/imageio/spi/FileIOSSpi.java b/awt/org/apache/harmony/x/imageio/spi/FileIOSSpi.java new file mode 100644 index 000000000..acda6a1bf --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/spi/FileIOSSpi.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.2 $ + */ +package org.apache.harmony.x.imageio.spi; + +import javax.imageio.spi.ImageOutputStreamSpi; +import javax.imageio.stream.ImageOutputStream; +import javax.imageio.stream.FileImageOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.Locale; + +public class FileIOSSpi extends ImageOutputStreamSpi { + private static final String vendor = "Apache"; + + private static final String ver = "0.1"; + + public FileIOSSpi() { + super(vendor, ver, File.class); + } + + @Override + public ImageOutputStream createOutputStreamInstance(Object output, boolean useCache, + File cacheDir) throws IOException { + if (output instanceof File) { + return new FileImageOutputStream((File) output); + } + throw new IllegalArgumentException("output is not instance of File"); + } + + @Override + public String getDescription(Locale locale) { + return "File IOS Spi"; + } +} diff --git a/awt/org/apache/harmony/x/imageio/spi/InputStreamIISSpi.java b/awt/org/apache/harmony/x/imageio/spi/InputStreamIISSpi.java new file mode 100644 index 000000000..ed2fef076 --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/spi/InputStreamIISSpi.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.harmony.x.imageio.spi; + +import javax.imageio.spi.ImageInputStreamSpi; +import javax.imageio.stream.*; +import java.io.OutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; + +public class InputStreamIISSpi extends ImageInputStreamSpi { + private static final String vendor = "Apache"; + + private static final String ver = "0.1"; + + public InputStreamIISSpi() { + super(vendor, ver, InputStream.class); + } + + @Override + public String getDescription(Locale locale) { + return "Output Stream IOS Spi"; + } + + @Override + public boolean canUseCacheFile() { + return true; + } + + @Override + public ImageInputStream createInputStreamInstance(Object input, boolean useCache, File cacheDir) throws IOException { + if (input instanceof InputStream) { + if (useCache) { + return new FileCacheImageInputStream((InputStream) input, cacheDir); + } else { + return new MemoryCacheImageInputStream((InputStream) input); + } + } + throw new IllegalArgumentException("Output is not an instance of InputStream"); + } +} diff --git a/awt/org/apache/harmony/x/imageio/spi/OutputStreamIOSSpi.java b/awt/org/apache/harmony/x/imageio/spi/OutputStreamIOSSpi.java new file mode 100644 index 000000000..dd1e88dd4 --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/spi/OutputStreamIOSSpi.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.harmony.x.imageio.spi; + +import javax.imageio.spi.ImageOutputStreamSpi; +import javax.imageio.stream.ImageOutputStream; +import javax.imageio.stream.FileCacheImageOutputStream; +import javax.imageio.stream.MemoryCacheImageOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Locale; + +public class OutputStreamIOSSpi extends ImageOutputStreamSpi { + private static final String vendor = "Apache"; + + private static final String ver = "0.1"; + + public OutputStreamIOSSpi() { + super(vendor, ver, OutputStream.class); + } + + @Override + public ImageOutputStream createOutputStreamInstance(Object output, boolean useCache, File cacheDir) throws IOException { + if (output instanceof OutputStream) { + if (useCache) { + return new FileCacheImageOutputStream((OutputStream) output, cacheDir); + } else { + return new MemoryCacheImageOutputStream((OutputStream) output); + } + } + throw new IllegalArgumentException("Output is not an instance of OutputStream"); + } + + @Override + public String getDescription(Locale locale) { + return "Output Stream IOS Spi"; + } + + @Override + public boolean canUseCacheFile() { + return true; + } +} diff --git a/awt/org/apache/harmony/x/imageio/spi/RAFIISSpi.java b/awt/org/apache/harmony/x/imageio/spi/RAFIISSpi.java new file mode 100644 index 000000000..f97eb87e2 --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/spi/RAFIISSpi.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.2 $ + */ +package org.apache.harmony.x.imageio.spi; + +import javax.imageio.spi.ImageInputStreamSpi; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.FileImageInputStream; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Locale; + +public class RAFIISSpi extends ImageInputStreamSpi { + private static final String vendor = "Apache"; + + private static final String ver = "0.1"; + + public RAFIISSpi() { + super(vendor, ver, RandomAccessFile.class); + } + + @Override + public ImageInputStream createInputStreamInstance(Object input, boolean useCache, + File cacheDir) throws IOException { + if (RandomAccessFile.class.isInstance(input)) { + return new FileImageInputStream((RandomAccessFile) input); + } + throw new IllegalArgumentException( + "input is not an instance of java.io.RandomAccessFile"); + } + + @Override + public String getDescription(Locale locale) { + return "RandomAccessFile IIS Spi"; + } +} diff --git a/awt/org/apache/harmony/x/imageio/spi/RAFIOSSpi.java b/awt/org/apache/harmony/x/imageio/spi/RAFIOSSpi.java new file mode 100644 index 000000000..a9d36492a --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/spi/RAFIOSSpi.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Rustem V. Rafikov + * @version $Revision: 1.2 $ + */ +package org.apache.harmony.x.imageio.spi; + +import javax.imageio.spi.ImageOutputStreamSpi; +import javax.imageio.stream.ImageOutputStream; +import javax.imageio.stream.FileImageOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Locale; + +public class RAFIOSSpi extends ImageOutputStreamSpi { + private static final String vendor = "Apache"; + + private static final String ver = "0.1"; + + public RAFIOSSpi() { + super(vendor, ver, RandomAccessFile.class); + } + + @Override + public ImageOutputStream createOutputStreamInstance(Object output, boolean useCache, + File cacheDir) throws IOException { + if (output instanceof RandomAccessFile) { + return new FileImageOutputStream((RandomAccessFile) output); + } + throw new IllegalArgumentException("output is not instance of java.io.RandomAccessFile"); + } + + @Override + public String getDescription(Locale locale) { + return "RandomAccessFile IOS Spi"; + } +} diff --git a/awt/org/apache/harmony/x/imageio/stream/RandomAccessMemoryCache.java b/awt/org/apache/harmony/x/imageio/stream/RandomAccessMemoryCache.java new file mode 100644 index 000000000..64f7b2a09 --- /dev/null +++ b/awt/org/apache/harmony/x/imageio/stream/RandomAccessMemoryCache.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.harmony.x.imageio.stream; + +import java.util.ArrayList; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public final class RandomAccessMemoryCache { + private static final int BLOCK_SHIFT = 9; + private static final int BLOCK_SIZE = 1 << BLOCK_SHIFT; + private static final int BLOCK_MASK = BLOCK_SIZE - 1; + + private long length; + + private int firstUndisposed = 0; + + private ArrayList blocks = new ArrayList(); + + public RandomAccessMemoryCache() { + } + + public long length() { + return length; + } + + public void close() { + blocks.clear(); + length = 0; + } + + private void grow(long pos) { + int blocksNeeded = (int)(pos >> BLOCK_SHIFT) - blocks.size() + 1; + for (int i=0; i < blocksNeeded; i++) { + blocks.add(new byte[BLOCK_SIZE]); + } + + length = pos + 1; + } + + public void putData(int oneByte, long pos) { + if (pos >= length) { + grow(pos); + } + + byte[] block = blocks.get((int)(pos >> BLOCK_SHIFT)); + block[(int)(pos & BLOCK_MASK)] = (byte) oneByte; + } + + public void putData(byte[] buffer, int offset, int count, long pos) { + if (count > buffer.length - offset || count < 0 || offset < 0) { + throw new IndexOutOfBoundsException(); + } + if (count == 0){ + return; + } + + long lastPos = pos + count - 1; + if (lastPos >= length) { + grow(lastPos); + } + + while (count > 0) { + byte[] block = blocks.get((int)(pos >> BLOCK_SHIFT)); + int blockOffset = (int)(pos & BLOCK_MASK); + int toCopy = Math.min(BLOCK_SIZE - blockOffset, count); + System.arraycopy(buffer, offset, block, blockOffset, toCopy); + pos += toCopy; + count -= toCopy; + offset += toCopy; + } + } + + public int getData(long pos) { + if (pos >= length) { + return -1; + } + + byte[] block = blocks.get((int)(pos >> BLOCK_SHIFT)); + return block[(int)(pos & BLOCK_MASK)] & 0xFF; + } + + public int getData(byte[] buffer, int offset, int count, long pos) { + if (count > buffer.length - offset || count < 0 || offset < 0) { + throw new IndexOutOfBoundsException(); + } + if (count == 0) { + return 0; + } + if (pos >= length) { + return -1; + } + + if (count + pos > length) { + count = (int) (length - pos); + } + + byte[] block = blocks.get((int)(pos >> BLOCK_SHIFT)); + int nbytes = Math.min(count, BLOCK_SIZE - (int)(pos & BLOCK_MASK)); + System.arraycopy(block, (int)(pos & BLOCK_MASK), buffer, offset, nbytes); + + return nbytes; + } + /* + public void seek(long pos) throws IOException { + if (pos < 0) { + throw new IOException("seek position is negative"); + } + this.pos = pos; + } + + public void readFully(byte[] buffer) throws IOException { + readFully(buffer, 0, buffer.length); + } + + public void readFully(byte[] buffer, int offset, int count) throws IOException { + if (0 <= offset && offset <= buffer.length && 0 <= count && count <= buffer.length - offset) { + while (count > 0) { + int result = read(buffer, offset, count); + if (result >= 0) { + offset += result; + count -= result; + } else { + throw new EOFException(); + } + } + } else { + throw new IndexOutOfBoundsException(); + } + } + + public long getFilePointer() { + return pos; + } +*/ + + public void freeBefore(long pos) { + int blockIdx = (int)(pos >> BLOCK_SHIFT); + if (blockIdx <= firstUndisposed) { // Nothing to do + return; + } + + for (int i = firstUndisposed; i < blockIdx; i++) { + blocks.set(i, null); + } + + firstUndisposed = blockIdx; + } + + public int appendData(InputStream is, int count) throws IOException { + if (count <= 0) { + return 0; + } + + long startPos = length; + long lastPos = length + count - 1; + grow(lastPos); // Changes length + + int blockIdx = (int)(startPos >> BLOCK_SHIFT); + int offset = (int) (startPos & BLOCK_MASK); + + int bytesAppended = 0; + + while (count > 0) { + byte[] block = blocks.get(blockIdx); + int toCopy = Math.min(BLOCK_SIZE - offset, count); + count -= toCopy; + + while (toCopy > 0) { + int bytesRead = is.read(block, offset, toCopy); + + if (bytesRead < 0) { + length -= (count - bytesAppended); + return bytesAppended; + } + + toCopy -= bytesRead; + offset += bytesRead; + } + + blockIdx++; + offset = 0; + } + + return count; + } + + public void getData(OutputStream os, int count, long pos) throws IOException { + if (pos + count > length) { + throw new IndexOutOfBoundsException("Argument out of cache"); + } + + int blockIdx = (int)(pos >> BLOCK_SHIFT); + int offset = (int) (pos & BLOCK_MASK); + if (blockIdx < firstUndisposed) { + throw new IndexOutOfBoundsException("The requested data are already disposed"); + } + + while (count > 0) { + byte[] block = blocks.get(blockIdx); + int toWrite = Math.min(BLOCK_SIZE - offset, count); + os.write(block, offset, toWrite); + + blockIdx++; + offset = 0; + count -= toWrite; + } + } +} diff --git a/awt/resources/org/apache/harmony/awt/internal/nls/messages.properties b/awt/resources/org/apache/harmony/awt/internal/nls/messages.properties new file mode 100644 index 000000000..9f647e9be --- /dev/null +++ b/awt/resources/org/apache/harmony/awt/internal/nls/messages.properties @@ -0,0 +1,495 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +# messages for EN locale +awt.00=FontRenderContext is null +awt.01='{0}' parameter is null +awt.02='{0}' parameter has zero length +awt.03='{0}' iterator parameter is null +awt.04='{0}' iterator parameter has zero length +awt.05=Operation cannot be null +awt.06=Unexpected type of the internal data buffer +awt.07=Transfer data is not available +awt.08=xfld parse string error: {0} +awt.09=min range bound value is greater than max range bound +awt.0A=Cannot use SinglePixedPackedSampleModel for bpp = {0} +awt.0B=Wrong color model created for drawable +awt.0C=Unknown visual class +awt.0D=Invalid transparency +awt.0E=Dimensions of the image should be positive +awt.0F=Cannot open display '{0}' +awt.10=Only 32-bit format is supported for window state operations. +awt.11=Invalid key code +awt.12=XTest is not supported by your X server\! +awt.13=Cannot allocate color named '{0}' +awt.14=Transfer data is not available +awt.15=Can not get monitor info +awt.16=Can not create DC for device +awt.17=Unknown Composite type : {0} +awt.18=Transparency is not supported +awt.19=Illegal size of volatile image +awt.1A=Failed to register window class {0} GetLastError returned {1} +awt.1B=Invalid key code +awt.1C=Failure to create JavaWindow GetLastError returned {0} +awt.1D=Cannot get data from OLE clipboard +awt.1E=Attempt to replace WindowProc handler +awt.1F=Waiting for resource access thread interrupted not from unlock method +awt.20=Can't unlock not locked resource +awt.21=Not owner can't unlock resource +awt.22=Not owner can't free resource +awt.23=One thread can't store state several times in a row +awt.24=Owner can't overwrite resource state. Lock operations may be lost +awt.25=No state stored for current thread +awt.26=Shutdown thread was interrupted while starting +awt.27=Shutdown thread was interrupted while stopping +awt.28=bad index: {0} +awt.29=Invalid range +awt.2A=Position not represented by view +awt.2B=No word at {0} +awt.2C=Invalid position: {0} +awt.2D=Invalid direction +awt.2E={0} not in range {1},{2} +awt.2F=No more words +awt.30=wrong number of elements to copy: {0}, size: {1} +awt.31=no room to copy: {0}, size: {1} +awt.32=String: '{0}' does not fit +awt.33=index is out of range +awt.34=Initial offset in the destination array is wrong: {0} +awt.35=Wrong number of elements to copy: {0} +awt.36=Wrong segment +awt.37=Unknown composite type {0} +awt.38=Property name is not defined +awt.39=This method is not implemented for image obtained from ImageProducer +awt.3A=Color Model is null +awt.3B=Incorrect ImageConsumer completion status +awt.3C=Unknown PNG color type +awt.3D=Unknown colorspace +awt.3E=Clone not supported +awt.3F=Invalid baseline index +awt.40=Wrong number of metrics\! +awt.41=Font returned unsupported type of line metrics. This case is known, but not supported yet. +awt.42=TextHitInfo out of range +awt.43=glyphIndex is out of vector's limits +awt.44=beginGlyphIndex is out of vector's range +awt.45=numEntries is out of vector's range +awt.46=length of setPositions array differs from the length of positions array +awt.47=First argument should be byte or short array +awt.48=The srcIn raster is incompatible with src ColorModel +awt.49=The dstIn raster is incompatible with dst ColorModel +awt.4A=The dstOut raster is incompatible with dst ColorModel +awt.4B=Iterator out of bounds +awt.4C=Invalid MultiRectArea in method {0} +awt.4D=The raster is incompatible with this ColorModel +awt.4E=Unknown native platform. +awt.4F=Data is not available +awt.50=Iterator is read-only +awt.51=Component expected to be a parent +awt.52=Time interval can't be <= 0 +awt.53=Handler can't be null +awt.54=Key event for unfocused component +awt.55=Double mouse enter event for component +awt.56=Double mouse exit event for component +awt.57=Double focus gained event for component +awt.58=Double focus lost event for component +awt.59=Application has run out of context thread group +awt.5A=Default class for PrinterJob is not found +awt.5B=No access to default class for PrinterJob +awt.5C=Instantiation exception for PrinterJob +awt.5D={0} is not supported +awt.5E=pageIndex is more than book size +awt.5F=wrong orientation +awt.60=Width and Height mustn't be equal zero both +awt.61=Unsupported data type: {0} +awt.62=Wrong mask : {0} +awt.63=Coordinates are not in bounds +awt.64=The number of the bands in the subset is greater than the number of bands in the sample model +awt.65=null argument +awt.66=Invalid format +awt.67=subclass is not derived from AWTKeyStroke +awt.68=subclass could not be instantiated +awt.69=columns less than zero. +awt.6A=rows less than zero. +awt.6B=Queue stack is empty +awt.6C=Event queue stack is broken +awt.6D=Point is null +awt.6E=Color is null +awt.6F=Index less than zero +awt.70=MenuItem is null +awt.71=Parent is null +awt.72=Key event for unfocused component +awt.73=no such item +awt.74=Input parameters a and b should not be null +awt.75=rows and cols cannot both be zero +awt.76=rows and cols cannot be negative +awt.77=default focus traversal policy cannot be null +awt.78=invalid focus traversal key identifier +awt.79=cannot set null focus traversal key +awt.7A=focus traversal keys cannot map to KEY_TYPED events +awt.7B=focus traversal keys must be unique for a Component +awt.7C=this KeyboardFocusManager is not installed in the current thread's context +awt.7D=Property name is null +awt.7E=invalid hotSpot +awt.7F=AddLayoutComponent: attempt to add null component +awt.80=AddLayoutComponent: constraint object must be GridBagConstraints +awt.81=AddLayoutComponent: {0} +awt.82=RemoveLayoutComponent: attempt to remove null component +awt.83=SetConstraints: attempt to get constraints of null component +awt.84=SetConstraints: attempt to set null constraints +awt.85=SetConstraints: {0} +awt.86=MinimumLayoutSize: {0} +awt.87=PreferredLayoutSize: {0} +awt.88=LayoutContainer: {0} +awt.89=LookupConstraints: attempt to get constraints of null component +awt.8A=AdjustForGravity: attempt to use null constraints +awt.8B=AdjustForGravity: attempt to use null rectangle +awt.8C=AdjustForGravity: {0} +awt.8D=REMINDER component expected after RELATIVE one +awt.8E=component is out of grid's range +awt.8F=Weights' overrides array is too long +awt.90=Lengths' overrides array is too long +awt.91=Unsupported constraints object: {0} +awt.92=Constraints object must be String +awt.93=cannot get component: invalid constraint: {0} +awt.94=transform can not be null +awt.95=Wrong start index: {0} +awt.96=Wrong finish index: {0} +awt.97=Wrong range length: {0} +awt.98=Wrong count value, can not be negative: {0} +awt.99=Wrong [start + count] is out of range: {0} +awt.9A=Unsupported font format +awt.9B=Can't create font - bad font data +awt.9C=wrong value of GridBagConstraints: {0} +awt.9D=relative grid size parameter goes after absolute grid coordinate +awt.9E=wrong values sum of GridBagConstraints' gridwidth and gridx +awt.9F=wrong values sum of GridBagConstraints' gridheight and gridy +awt.100=component has RELATIVE width and height +awt.101=position less than zero. +awt.102=columns less than zero. +awt.103=item is null +awt.104=item doesn't exist in the choice menu +awt.105=index less than zero +awt.106=specified position is greater than the number of items +awt.107=Color parameter outside of expected range: component {0} +awt.108=Alpha value outside of expected range +awt.109=Color parameter outside of expected range +awt.10A=Priority must be a value between 0 and 1, inclusive +awt.10B=aContainer and aComponent cannot be null +awt.10C=aContainer is not a focus cycle root of aComponent +awt.10D=aContainer should be focus cycle root or focus traversal policy provider +awt.10E=focusCycleRoot cannot be null +awt.10F=improper alignment: {0} +awt.110=Iterator out of bounds +awt.111=Parameter npoints is greater than array length +awt.112=Negative number of points +awt.113=illegal scrollbar orientation +awt.114=Image is null +awt.115=Anchor is null +awt.116=Invalid value for media +awt.117=Invalid value for orientationRequested +awt.118=Invalid value for printerResolution +awt.119=Invalid value for origin +awt.11A=Invalid value for printQuality +awt.11B=Invalid value for printerResolution[] +awt.11C=Invalid value for color +awt.11D=Unknown rule +awt.11E=Wrong alpha value +awt.11F=parent is not a component +awt.120=origin is not a descendant of parent +awt.121=parent must be showing on the screen +awt.122=Does not support display mode changes +awt.123=Unsupported display mode: {0} +awt.124=Cannot change the modality while the dialog is visible +awt.125=null owner window +awt.126=Window is showing +awt.127=Cannot change the decorations while the window is visible +awt.128=Graphics environment is headless +awt.129=Not a screen device +awt.12A=illegal component position +awt.12B=adding container to itself +awt.12C=adding container's parent to itself +awt.12D=adding a window to a container +awt.12E=Unknown component event id +awt.12F=Attempt to start nested mouse grab +awt.130=Attempt to grab mouse in not displayable window +awt.131=AddLayoutComponent: constraint object must be String +awt.132=wrong parent for CardLayout +awt.133=Negative width +awt.134=Illegal cap +awt.135=Illegal join +awt.136=miterLimit less than 1.0f +awt.137=Negative dashPhase +awt.138=Zero dash length +awt.139=Negative dash[{0}] +awt.13A=All dash lengths zero +awt.13B=offset off is out of range +awt.13C=number of elemets len is out of range +awt.13D=Rectangle width and height must be > 0 +awt.13E=Cannot call method from the event dispatcher thread +awt.13F=Delay must be to 0 to 60,000ms +awt.140=Invalid combination of button flags +awt.141=failed to parse hotspot property for cursor: +awt.142=Exception: class {0} {1} occurred while loading: {2} +awt.143=illegal cursor type +awt.144=Can be set by scrollpane only +awt.145=illegal file dialog mode +awt.146=illegal scrollbar display policy +awt.147=position greater than 0 +awt.148=child is null +awt.149=ScrollPane controls layout +awt.14A=Can not create VolatileImage with specified capabilities +awt.14B=Only Canvas or Window is allowed +awt.14C=Number of buffers must be greater than one +awt.14D=Buffer capabilities should support flipping +awt.14E=Component should be displayable +awt.14F=invalid focus traversal key identifier +awt.150=no parent +awt.151=component must be showing on the screen to determine its location +awt.152=Invalid number of copies +awt.153=Invalid value for maxPage +awt.154=Invalid value for minPage +awt.155=Invalid value for fromPage +awt.156=Invalid value for toPage +awt.157=Invalid value for pageRanges +awt.158=Invalid value for destination +awt.159=Invalid value for dialog +awt.15A=Invalid value for defaultSelection +awt.15B=Invalid value for multipleDocumentHandling +awt.15C=Invalid value for attribute sides +awt.15D=Invalid colorspace +awt.15E=Unknown component. Must be REDCOMPONENT, GREENCOMPONENT or BLUECOMPONENT. +awt.15F=Profile class does not comply with ICC specification +awt.160=Color space doesn't comply with ICC specification +awt.161=Unable to open file {0} +awt.162=Invalid ICC Profile Data +awt.163=Can't open color profile +awt.164=Not a predefined color space +awt.165=Color space doesn't comply with ICC specification +awt.166=TRC is not a simple gamma value +awt.167=TRC is a gamma value, not a table +awt.168=Invalid profile class +awt.169=Component index out of range +awt.16A=Invalid component index: {0} +awt.16B=Not a predefined colorspace +awt.16C=Can't load class: {0} +awt.16D=Can't parse MIME type: {0} +awt.16E=Transferable has null data +awt.16F=Can't create reader for this representation class +awt.170=Can't create default D&D cursor: {0} +awt.171=Attempt to start a drag while an existing drag operation is still executing +awt.172=Drag source is null +awt.173=One listener is already exist +awt.174=dgl is not current listener +awt.175=Listener mismatch +awt.176=DropTarget cannot be added as listener to itself +awt.177=Invalid user action +awt.178=Invalid source action +awt.179=Context peer is null +awt.17A=Trigger event is null +awt.17B=Can't init ACTION_NONE drag +awt.17C=Image offset is null +awt.17D=Transferable is null +awt.17E=Component associated with the trigger event is null +awt.17F=DragSource for the trigger event is null +awt.180=Source actions for the DragGestureRecognizer associated with the trigger event are equal to DnDConstants.ACTION_NONE +awt.181=Attempt to register context as its listener +awt.182=dsl is not current listener +awt.183=Invalid status +awt.184=Invalid action +awt.185=Component is null +awt.186=DragSource is null +awt.187=Origin is null +awt.188=Event list is null +awt.189=Event list is empty +awt.18A=Context is null +awt.18B=Invalid button value +awt.18C=Cannot invoke null runnable +awt.18D=Source is null +awt.18E=Wrong event id +awt.18F=Text must be null for CARET_POSITION_CHANGED +awt.190=Wrong committedCharacterCount +awt.191=Invalid keyCode for KEY_TYPED event, must be VK_UNDEFINED +awt.192=Invalid keyChar for KEY_TYPED event, can't be CHAR_UNDEFINED +awt.193=Listener can't be zero +awt.194=Unknown attribute name +awt.195=Offset is out of bounds +awt.196=Justification impossible, layout already justified +awt.197=Endpoints are out of range +awt.198=Illegal alignment argument +awt.199=Illegal range argument value: {0} +awt.19A=start or count arguments are out of text range +awt.19B=count argument must be positive +awt.19C=weight must be a positive number +awt.19D=growLeftLimit must be a positive number +awt.19E=growRightLimit must be a positive number +awt.19F=incorrect value for shrinkPriority, more than PRIORITY_NONE or less than PRIORITY_KASHIDA value +awt.200=incorrect value for growPriority, more than PRIORITY_NONE or less than PRIORITY_KASHIDA value +awt.201=shrinkLeftLimit must be a positive number +awt.202=shrinkRightLimit must be a positive number +awt.203=Offset limit should be greater than current position +awt.204=Determinant is zero +awt.205=Invalid type of Arc: {0} +awt.206=Flatness is less then zero +awt.207=Limit is less then zero +awt.208=Path is null +awt.209=Invalid winding rule value +awt.20A=First segment should be SEG_MOVETO type +awt.20B=unknown input method highlight state +awt.20C=Number of Bits equals to zero +awt.20D=The number of bits per pixel is not a power of 2 or pixels span data element boundaries +awt.20E=Data Bit offset is not a multiple of pixel bit stride +awt.20F=Number of bands must be only 1 +awt.210=The component value for this ColorModel is signed +awt.211=Pixel values for this ColorModel are not conveniently representable as a single int +awt.212=There is more than one component in this ColorModel +awt.213=This ComponentColorModel does not support the unnormalized form +awt.214=This Color Model doesn't support this transferType +awt.215=transferType is not one of DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, or DataBuffer.TYPE_DOUBLE +awt.216=The components array is not large enough to hold all the color and alpha components +awt.217=The transfer type of this ComponentColorModel is not one of the following transfer types: DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT +awt.218=The components array is not large enough to hold all the color and alpha components +awt.219=This transferType is not supported by this color model +awt.21A=This ComponentColorModel does not support this transferType +awt.21B=The length of normComponents minus normOffset is less than numComponents +awt.21C=The number of scale factors should not be zero +awt.21D=Number of src bands ({0}) does not match number of dst bands ({1}) +awt.21E=Number of scaling constants is not equal to the number of bands +awt.21F=Unable to transform source +awt.220=Source should not have IndexColorModel +awt.221=The imageType is TYPE_BYTE_BINARY and the color map has more than 16 entries +awt.222=The imageType is not TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED +awt.223=The imageType is not compatible with ColorModel +awt.224=Unknown image type +awt.225=Property name is null +awt.226=Both tileX and tileY are not equal to 0 +awt.227=This image type can't have alpha +awt.228=minX or minY of this raster not equal to zero +awt.229=Number of components in the LUT does not match the number of bands +awt.22A=Wrong type of pixels array +awt.22B=Length of data should not be less than width*height +awt.22C=Unknown data type {0} +awt.22D=This transferType ( {0} ) is not supported by this color model +awt.22E=w or h is less than or equal to zero +awt.22F=The product of w and h is greater than Integer.MAX_VALUE +awt.230=dataType is not one of the supported data types +awt.231=Number of bands must be more then 0 +awt.232=Offset should be not less than zero +awt.233=Number of components should be positive +awt.234=Width or Height equals zero +awt.235=Wrong Data Buffer type : {0} +awt.236=The bits is less than 1 or greater than 32 +awt.237=Source and destinations rasters do not have the same number of bands +awt.238=The number of arrays in the LookupTable does not meet the restrictions +awt.239=The space is not a TYPE_RGB space +awt.23A=The min/max normalized component values are not 0.0/1.0 +awt.23B=The mask of the {0} component is not contiguous +awt.23C=The mask of the alpha component is not contiguous +awt.23D=The mask of the red component is not contiguous +awt.23E=The mask of the green component is not contiguous +awt.23F=The mask of the blue component is not contiguous +awt.240=The transferType not is one of DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT or DataBuffer.TYPE_INT +awt.241=Any offset between bands is greater than the Scanline stride +awt.242=Pixel stride is less than any offset between bands +awt.243=Product of Pixel stride and w is greater than Scanline stride +awt.244=Width or Height of child Raster is less than or equal to zero +awt.245=parentX disposes outside Raster +awt.246=parentY disposes outside Raster +awt.247=parentX + w results in integer overflow +awt.248=parentY + h results in integer overflow +awt.249=childMinX + w results in integer overflow +awt.24A=childMinY + h results in integer overflow +awt.24B=Pixel stride must be >= 0 +awt.24C=Scanline stride must be >= 0 +awt.24D=Bank Indices length must be equal Bank Offsets length +awt.24E=Index of {0} bank must be >= 0 +awt.24F=Unable to invert transform {0} +awt.250=Unknown interpolation type: {0} +awt.251=Transformed width ({0}) and height ({1}) should be greater than 0 +awt.252=Source can't be same as the destination +awt.253=Different number of bands in source and destination +awt.254=Number of bands in the source raster ({0}) is incompatible with the matrix [{1}x{2}] +awt.255=Number of bands in the destination raster ({0}) is incompatible with the matrix [{1}x{2}] +awt.256=Source raster is null +awt.257=Source raster is equal to destination +awt.258=Number of source bands ({0}) is not equal to number of destination bands ({1}) +awt.259=Source image is null +awt.25A=Source equals to destination +awt.25B=Null ColorSpace passed as a parameter +awt.25C=Null profiles passed as a parameter +awt.25D=Source or destination color space is not defined +awt.25E=Incorrect number of source raster bands. Should be equal to the number of color components of source colorspace. +awt.25F=Incorrect number of destination raster bands. Should be equal to the number of color components of destination colorspace. +awt.260=Incompatible rasters - width or height differs +awt.261=Destination color space is undefined +awt.262=Destionation color space should be defined +awt.263=Incompatible images - width or height differs +awt.264=Size of the color map is less than 1 +awt.265=The raster argument is not compatible with this IndexColorModel +awt.266=The number of bits in a pixel is greater than 16 +awt.267=The transferType is invalid +awt.268=The pixel is not a primitive array of type transferType +awt.269=The transferType is not one of DataBuffer.TYPE_BYTE or DataBuffer.TYPE_USHORT +awt.26A=Incorrect ImageConsumer completion status +awt.26B=The number of bits in the pixel values is less than 1 +awt.26C=bits is null +awt.26D=The elements in bits is less than 0 +awt.26E=The sum of the number of bits in bits is less than 1 +awt.26F=The cspace is null +awt.270=The transparency is not a valid value +awt.271=The number of bits in bits is less than 1 +awt.272=The length of components minus offset is less than numComponents +awt.273=The length of normComponents minus normOffset is less than numComponents +awt.274=componentIdx is greater than the number of components or less than zero +awt.275=This pixel representation is not suuported by tis Color Model +awt.276=location.x + w or location.y + h results in integer overflow +awt.277=bankIndices or bandOffsets is null +awt.278=dataBuffer is null +awt.279=bands is less than 1 +awt.27A=dataBuffer has more than one bank +awt.27B=bandOffsets is null +awt.27C=bandMasks is null +awt.27D=bitsPerBand or bands is not greater than zero +awt.27E=The product of bitsPerBand and bands is greater than the number of bits held by dataType +awt.27F=SampleModel or DataBuffer is null +awt.280=SampleModel is null +awt.281=sampleModel, dataBuffer, aRegion or sampleModelTranslate is null +awt.282=aRegion has width or height less than or equal to zero +awt.283=Overflow X coordinate of Raster +awt.284=Overflow Y coordinate of Raster +awt.285=Width or Height of child Raster is less than or equal to zero +awt.286=parentX disposes outside Raster +awt.287=parentY disposes outside Raster +awt.288=parentX + width results in integer overflow +awt.289=parentY + height results in integer overflow +awt.28A=childMinX + width results in integer overflow +awt.28B=childMinY + height results in integer overflow +awt.28C=Rect is null +awt.28D=Length of dataArray[{0}] is less than size + offset[{1}] +awt.28E=Length of dataArray is less than size + offset +awt.28F=Source and destination rasters do not have the same width! +awt.290=Source and destination rasters do not have the same height! +awt.291=Source and destination images do not have the same width! +awt.292=Source and destination images do not have the same height! +awt.294=pixel is null +awt.295=data is null +awt.296=can't allocate memory on video card to create new display list +awt.297=Invalid keyLocation +awt.298=dataBuffer is too small + +awt.err.00=file dialog {0} error! +awt.err.01=error: {0} +awt.err.02=GDIPlus DrawDriverString error status = {0} +awt.err.03=gdipDrawCompositeGlyphVector: GDIPlus DrawDriverString error status = {0} +awt.err.04=gdipDrawCompositeGlyphVector: GDIPlus DrawDriverString error status = {0} diff --git a/awt/resources/org/apache/harmony/beans/internals/nls/messages.properties b/awt/resources/org/apache/harmony/beans/internals/nls/messages.properties new file mode 100644 index 000000000..72b1c8cad --- /dev/null +++ b/awt/resources/org/apache/harmony/beans/internals/nls/messages.properties @@ -0,0 +1,103 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +# messages for EN locale +beans.00=no getter for {0} property +beans.01=no property for name {0} is found +beans.02=in DefaultPersistenceDelegate.mutatesTo() {0} : {1} +beans.03=Target Bean class is null +beans.04=bad property name +beans.05=Modifier for setter method should be public. +beans.06=Number of parameters in setter method is not equal to 1. +beans.07=Parameter type in setter method does not corresponds to predefined. +beans.08=Number of parameters in getter method is not equal to 0. +beans.09=Parameter type in getter method does not corresponds to predefined. +beans.0A=Modifier for getter method should be public. +beans.0B=Exception in command execution +beans.0C=source is null +beans.0D=Error in expression: {0} +beans.0E=Changes are null +beans.0F=The new BeanContext can not be set +beans.10=no node is found for statement with target = {0} +beans.11=no getter for property {0} found +beans.12=cannot access property {0} getter +beans.13=no setter for property {0} found +beans.14=Exception while finding property descriptor +beans.15=The listener is null +beans.16=The provider is null +beans.17=The child is null +beans.18=The requestor is null +beans.19=The service class is null +beans.1A=The service selector is null +beans.1B=The service is null +beans.1C=The event is null +beans.1D=bean is null +beans.1E=Illegal class name: {0} +beans.1F=Method not found: get{0} +beans.20=Method not found: set{0} +beans.21=Modifier for indexed getter method should be public. +beans.22=Number of parameters in getter method is not equal to 1. +beans.23=Parameter in indexed getter method is not of integer type. +beans.24=Parameter type in indexed getter method does not correspond to predefined. +beans.25=Modifier for indexed setter method should be public. +beans.26=Number of parameters in indexed setter method is not equal to 2. +beans.27=First parameter type in indexed setter method should be int. +beans.28=Second parameter type in indexed setter method does not corresponds to predefined. +beans.29=Membership listener is null +beans.2A=Target child can not be null +beans.2B=Resource name can not be null +beans.2C=The child can not be null +beans.2D=Invalid resource +beans.2E=PropertyVetoException was thrown while removing a child: {0}; Original error message:{1} +beans.2F=Target child is null +beans.30=PropertyVetoException was thrown while adding a child: {0}; Original error message:{1} +beans.31=No valid method {0} for {1} found. +beans.32=Cannot acquire event type from {0} listener. +beans.33={0} does not return +beans.34={0} should have a single input parameter +beans.35=Single parameter does not match to {0} class +beans.36=No input params are allowed for getListenerMethod +beans.37=Return type of getListenerMethod is not an array of listeners +beans.38=Add and remove methods are not available +beans.39=Cannot generate event set descriptor for name {0}. +beans.3A=Event type with name {0} is not found. +beans.3B=skipping expression {0}... +beans.3C=Unknown method name for array +beans.3D=First parameter in array getter(setter) is not of Integer type +beans.3E=Illegal number of arguments in array getter +beans.3F=Illegal number of arguments in array setter +beans.40=No constructor for class {0} found +beans.41=No method with name {0} is found +beans.42=target is not generated: classname {0} is not found +beans.43=Cannot convert {0} to char +beans.44=for property {0} no getter(setter) is found +beans.45=method name is not generated: error in getMethodName() +beans.46=Not a valid child +beans.47=Unable to instantiate property editor +beans.48=Property editor is not assignable from the PropertyEditor interface +beans.49=Child cannot implement both BeanContextChild and BeanContextProxy +beans.4A=newInstance is null +beans.4B=type is null +beans.4C=encoder is null +beans.4D=Invalid method call +beans.4E=stopClass is not ancestor of beanClass +beans.4F=search path is null +beans.50=not an indexed property +beans.51=Listener method {0} should have parameter of type {1} +beans.52=listenerMethodName(s) is null +beans.53=eventSetName is null +beans.54=listenerType is null +beans.55=Method is null diff --git a/camera/libcameraservice/Android.mk b/camera/libcameraservice/Android.mk new file mode 100644 index 000000000..2dfe659a2 --- /dev/null +++ b/camera/libcameraservice/Android.mk @@ -0,0 +1,59 @@ +LOCAL_PATH:= $(call my-dir) + +# +# Set USE_CAMERA_STUB for non-emulator and non-simulator builds, if you want +# the camera service to use the fake camera. For emulator or simulator builds, +# we always use the fake camera. + +ifeq ($(USE_CAMERA_STUB),) +USE_CAMERA_STUB:=false +ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),) +USE_CAMERA_STUB:=true +endif #libcamerastub +endif + +ifeq ($(USE_CAMERA_STUB),true) +# +# libcamerastub +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + CameraHardwareStub.cpp \ + FakeCamera.cpp + +LOCAL_MODULE:= libcamerastub + +LOCAL_SHARED_LIBRARIES:= libui + +include $(BUILD_STATIC_LIBRARY) +endif # USE_CAMERA_STUB + +# +# libcameraservice +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + CameraService.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libui \ + libutils \ + libcutils + +LOCAL_MODULE:= libcameraservice + +LOCAL_CFLAGS+=-DLOG_TAG=\"CameraService\" + +ifeq ($(USE_CAMERA_STUB), true) +LOCAL_STATIC_LIBRARIES += libcamerastub +LOCAL_CFLAGS += -include CameraHardwareStub.h +else +LOCAL_SHARED_LIBRARIES += libcamera +endif + +include $(BUILD_SHARED_LIBRARY) + diff --git a/camera/libcameraservice/CameraHardwareStub.cpp b/camera/libcameraservice/CameraHardwareStub.cpp new file mode 100644 index 000000000..0f1ae8ea4 --- /dev/null +++ b/camera/libcameraservice/CameraHardwareStub.cpp @@ -0,0 +1,388 @@ +/* +** +** Copyright 2008, 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. +*/ + +#define LOG_TAG "CameraHardwareStub" +#include + +#include "CameraHardwareStub.h" +#include +#include +#include + +#include "CannedJpeg.h" + +namespace android { + +CameraHardwareStub::CameraHardwareStub() + : mParameters(), + mPreviewHeap(0), + mRawHeap(0), + mFakeCamera(0), + mPreviewFrameSize(0), + mRawPictureCallback(0), + mJpegPictureCallback(0), + mPictureCallbackCookie(0), + mPreviewCallback(0), + mPreviewCallbackCookie(0), + mAutoFocusCallback(0), + mAutoFocusCallbackCookie(0), + mCurrentPreviewFrame(0) +{ + initDefaultParameters(); +} + +void CameraHardwareStub::initDefaultParameters() +{ + CameraParameters p; + + p.setPreviewSize(176, 144); + p.setPreviewFrameRate(15); + p.setPreviewFormat("yuv422sp"); + + p.setPictureSize(kCannedJpegWidth, kCannedJpegHeight); + p.setPictureFormat("jpeg"); + + if (setParameters(p) != NO_ERROR) { + LOGE("Failed to set default parameters?!"); + } +} + +void CameraHardwareStub::initHeapLocked() +{ + // Create raw heap. + int picture_width, picture_height; + mParameters.getPictureSize(&picture_width, &picture_height); + mRawHeap = new MemoryHeapBase(picture_width * 2 * picture_height); + + int preview_width, preview_height; + mParameters.getPreviewSize(&preview_width, &preview_height); + LOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height); + + // Note that we enforce yuv422 in setParameters(). + int how_big = preview_width * preview_height * 2; + + // If we are being reinitialized to the same size as before, no + // work needs to be done. + if (how_big == mPreviewFrameSize) + return; + + mPreviewFrameSize = how_big; + + // Make a new mmap'ed heap that can be shared across processes. + // use code below to test with pmem + mPreviewHeap = new MemoryHeapBase(mPreviewFrameSize * kBufferCount); + // Make an IMemory for each frame so that we can reuse them in callbacks. + for (int i = 0; i < kBufferCount; i++) { + mBuffers[i] = new MemoryBase(mPreviewHeap, i * mPreviewFrameSize, mPreviewFrameSize); + } + + // Recreate the fake camera to reflect the current size. + delete mFakeCamera; + mFakeCamera = new FakeCamera(preview_width, preview_height); +} + +CameraHardwareStub::~CameraHardwareStub() +{ + delete mFakeCamera; + mFakeCamera = 0; // paranoia + singleton.clear(); +} + +sp CameraHardwareStub::getPreviewHeap() const +{ + return mPreviewHeap; +} + +sp CameraHardwareStub::getRawHeap() const +{ + return mRawHeap; +} + +// --------------------------------------------------------------------------- + +int CameraHardwareStub::previewThread() +{ + mLock.lock(); + // the attributes below can change under our feet... + + int previewFrameRate = mParameters.getPreviewFrameRate(); + + // Find the offset within the heap of the current buffer. + ssize_t offset = mCurrentPreviewFrame * mPreviewFrameSize; + + sp heap = mPreviewHeap; + + // this assumes the internal state of fake camera doesn't change + // (or is thread safe) + FakeCamera* fakeCamera = mFakeCamera; + + sp buffer = mBuffers[mCurrentPreviewFrame]; + + mLock.unlock(); + + // TODO: here check all the conditions that could go wrong + if (buffer != 0) { + // Calculate how long to wait between frames. + int delay = (int)(1000000.0f / float(previewFrameRate)); + + // This is always valid, even if the client died -- the memory + // is still mapped in our process. + void *base = heap->base(); + + // Fill the current frame with the fake camera. + uint8_t *frame = ((uint8_t *)base) + offset; + fakeCamera->getNextFrameAsYuv422(frame); + + //LOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame); + + // Notify the client of a new frame. + mPreviewCallback(buffer, mPreviewCallbackCookie); + + // Advance the buffer pointer. + mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount; + + // Wait for it... + usleep(delay); + } + + return NO_ERROR; +} + +status_t CameraHardwareStub::startPreview(preview_callback cb, void* user) +{ + Mutex::Autolock lock(mLock); + if (mPreviewThread != 0) { + // already running + return INVALID_OPERATION; + } + mPreviewCallback = cb; + mPreviewCallbackCookie = user; + mPreviewThread = new PreviewThread(this); + return NO_ERROR; +} + +void CameraHardwareStub::stopPreview() +{ + sp previewThread; + + { // scope for the lock + Mutex::Autolock lock(mLock); + previewThread = mPreviewThread; + } + + // don't hold the lock while waiting for the thread to quit + if (previewThread != 0) { + previewThread->requestExitAndWait(); + } + + Mutex::Autolock lock(mLock); + mPreviewThread.clear(); +} + +bool CameraHardwareStub::previewEnabled() { + return mPreviewThread != 0; +} + +status_t CameraHardwareStub::startRecording(recording_callback cb, void* user) +{ + return UNKNOWN_ERROR; +} + +void CameraHardwareStub::stopRecording() +{ +} + +bool CameraHardwareStub::recordingEnabled() +{ + return false; +} + +void CameraHardwareStub::releaseRecordingFrame(const sp& mem) +{ +} + +// --------------------------------------------------------------------------- + +int CameraHardwareStub::beginAutoFocusThread(void *cookie) +{ + CameraHardwareStub *c = (CameraHardwareStub *)cookie; + return c->autoFocusThread(); +} + +int CameraHardwareStub::autoFocusThread() +{ + if (mAutoFocusCallback != NULL) { + mAutoFocusCallback(true, mAutoFocusCallbackCookie); + mAutoFocusCallback = NULL; + return NO_ERROR; + } + return UNKNOWN_ERROR; +} + +status_t CameraHardwareStub::autoFocus(autofocus_callback af_cb, + void *user) +{ + Mutex::Autolock lock(mLock); + + if (mAutoFocusCallback != NULL) { + return mAutoFocusCallback == af_cb ? NO_ERROR : INVALID_OPERATION; + } + + mAutoFocusCallback = af_cb; + mAutoFocusCallbackCookie = user; + if (createThread(beginAutoFocusThread, this) == false) + return UNKNOWN_ERROR; + return NO_ERROR; +} + +/*static*/ int CameraHardwareStub::beginPictureThread(void *cookie) +{ + CameraHardwareStub *c = (CameraHardwareStub *)cookie; + return c->pictureThread(); +} + +int CameraHardwareStub::pictureThread() +{ + if (mShutterCallback) + mShutterCallback(mPictureCallbackCookie); + + if (mRawPictureCallback) { + //FIXME: use a canned YUV image! + // In the meantime just make another fake camera picture. + int w, h; + mParameters.getPictureSize(&w, &h); + sp mem = new MemoryBase(mRawHeap, 0, w * 2 * h); + FakeCamera cam(w, h); + cam.getNextFrameAsYuv422((uint8_t *)mRawHeap->base()); + if (mRawPictureCallback) + mRawPictureCallback(mem, mPictureCallbackCookie); + } + + if (mJpegPictureCallback) { + sp heap = new MemoryHeapBase(kCannedJpegSize); + sp mem = new MemoryBase(heap, 0, kCannedJpegSize); + memcpy(heap->base(), kCannedJpeg, kCannedJpegSize); + if (mJpegPictureCallback) + mJpegPictureCallback(mem, mPictureCallbackCookie); + } + return NO_ERROR; +} + +status_t CameraHardwareStub::takePicture(shutter_callback shutter_cb, + raw_callback raw_cb, + jpeg_callback jpeg_cb, + void* user) +{ + stopPreview(); + mShutterCallback = shutter_cb; + mRawPictureCallback = raw_cb; + mJpegPictureCallback = jpeg_cb; + mPictureCallbackCookie = user; + if (createThread(beginPictureThread, this) == false) + return -1; + return NO_ERROR; +} + +status_t CameraHardwareStub::cancelPicture(bool cancel_shutter, + bool cancel_raw, + bool cancel_jpeg) +{ + if (cancel_shutter) mShutterCallback = NULL; + if (cancel_raw) mRawPictureCallback = NULL; + if (cancel_jpeg) mJpegPictureCallback = NULL; + return NO_ERROR; +} + +status_t CameraHardwareStub::dump(int fd, const Vector& args) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + AutoMutex lock(&mLock); + if (mFakeCamera != 0) { + mFakeCamera->dump(fd, args); + mParameters.dump(fd, args); + snprintf(buffer, 255, " preview frame(%d), size (%d), running(%s)\n", mCurrentPreviewFrame, mPreviewFrameSize, mPreviewRunning?"true": "false"); + result.append(buffer); + } else { + result.append("No camera client yet.\n"); + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t CameraHardwareStub::setParameters(const CameraParameters& params) +{ + Mutex::Autolock lock(mLock); + // XXX verify params + + if (strcmp(params.getPreviewFormat(), "yuv422sp") != 0) { + LOGE("Only yuv422sp preview is supported"); + return -1; + } + + if (strcmp(params.getPictureFormat(), "jpeg") != 0) { + LOGE("Only jpeg still pictures are supported"); + return -1; + } + + int w, h; + params.getPictureSize(&w, &h); + if (w != kCannedJpegWidth && h != kCannedJpegHeight) { + LOGE("Still picture size must be size of canned JPEG (%dx%d)", + kCannedJpegWidth, kCannedJpegHeight); + return -1; + } + + mParameters = params; + + initHeapLocked(); + + return NO_ERROR; +} + +CameraParameters CameraHardwareStub::getParameters() const +{ + Mutex::Autolock lock(mLock); + return mParameters; +} + +void CameraHardwareStub::release() +{ +} + +wp CameraHardwareStub::singleton; + +sp CameraHardwareStub::createInstance() +{ + if (singleton != 0) { + sp hardware = singleton.promote(); + if (hardware != 0) { + return hardware; + } + } + sp hardware(new CameraHardwareStub()); + singleton = hardware; + return hardware; +} + +extern "C" sp openCameraHardware() +{ + return CameraHardwareStub::createInstance(); +} + +}; // namespace android diff --git a/camera/libcameraservice/CameraHardwareStub.h b/camera/libcameraservice/CameraHardwareStub.h new file mode 100644 index 000000000..0d26d47ec --- /dev/null +++ b/camera/libcameraservice/CameraHardwareStub.h @@ -0,0 +1,124 @@ +/* +** +** Copyright 2008, 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. +*/ + +#ifndef ANDROID_HARDWARE_CAMERA_HARDWARE_STUB_H +#define ANDROID_HARDWARE_CAMERA_HARDWARE_STUB_H + +#include "FakeCamera.h" +#include +#include +#include +#include +#include + +namespace android { + +class CameraHardwareStub : public CameraHardwareInterface { +public: + virtual sp getPreviewHeap() const; + virtual sp getRawHeap() const; + + virtual status_t startPreview(preview_callback cb, void* user); + virtual void stopPreview(); + virtual bool previewEnabled(); + + virtual status_t startRecording(recording_callback cb, void* user); + virtual void stopRecording(); + virtual bool recordingEnabled(); + virtual void releaseRecordingFrame(const sp& mem); + + virtual status_t autoFocus(autofocus_callback, void *user); + virtual status_t takePicture(shutter_callback, + raw_callback, + jpeg_callback, + void* user); + virtual status_t cancelPicture(bool cancel_shutter, + bool cancel_raw, + bool cancel_jpeg); + virtual status_t dump(int fd, const Vector& args) const; + virtual status_t setParameters(const CameraParameters& params); + virtual CameraParameters getParameters() const; + virtual void release(); + + static sp createInstance(); + +private: + CameraHardwareStub(); + virtual ~CameraHardwareStub(); + + static wp singleton; + + static const int kBufferCount = 4; + + class PreviewThread : public Thread { + CameraHardwareStub* mHardware; + public: + PreviewThread(CameraHardwareStub* hw) + : Thread(false), mHardware(hw) { } + virtual void onFirstRef() { + run("CameraPreviewThread", PRIORITY_URGENT_DISPLAY); + } + virtual bool threadLoop() { + mHardware->previewThread(); + // loop until we need to quit + return true; + } + }; + + void initDefaultParameters(); + void initHeapLocked(); + + int previewThread(); + + static int beginAutoFocusThread(void *cookie); + int autoFocusThread(); + + static int beginPictureThread(void *cookie); + int pictureThread(); + + mutable Mutex mLock; + + CameraParameters mParameters; + + sp mPreviewHeap; + sp mRawHeap; + sp mBuffers[kBufferCount]; + + FakeCamera *mFakeCamera; + bool mPreviewRunning; + int mPreviewFrameSize; + + shutter_callback mShutterCallback; + raw_callback mRawPictureCallback; + jpeg_callback mJpegPictureCallback; + void *mPictureCallbackCookie; + + // protected by mLock + sp mPreviewThread; + preview_callback mPreviewCallback; + void *mPreviewCallbackCookie; + + autofocus_callback mAutoFocusCallback; + void *mAutoFocusCallbackCookie; + + // only used from PreviewThread + int mCurrentPreviewFrame; +}; + +}; // namespace android + +#endif diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp new file mode 100644 index 000000000..15e3b21ae --- /dev/null +++ b/camera/libcameraservice/CameraService.cpp @@ -0,0 +1,1073 @@ +/* +** +** Copyright (C) 2008, The Android Open Source Project +** Copyright (C) 2008 HTC Inc. +** +** 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. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "CameraService" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "CameraService.h" + +namespace android { + +extern "C" { +#include +#include +#include +#include +#include +} + +// When you enable this, as well as DEBUG_REFS=1 and +// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp, this will track all +// references to the CameraService::Client in order to catch the case where the +// client is being destroyed while a callback from the CameraHardwareInterface +// is outstanding. This is a serious bug because if we make another call into +// CameraHardwreInterface that itself triggers a callback, we will deadlock. + +#define DEBUG_CLIENT_REFERENCES 0 + +#define PICTURE_TIMEOUT seconds(5) + +#define DEBUG_DUMP_PREVIEW_FRAME_TO_FILE 0 /* n-th frame to write */ +#define DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE 0 +#define DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE 0 + +#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE +static int debug_frame_cnt; +#endif + +// ---------------------------------------------------------------------------- + +void CameraService::instantiate() { + defaultServiceManager()->addService( + String16("media.camera"), new CameraService()); +} + +// ---------------------------------------------------------------------------- + +CameraService::CameraService() : + BnCameraService() +{ + LOGI("CameraService started: pid=%d", getpid()); +} + +CameraService::~CameraService() +{ + if (mClient != 0) { + LOGE("mClient was still connected in destructor!"); + } +} + +sp CameraService::connect(const sp& cameraClient) +{ + LOGD("Connect E from ICameraClient %p", cameraClient->asBinder().get()); + + Mutex::Autolock lock(mLock); + sp client; + if (mClient != 0) { + sp currentClient = mClient.promote(); + if (currentClient != 0) { + sp currentCameraClient(currentClient->getCameraClient()); + if (cameraClient->asBinder() == currentCameraClient->asBinder()) { + // this is the same client reconnecting... + LOGD("Connect X same client (%p) is reconnecting...", cameraClient->asBinder().get()); + return currentClient; + } else { + // it's another client... reject it + LOGD("new client (%p) attempting to connect - rejected", cameraClient->asBinder().get()); + return client; + } + } else { + // can't promote, the previous client has died... + LOGD("new client connecting, old reference was dangling..."); + mClient.clear(); + } + } + + // create a new Client object + client = new Client(this, cameraClient, IPCThreadState::self()->getCallingPid()); + mClient = client; +#if DEBUG_CLIENT_REFERENCES + // Enable tracking for this object, and track increments and decrements of + // the refcount. + client->trackMe(true, true); +#endif + LOGD("Connect X"); + return client; +} + +void CameraService::removeClient(const sp& cameraClient) +{ + // declar this outside the lock to make absolutely sure the + // destructor won't be called with the lock held. + sp client; + + Mutex::Autolock lock(mLock); + + if (mClient == 0) { + // This happens when we have already disconnected. + LOGV("mClient is null."); + return; + } + + // Promote mClient. It should never fail because we're called from + // a binder call, so someone has to have a strong reference. + client = mClient.promote(); + if (client == 0) { + LOGW("can't get a strong reference on mClient!"); + mClient.clear(); + return; + } + + if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) { + // ugh! that's not our client!! + LOGW("removeClient() called, but mClient doesn't match!"); + } else { + // okay, good, forget about mClient + mClient.clear(); + } +} + +CameraService::Client::Client(const sp& cameraService, + const sp& cameraClient, pid_t clientPid) +{ + LOGD("Client E constructor"); + mCameraService = cameraService; + mCameraClient = cameraClient; + mClientPid = clientPid; + mHardware = openCameraHardware(); + mUseOverlay = mHardware->useOverlay(); + + // Callback is disabled by default + mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; + LOGD("Client X constructor"); +} + +status_t CameraService::Client::checkPid() +{ + if (mClientPid == IPCThreadState::self()->getCallingPid()) return NO_ERROR; + LOGW("Attempt to use locked camera (%p) from different process", getCameraClient()->asBinder().get()); + return -EBUSY; +} + +status_t CameraService::Client::lock() +{ + Mutex::Autolock _l(mLock); + // lock camera to this client if the the camera is unlocked + if (mClientPid == 0) { + mClientPid = IPCThreadState::self()->getCallingPid(); + return NO_ERROR; + } + // returns NO_ERROR if the client already owns the camera, -EBUSY otherwise + return checkPid(); +} + +status_t CameraService::Client::unlock() +{ + Mutex::Autolock _l(mLock); + // allow anyone to use camera + LOGV("unlock (%p)", getCameraClient()->asBinder().get()); + status_t result = checkPid(); + if (result == NO_ERROR) mClientPid = 0; + return result; +} + +status_t CameraService::Client::connect(const sp& client) +{ + // connect a new process to the camera + LOGV("connect (%p)", client->asBinder().get()); + + // I hate this hack, but things get really ugly when the media recorder + // service is handing back the camera to the app. The ICameraClient + // destructor will be called during the same IPC, making it look like + // the remote client is trying to disconnect. This hack temporarily + // sets the mClientPid to an invalid pid to prevent the hardware from + // being torn down. + { + + // hold a reference to the old client or we will deadlock if the client is + // in the same process and we hold the lock when we remove the reference + sp oldClient; + { + Mutex::Autolock _l(mLock); + if (mClientPid != 0) { + LOGW("Tried to connect to locked camera"); + return -EBUSY; + } + oldClient = mCameraClient; + + // did the client actually change? + if (client->asBinder() == mCameraClient->asBinder()) return NO_ERROR; + + mCameraClient = client; + mClientPid = -1; + mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; + LOGV("connect new process (%d) to existing camera client", mClientPid); + } + + } + // the old client destructor is called when oldClient goes out of scope + // now we set the new PID to lock the interface again + mClientPid = IPCThreadState::self()->getCallingPid(); + + return NO_ERROR; +} + +#if HAVE_ANDROID_OS +static void *unregister_surface(void *arg) +{ + ISurface *surface = (ISurface *)arg; + surface->unregisterBuffers(); + IPCThreadState::self()->flushCommands(); + return NULL; +} +#endif + +CameraService::Client::~Client() +{ + // tear down client + LOGD("Client (%p) E destructor", getCameraClient()->asBinder().get()); + if (mSurface != 0 && !mUseOverlay) { +#if HAVE_ANDROID_OS + pthread_t thr; + // We unregister the buffers in a different thread because binder does + // not let us make sychronous transactions in a binder destructor (that + // is, upon our reaching a refcount of zero.) + pthread_create(&thr, NULL, + unregister_surface, + mSurface.get()); + pthread_join(thr, NULL); +#else + mSurface->unregisterBuffers(); +#endif + } + + // make sure we tear down the hardware + mClientPid = IPCThreadState::self()->getCallingPid(); + disconnect(); + LOGD("Client X destructor"); +} + +void CameraService::Client::disconnect() +{ + LOGD("Client (%p) E disconnect from (%d)", + getCameraClient()->asBinder().get(), + IPCThreadState::self()->getCallingPid()); + Mutex::Autolock lock(mLock); + if (mClientPid <= 0) { + LOGV("camera is unlocked, don't tear down hardware"); + return; + } + if (checkPid() != NO_ERROR) { + LOGV("Different client - don't disconnect"); + return; + } + + mCameraService->removeClient(mCameraClient); + if (mHardware != 0) { + LOGV("hardware teardown"); + // Before destroying mHardware, we must make sure it's in the + // idle state. + mHardware->stopPreview(); + // Cancel all picture callbacks. + mHardware->cancelPicture(true, true, true); + // Release the hardware resources. + mHardware->release(); + } + mHardware.clear(); + LOGD("Client X disconnect"); +} + +// pass the buffered ISurface to the camera service +status_t CameraService::Client::setPreviewDisplay(const sp& surface) +{ + LOGD("setPreviewDisplay(%p)", surface.get()); + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + Mutex::Autolock surfaceLock(mSurfaceLock); + // asBinder() is safe on NULL (returns NULL) + if (surface->asBinder() != mSurface->asBinder()) { + if (mSurface != 0 && !mUseOverlay) { + LOGD("clearing old preview surface %p", mSurface.get()); + mSurface->unregisterBuffers(); + } + mSurface = surface; + } + return NO_ERROR; +} + +// set the preview callback flag to affect how the received frames from +// preview are handled. +void CameraService::Client::setPreviewCallbackFlag(int callback_flag) +{ + LOGV("setPreviewCallbackFlag"); + Mutex::Autolock lock(mLock); + if (checkPid() != NO_ERROR) return; + mPreviewCallbackFlag = callback_flag; +} + +// start preview mode, must call setPreviewDisplay first +status_t CameraService::Client::startCameraMode(camera_mode mode) +{ + LOGD("startCameraMode(%d)", mode); + + /* we cannot call into mHardware with mLock held because + * mHardware has callbacks onto us which acquire this lock + */ + + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + if (mSurface == 0) { + LOGE("setPreviewDisplay must be called before startCameraMode!"); + return INVALID_OPERATION; + } + + switch(mode) { + case CAMERA_RECORDING_MODE: + return startRecordingMode(); + + default: // CAMERA_PREVIEW_MODE + return startPreviewMode(); + } +} + +status_t CameraService::Client::startRecordingMode() +{ + LOGV("startRecordingMode"); + + status_t ret = UNKNOWN_ERROR; + + // if preview has not been started, start preview first + if (!mHardware->previewEnabled()) { + ret = startPreviewMode(); + if (ret != NO_ERROR) { + return ret; + } + } + + // if recording has been enabled, nothing needs to be done + if (mHardware->recordingEnabled()) { + return NO_ERROR; + } + + // start recording mode + ret = mHardware->startRecording(recordingCallback, + mCameraService.get()); + if (ret != NO_ERROR) { + LOGE("mHardware->startRecording() failed with status %d", ret); + } + return ret; +} + +status_t CameraService::Client::startPreviewMode() +{ + LOGV("startPreviewMode"); + + // if preview has been enabled, nothing needs to be done + if (mHardware->previewEnabled()) { + return NO_ERROR; + } + + // start preview mode +#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE + debug_frame_cnt = 0; +#endif + status_t ret = UNKNOWN_ERROR; + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + if (mUseOverlay) { + const char *format = params.getPreviewFormat(); + int fmt; + LOGD("Use Overlays"); + if (!strcmp(format, "yuv422i")) + fmt = OVERLAY_FORMAT_YCbCr_422_I; + else if (!strcmp(format, "rgb565")) + fmt = OVERLAY_FORMAT_RGB_565; + else { + LOGE("Invalid preview format for overlays"); + return -EINVAL; + } + sp ref = mSurface->createOverlay(w, h, fmt); + ret = mHardware->setOverlay(new Overlay(ref)); + if (ret != NO_ERROR) { + LOGE("mHardware->setOverlay() failed with status %d\n", ret); + return ret; + } + ret = mHardware->startPreview(NULL, mCameraService.get()); + if (ret != NO_ERROR) + LOGE("mHardware->startPreview() failed with status %d\n", ret); + + } else { + ret = mHardware->startPreview(previewCallback, + mCameraService.get()); + if (ret == NO_ERROR) { + + mSurface->unregisterBuffers(); + + uint32_t transform = 0; + if (params.getOrientation() == + CameraParameters::CAMERA_ORIENTATION_PORTRAIT) { + LOGV("portrait mode"); + transform = ISurface::BufferHeap::ROT_90; + } + ISurface::BufferHeap buffers(w, h, w, h, + PIXEL_FORMAT_YCbCr_420_SP, + transform, + 0, + mHardware->getPreviewHeap()); + + mSurface->registerBuffers(buffers); + } else { + LOGE("mHardware->startPreview() failed with status %d", ret); + } + } + return ret; +} + +status_t CameraService::Client::startPreview() +{ + return startCameraMode(CAMERA_PREVIEW_MODE); +} + +status_t CameraService::Client::startRecording() +{ + return startCameraMode(CAMERA_RECORDING_MODE); +} + +// stop preview mode +void CameraService::Client::stopPreview() +{ + LOGD("stopPreview()"); + + Mutex::Autolock lock(mLock); + if (checkPid() != NO_ERROR) return; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return; + } + + mHardware->stopPreview(); + LOGD("stopPreview(), hardware stopped OK"); + + if (mSurface != 0 && !mUseOverlay) { + mSurface->unregisterBuffers(); + } + mPreviewBuffer.clear(); +} + +// stop recording mode +void CameraService::Client::stopRecording() +{ + LOGV("stopRecording()"); + + Mutex::Autolock lock(mLock); + if (checkPid() != NO_ERROR) return; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return; + } + + mHardware->stopRecording(); + LOGV("stopRecording(), hardware stopped OK"); + mPreviewBuffer.clear(); +} + +// release a recording frame +void CameraService::Client::releaseRecordingFrame(const sp& mem) +{ + LOGV("releaseRecordingFrame()"); + + Mutex::Autolock lock(mLock); + if (checkPid() != NO_ERROR) return; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return; + } + + mHardware->releaseRecordingFrame(mem); +} + +bool CameraService::Client::previewEnabled() +{ + Mutex::Autolock lock(mLock); + if (mHardware == 0) return false; + return mHardware->previewEnabled(); +} + +bool CameraService::Client::recordingEnabled() +{ + Mutex::Autolock lock(mLock); + if (mHardware == 0) return false; + return mHardware->recordingEnabled(); +} + +// Safely retrieves a strong pointer to the client during a hardware callback. +sp CameraService::Client::getClientFromCookie(void* user) +{ + sp client = 0; + CameraService *service = static_cast(user); + if (service != NULL) { + Mutex::Autolock ourLock(service->mLock); + if (service->mClient != 0) { + client = service->mClient.promote(); + if (client == 0) { + LOGE("getClientFromCookie: client appears to have died"); + service->mClient.clear(); + } + } else { + LOGE("getClientFromCookie: got callback but client was NULL"); + } + } + return client; +} + + +#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE || \ + DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE || \ + DEBUG_DUMP_PREVIEW_FRAME_TO_FILE +static void dump_to_file(const char *fname, + uint8_t *buf, uint32_t size) +{ + int nw, cnt = 0; + uint32_t written = 0; + + LOGD("opening file [%s]\n", fname); + int fd = open(fname, O_RDWR | O_CREAT); + if (fd < 0) { + LOGE("failed to create file [%s]: %s", fname, strerror(errno)); + return; + } + + LOGD("writing %d bytes to file [%s]\n", size, fname); + while (written < size) { + nw = ::write(fd, + buf + written, + size - written); + if (nw < 0) { + LOGE("failed to write to file [%s]: %s", + fname, strerror(errno)); + break; + } + written += nw; + cnt++; + } + LOGD("done writing %d bytes to file [%s] in %d passes\n", + size, fname, cnt); + ::close(fd); +} +#endif + +// preview callback - frame buffer update +void CameraService::Client::previewCallback(const sp& mem, void* user) +{ + LOGV("previewCallback()"); + sp client = getClientFromCookie(user); + if (client == 0) { + return; + } + +#if DEBUG_HEAP_LEAKS && 0 // debugging + if (gWeakHeap == NULL) { + ssize_t offset; + size_t size; + sp heap = mem->getMemory(&offset, &size); + if (gWeakHeap != heap) { + LOGD("SETTING PREVIEW HEAP"); + heap->trackMe(true, true); + gWeakHeap = heap; + } + } +#endif + +#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE + { + if (debug_frame_cnt++ == DEBUG_DUMP_PREVIEW_FRAME_TO_FILE) { + ssize_t offset; + size_t size; + sp heap = mem->getMemory(&offset, &size); + dump_to_file("/data/preview.yuv", + (uint8_t *)heap->base() + offset, size); + } + } +#endif + + // The strong pointer guarantees the client will exist, but no lock is held. + client->postPreviewFrame(mem); + +#if DEBUG_CLIENT_REFERENCES + //**** if the client's refcount is 1, then we are about to destroy it here, + // which is bad--print all refcounts. + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (PREVIEW) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +// recording callback +void CameraService::Client::recordingCallback(const sp& mem, void* user) +{ + LOGV("recordingCallback"); + sp client = getClientFromCookie(user); + if (client == 0) { + return; + } + // The strong pointer guarantees the client will exist, but no lock is held. + client->postRecordingFrame(mem); +} + +// take a picture - image is returned in callback +status_t CameraService::Client::autoFocus() +{ + LOGV("autoFocus"); + + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + return mHardware->autoFocus(autoFocusCallback, + mCameraService.get()); +} + +// take a picture - image is returned in callback +status_t CameraService::Client::takePicture() +{ + LOGD("takePicture"); + + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + return mHardware->takePicture(shutterCallback, + yuvPictureCallback, + jpegPictureCallback, + mCameraService.get()); +} + +// picture callback - snapshot taken +void CameraService::Client::shutterCallback(void *user) +{ + sp client = getClientFromCookie(user); + if (client == 0) { + return; + } + + // Screen goes black after the buffer is unregistered. + if (client->mSurface != 0 && !client->mUseOverlay) { + client->mSurface->unregisterBuffers(); + } + + client->postShutter(); + + // It takes some time before yuvPicture callback to be called. + // Register the buffer for raw image here to reduce latency. + if (client->mSurface != 0 && !client->mUseOverlay) { + int w, h; + CameraParameters params(client->mHardware->getParameters()); + params.getPictureSize(&w, &h); + uint32_t transform = 0; + if (params.getOrientation() == CameraParameters::CAMERA_ORIENTATION_PORTRAIT) { + LOGV("portrait mode"); + transform = ISurface::BufferHeap::ROT_90; + } + ISurface::BufferHeap buffers(w, h, w, h, + PIXEL_FORMAT_YCbCr_420_SP, transform, 0, client->mHardware->getRawHeap()); + + client->mSurface->registerBuffers(buffers); + } +} + +// picture callback - raw image ready +void CameraService::Client::yuvPictureCallback(const sp& mem, + void *user) +{ + sp client = getClientFromCookie(user); + if (client == 0) { + return; + } + if (mem == NULL) { + client->postRaw(NULL); + client->postError(UNKNOWN_ERROR); + return; + } + + ssize_t offset; + size_t size; + sp heap = mem->getMemory(&offset, &size); +#if DEBUG_HEAP_LEAKS && 0 // debugging + gWeakHeap = heap; // debugging +#endif + + //LOGV("yuvPictureCallback(%d, %d, %p)", offset, size, user); +#if DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE // for testing pursposes only + dump_to_file("/data/photo.yuv", + (uint8_t *)heap->base() + offset, size); +#endif + + // Put the YUV version of the snapshot in the preview display. + if (client->mSurface != 0 && !client->mUseOverlay) { + client->mSurface->postBuffer(offset); + } + + client->postRaw(mem); + +#if DEBUG_CLIENT_REFERENCES + //**** if the client's refcount is 1, then we are about to destroy it here, + // which is bad--print all refcounts. + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (RAW) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +// picture callback - jpeg ready +void CameraService::Client::jpegPictureCallback(const sp& mem, void *user) +{ + sp client = getClientFromCookie(user); + if (client == 0) { + return; + } + if (mem == NULL) { + client->postJpeg(NULL); + client->postError(UNKNOWN_ERROR); + return; + } + + /** We absolutely CANNOT call into user code with a lock held **/ + +#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE // for testing pursposes only + { + ssize_t offset; + size_t size; + sp heap = mem->getMemory(&offset, &size); + dump_to_file("/data/photo.jpg", + (uint8_t *)heap->base() + offset, size); + } +#endif + + client->postJpeg(mem); + +#if DEBUG_CLIENT_REFERENCES + //**** if the client's refcount is 1, then we are about to destroy it here, + // which is bad--print all refcounts. + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (JPEG) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +void CameraService::Client::autoFocusCallback(bool focused, void *user) +{ + LOGV("autoFocusCallback"); + + sp client = getClientFromCookie(user); + if (client == 0) { + return; + } + + client->postAutoFocus(focused); + +#if DEBUG_CLIENT_REFERENCES + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (AUTOFOCUS) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +// set preview/capture parameters - key/value pairs +status_t CameraService::Client::setParameters(const String8& params) +{ + LOGD("setParameters(%s)", params.string()); + + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + CameraParameters p(params); + mHardware->setParameters(p); + return NO_ERROR; +} + +// get preview/capture parameters - key/value pairs +String8 CameraService::Client::getParameters() const +{ + LOGD("getParameters"); + + Mutex::Autolock lock(mLock); + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return String8(); + } + + return mHardware->getParameters().flatten(); +} + +void CameraService::Client::postAutoFocus(bool focused) +{ + LOGV("postAutoFocus"); + mCameraClient->autoFocusCallback(focused); +} + +void CameraService::Client::postShutter() +{ + mCameraClient->shutterCallback(); +} + +void CameraService::Client::postRaw(const sp& mem) +{ + LOGD("postRaw"); + mCameraClient->rawCallback(mem); +} + +void CameraService::Client::postJpeg(const sp& mem) +{ + LOGD("postJpeg"); + mCameraClient->jpegCallback(mem); +} + +void CameraService::Client::copyFrameAndPostCopiedFrame(sp heap, size_t offset, size_t size) +{ + LOGV("copyFrameAndPostCopiedFrame"); + // It is necessary to copy out of pmem before sending this to + // the callback. For efficiency, reuse the same MemoryHeapBase + // provided it's big enough. Don't allocate the memory or + // perform the copy if there's no callback. + if (mPreviewBuffer == 0) { + mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); + } else if (size > mPreviewBuffer->virtualSize()) { + mPreviewBuffer.clear(); + mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); + if (mPreviewBuffer == 0) { + LOGE("failed to allocate space for preview buffer"); + return; + } + } + memcpy(mPreviewBuffer->base(), + (uint8_t *)heap->base() + offset, size); + + sp frame = new MemoryBase(mPreviewBuffer, 0, size); + if (frame == 0) { + LOGE("failed to allocate space for frame callback"); + return; + } + mCameraClient->previewCallback(frame); +} + +void CameraService::Client::postRecordingFrame(const sp& frame) +{ + LOGV("postRecordingFrame"); + if (frame == 0) { + LOGW("frame is a null pointer"); + return; + } + mCameraClient->recordingCallback(frame); +} + +void CameraService::Client::postPreviewFrame(const sp& mem) +{ + LOGV("postPreviewFrame"); + if (mem == 0) { + LOGW("mem is a null pointer"); + return; + } + + ssize_t offset; + size_t size; + sp heap = mem->getMemory(&offset, &size); + { + Mutex::Autolock surfaceLock(mSurfaceLock); + if (mSurface != NULL) { + mSurface->postBuffer(offset); + } + } + + // Is the callback enabled or not? + if (!(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)) { + // If the enable bit is off, the copy-out and one-shot bits are ignored + LOGV("frame callback is diabled"); + return; + } + + // Is the received frame copied out or not? + if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) { + LOGV("frame is copied out"); + copyFrameAndPostCopiedFrame(heap, offset, size); + } else { + LOGV("frame is directly sent out without copying"); + mCameraClient->previewCallback(mem); + } + + // Is this is one-shot only? + if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) { + LOGV("One-shot only, thus clear the bits and disable frame callback"); + mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK | + FRAME_CALLBACK_FLAG_COPY_OUT_MASK | + FRAME_CALLBACK_FLAG_ENABLE_MASK); + } +} + +void CameraService::Client::postError(status_t error) +{ + mCameraClient->errorCallback(error); +} + +status_t CameraService::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump CameraService from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + } else { + AutoMutex lock(&mLock); + if (mClient != 0) { + sp currentClient = mClient.promote(); + sprintf(buffer, "Client (%p) PID: %d\n", + currentClient->getCameraClient()->asBinder().get(), + currentClient->mClientPid); + result.append(buffer); + write(fd, result.string(), result.size()); + currentClient->mHardware->dump(fd, args); + } else { + result.append("No camera client yet.\n"); + write(fd, result.string(), result.size()); + } + } + return NO_ERROR; +} + + +#if DEBUG_HEAP_LEAKS + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t CameraService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // permission checks... + switch (code) { + case BnCameraService::CONNECT: + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int self_pid = getpid(); + if (pid != self_pid) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.CAMERA"))) + { + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't use the camera pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + break; + } + + status_t err = BnCameraService::onTransact(code, data, reply, flags); + + LOGD("+++ onTransact err %d code %d", err, code); + + if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { + // the 'service' command interrogates this binder for its name, and then supplies it + // even for the debugging commands. that means we need to check for it here, using + // ISurfaceComposer (since we delegated the INTERFACE_TRANSACTION handling to + // BnSurfaceComposer before falling through to this code). + + LOGD("+++ onTransact code %d", code); + + CHECK_INTERFACE(ICameraService, data, reply); + + switch(code) { + case 1000: + { + if (gWeakHeap != 0) { + sp h = gWeakHeap.promote(); + IMemoryHeap *p = gWeakHeap.unsafe_get(); + LOGD("CHECKING WEAK REFERENCE %p (%p)", h.get(), p); + if (h != 0) + h->printRefs(); + bool attempt_to_delete = data.readInt32() == 1; + if (attempt_to_delete) { + // NOT SAFE! + LOGD("DELETING WEAK REFERENCE %p (%p)", h.get(), p); + if (p) delete p; + } + return NO_ERROR; + } + } + break; + default: + break; + } + } + return err; +} + +#endif // DEBUG_HEAP_LEAKS + +}; // namespace android diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h new file mode 100644 index 000000000..d9b79276a --- /dev/null +++ b/camera/libcameraservice/CameraService.h @@ -0,0 +1,206 @@ +/* +** +** Copyright (C) 2008, The Android Open Source Project +** Copyright (C) 2008 HTC Inc. +** +** 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. +*/ + +#ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H +#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H + +#include +#include +#include + +class android::MemoryHeapBase; + +namespace android { + +// ---------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// When enabled, this feature allows you to send an event to the CameraService +// so that you can cause all references to the heap object gWeakHeap, defined +// below, to be printed. You will also need to set DEBUG_REFS=1 and +// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp. You just have to +// set gWeakHeap to the appropriate heap you want to track. + +#define DEBUG_HEAP_LEAKS 0 + +// ---------------------------------------------------------------------------- + +class CameraService : public BnCameraService +{ + class Client; + +public: + static void instantiate(); + + // ICameraService interface + virtual sp connect(const sp& cameraClient); + + virtual status_t dump(int fd, const Vector& args); + + void removeClient(const sp& cameraClient); + +#if DEBUG_HEAP_LEAKS + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); +#endif + +private: + +// ---------------------------------------------------------------------------- + + class Client : public BnCamera { + + public: + virtual void disconnect(); + + // connect new client with existing camera remote + virtual status_t connect(const sp& client); + + // prevent other processes from using this ICamera interface + virtual status_t lock(); + + // allow other processes to use this ICamera interface + virtual status_t unlock(); + + // pass the buffered ISurface to the camera service + virtual status_t setPreviewDisplay(const sp& surface); + + // set the preview callback flag to affect how the received frames from + // preview are handled. + virtual void setPreviewCallbackFlag(int callback_flag); + + // start preview mode, must call setPreviewDisplay first + virtual status_t startPreview(); + + // stop preview mode + virtual void stopPreview(); + + // get preview state + virtual bool previewEnabled(); + + // start recording mode + virtual status_t startRecording(); + + // stop recording mode + virtual void stopRecording(); + + // get recording state + virtual bool recordingEnabled(); + + // release a recording frame + virtual void releaseRecordingFrame(const sp& mem); + + // auto focus + virtual status_t autoFocus(); + + // take a picture - returns an IMemory (ref-counted mmap) + virtual status_t takePicture(); + + // set preview/capture parameters - key/value pairs + virtual status_t setParameters(const String8& params); + + // get preview/capture parameters - key/value pairs + virtual String8 getParameters() const; + + // our client... + const sp& getCameraClient() const { return mCameraClient; } + + private: + friend class CameraService; + Client(const sp& cameraService, + const sp& cameraClient, + pid_t clientPid); + Client(); + virtual ~Client(); + + status_t checkPid(); + + static void recordingCallback(const sp& mem, void* user); + static void previewCallback(const sp& mem, void* user); + static void shutterCallback(void *user); + static void yuvPictureCallback(const sp& mem, void* user); + static void jpegPictureCallback(const sp& mem, void* user); + static void autoFocusCallback(bool focused, void* user); + static sp getClientFromCookie(void* user); + + void postShutter(); + void postRaw(const sp& mem); + void postJpeg(const sp& mem); + void postPreviewFrame(const sp& mem); + void postRecordingFrame(const sp& frame); + void copyFrameAndPostCopiedFrame(sp heap, size_t offset, size_t size); + void postError(status_t error); + void postAutoFocus(bool focused); + + // camera operation mode + enum camera_mode { + CAMERA_PREVIEW_MODE = 0, // frame automatically released + CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame() + }; + status_t startCameraMode(camera_mode mode); + status_t startPreviewMode(); + status_t startRecordingMode(); + + // Ensures atomicity among the public methods + mutable Mutex mLock; + + // mSurfaceLock synchronizes access to mSurface between + // setPreviewSurface() and postPreviewFrame(). Note that among + // the public methods, all accesses to mSurface are + // syncrhonized by mLock. However, postPreviewFrame() is called + // by the CameraHardwareInterface callback, and needs to + // access mSurface. It cannot hold mLock, however, because + // stopPreview() may be holding that lock while attempting + // to stop preview, and stopPreview itself will block waiting + // for a callback from CameraHardwareInterface. If this + // happens, it will cause a deadlock. + mutable Mutex mSurfaceLock; + mutable Condition mReady; + sp mCameraService; + sp mSurface; + sp mPreviewBuffer; + int mPreviewCallbackFlag; + + // these are immutable once the object is created, + // they don't need to be protected by a lock + sp mCameraClient; + sp mHardware; + pid_t mClientPid; + bool mUseOverlay; + }; + +// ---------------------------------------------------------------------------- + + CameraService(); + virtual ~CameraService(); + + mutable Mutex mLock; + wp mClient; + +#if DEBUG_HEAP_LEAKS + wp gWeakHeap; +#endif +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif diff --git a/camera/libcameraservice/CannedJpeg.h b/camera/libcameraservice/CannedJpeg.h new file mode 100644 index 000000000..532560a3d --- /dev/null +++ b/camera/libcameraservice/CannedJpeg.h @@ -0,0 +1,1546 @@ +const int kCannedJpegWidth = 213; +const int kCannedJpegHeight = 350; +const int kCannedJpegSize = 18474; + +const char kCannedJpeg[] = { + 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43, + 0x00, 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04, 0x04, 0x05, + 0x05, 0x05, 0x06, 0x07, 0x0c, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, + 0x0b, 0x09, 0x0c, 0x11, 0x0f, 0x12, 0x12, 0x11, 0x0f, 0x11, 0x11, 0x13, + 0x16, 0x1c, 0x17, 0x13, 0x14, 0x1a, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, + 0x1a, 0x1d, 0x1d, 0x1f, 0x1f, 0x1f, 0x13, 0x17, 0x22, 0x24, 0x22, 0x1e, + 0x24, 0x1c, 0x1e, 0x1f, 0x1e, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x05, 0x05, + 0x05, 0x07, 0x06, 0x07, 0x0e, 0x08, 0x08, 0x0e, 0x1e, 0x14, 0x11, 0x14, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x01, 0x5e, 0x00, 0xd5, 0x03, + 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, + 0x1c, 0x00, 0x00, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x05, 0x07, 0x01, 0x03, + 0x04, 0x02, 0x08, 0xff, 0xc4, 0x00, 0x55, 0x10, 0x00, 0x01, 0x03, 0x04, + 0x00, 0x03, 0x03, 0x05, 0x0a, 0x09, 0x08, 0x09, 0x02, 0x06, 0x03, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x00, 0x05, 0x06, 0x11, 0x07, 0x12, 0x21, 0x13, + 0x31, 0x41, 0x14, 0x22, 0x51, 0x61, 0x71, 0x15, 0x23, 0x32, 0x34, 0x37, + 0x72, 0x75, 0x81, 0xb1, 0xb3, 0x08, 0x17, 0x33, 0x42, 0x52, 0x62, 0x76, + 0x93, 0xb2, 0x16, 0x24, 0x43, 0x53, 0x56, 0x91, 0xa1, 0xd2, 0x25, 0x36, + 0x63, 0x73, 0x82, 0x92, 0xa2, 0xc1, 0xd1, 0x65, 0xf0, 0x26, 0x27, 0x64, + 0x66, 0x74, 0xe1, 0x83, 0xc2, 0xf1, 0xff, 0xc4, 0x00, 0x1b, 0x01, 0x01, + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff, + 0xc4, 0x00, 0x35, 0x11, 0x00, 0x02, 0x01, 0x03, 0x03, 0x01, 0x05, 0x05, + 0x07, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x11, 0x12, 0x21, 0x31, 0x41, 0x05, 0x22, 0x32, 0x51, 0x61, 0x06, + 0x13, 0x71, 0x81, 0xb1, 0x14, 0x33, 0x42, 0x91, 0xa1, 0xc1, 0xd1, 0x15, + 0x23, 0xe1, 0xf0, 0x24, 0x53, 0x92, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, + 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xfb, 0x2e, 0x8a, 0x2b, + 0xca, 0x95, 0xae, 0xfe, 0xea, 0x03, 0xd5, 0x15, 0x8d, 0xfb, 0x28, 0xdf, + 0xb2, 0x80, 0xcd, 0x15, 0x8d, 0xfb, 0x28, 0xdf, 0xb2, 0x80, 0xcd, 0x15, + 0x8d, 0xfb, 0x28, 0xe6, 0xa0, 0x33, 0x45, 0x63, 0x9a, 0xb3, 0x40, 0x14, + 0x51, 0x45, 0x00, 0x51, 0x45, 0x14, 0x01, 0x45, 0x14, 0x50, 0x05, 0x14, + 0x56, 0x37, 0x40, 0x66, 0x8a, 0x37, 0xd6, 0xb1, 0xbf, 0x65, 0x01, 0x9a, + 0x2b, 0x1b, 0xf6, 0x51, 0xbf, 0x65, 0x01, 0x9a, 0x2b, 0x1b, 0xf6, 0x51, + 0xbf, 0x65, 0x01, 0x9a, 0x2b, 0xc9, 0x56, 0xbb, 0xeb, 0xd0, 0xa0, 0x0a, + 0x2b, 0xca, 0x95, 0xa3, 0x45, 0x01, 0xea, 0x93, 0x78, 0xe1, 0xb1, 0xc1, + 0xec, 0xb8, 0x82, 0x41, 0xf7, 0x1e, 0x4f, 0x71, 0xd7, 0xf4, 0x66, 0x9c, + 0xa9, 0x37, 0x8e, 0x5f, 0x23, 0xb9, 0x77, 0xd0, 0xf2, 0x7e, 0xec, 0xd0, + 0x15, 0xb3, 0x5c, 0x3c, 0xc1, 0xd4, 0xd2, 0x14, 0x71, 0x7b, 0x69, 0x25, + 0x20, 0x9f, 0x7a, 0xaf, 0x5f, 0x8b, 0xbc, 0x1b, 0xfb, 0x2d, 0x6c, 0xfd, + 0xd5, 0x33, 0x31, 0xf9, 0x16, 0xfe, 0x68, 0xfb, 0x2b, 0xdd, 0x7b, 0x9a, + 0x23, 0xe4, 0x78, 0x9a, 0xe5, 0xe6, 0x2b, 0x7e, 0x2e, 0xf0, 0x6f, 0xec, + 0xb5, 0xb3, 0xf7, 0x54, 0x1e, 0x1e, 0x60, 0xc3, 0xaf, 0xf2, 0x5a, 0xd9, + 0xaf, 0xf7, 0x54, 0xc7, 0x36, 0x54, 0x68, 0x50, 0xde, 0x99, 0x32, 0x43, + 0x51, 0xa3, 0x30, 0x92, 0xb7, 0x5e, 0x75, 0x41, 0x28, 0x42, 0x40, 0xea, + 0x54, 0x4f, 0x40, 0x2a, 0x2e, 0xcf, 0x13, 0x28, 0xcd, 0xf9, 0x5c, 0xb3, + 0x07, 0xb1, 0xcc, 0x7d, 0x63, 0x7e, 0xeb, 0x49, 0x67, 0xf9, 0xd4, 0x94, + 0xfa, 0x63, 0x34, 0xaf, 0x80, 0x93, 0xe0, 0xe3, 0x83, 0xae, 0xf6, 0x10, + 0x7a, 0x1a, 0xce, 0xa4, 0xa9, 0x53, 0x59, 0x92, 0x46, 0x94, 0xe3, 0x52, + 0xa3, 0xc4, 0x5b, 0x17, 0x2f, 0x98, 0xdf, 0x0b, 0xec, 0xaa, 0x65, 0xa9, + 0xf6, 0x1b, 0x60, 0x92, 0xff, 0x00, 0x48, 0xf1, 0x19, 0x8c, 0xa7, 0xa4, + 0x3e, 0x7d, 0x08, 0x69, 0x00, 0xad, 0x5f, 0x50, 0xd7, 0xa7, 0x55, 0x98, + 0x1c, 0x28, 0x93, 0x91, 0x84, 0x2a, 0x0e, 0x09, 0x62, 0xc5, 0x20, 0xa8, + 0xef, 0xca, 0x6e, 0xcc, 0x87, 0xe5, 0x91, 0xae, 0xf4, 0xc7, 0x42, 0xb9, + 0x50, 0x7e, 0x7a, 0xf6, 0x3f, 0x46, 0xae, 0x9c, 0x33, 0x0a, 0xc7, 0x71, + 0x46, 0xd6, 0xbb, 0x4c, 0x1f, 0xe7, 0x6f, 0x0f, 0xe7, 0x33, 0xa4, 0x2c, + 0xbb, 0x2a, 0x41, 0xf4, 0xad, 0xd5, 0x79, 0xc7, 0xbb, 0xbb, 0xa2, 0x47, + 0x80, 0x14, 0xc5, 0xaa, 0xf3, 0xea, 0x5c, 0x6a, 0xf0, 0xa4, 0x8e, 0xfa, + 0x74, 0x34, 0xee, 0xdb, 0x65, 0x47, 0x8e, 0x7e, 0x0f, 0x1c, 0x37, 0xb6, + 0xbe, 0x26, 0x5c, 0x6d, 0xef, 0xdf, 0x26, 0xeb, 0xf2, 0x93, 0x1c, 0xe5, + 0x6d, 0x3e, 0x90, 0x96, 0x5b, 0xe5, 0x6c, 0x0f, 0x68, 0x27, 0xd7, 0x4c, + 0xbf, 0x8a, 0x2e, 0x19, 0xff, 0x00, 0x62, 0xac, 0xff, 0x00, 0xb8, 0xa7, + 0x8a, 0x2b, 0x9c, 0xe8, 0x12, 0x3f, 0x14, 0x7c, 0x33, 0xfe, 0xc5, 0x59, + 0xff, 0x00, 0x71, 0x47, 0xe2, 0x8f, 0x86, 0x7f, 0xd8, 0xab, 0x3f, 0xee, + 0x29, 0xde, 0x8a, 0x01, 0x09, 0xce, 0x16, 0x70, 0xad, 0xb7, 0xd0, 0xc3, + 0x98, 0x8d, 0x89, 0x0e, 0xb9, 0xf0, 0x10, 0xa6, 0xc0, 0x52, 0xbd, 0x83, + 0x7d, 0x6b, 0x77, 0xe2, 0x8f, 0x86, 0x7f, 0xd8, 0xab, 0x3f, 0xee, 0x2b, + 0xb6, 0xef, 0xef, 0x9c, 0x40, 0xb5, 0x23, 0xaf, 0x98, 0xca, 0xd5, 0xf6, + 0xd3, 0x55, 0x72, 0xdb, 0xd7, 0x75, 0x65, 0x51, 0x63, 0xc2, 0xf1, 0xfa, + 0x27, 0xfb, 0x90, 0x9e, 0x44, 0x8f, 0xc5, 0x1f, 0x0c, 0xff, 0x00, 0xb1, + 0x56, 0x7f, 0xdc, 0x51, 0xf8, 0xa3, 0xe1, 0x9f, 0xf6, 0x2a, 0xcf, 0xfb, + 0x8a, 0x77, 0xa2, 0xba, 0x89, 0x12, 0x3f, 0x14, 0x7c, 0x33, 0xfe, 0xc5, + 0x59, 0xff, 0x00, 0x71, 0x58, 0x57, 0x08, 0xb8, 0x66, 0xa4, 0x94, 0x9c, + 0x2a, 0xd1, 0xa2, 0x34, 0x74, 0xce, 0xa9, 0xe2, 0x8a, 0x02, 0x91, 0xbe, + 0x7e, 0x0e, 0x38, 0x70, 0x75, 0xd9, 0x58, 0xa9, 0x16, 0xa7, 0xd4, 0x76, + 0x23, 0x4c, 0x6b, 0xcb, 0xa1, 0xa8, 0xfa, 0x0a, 0x1c, 0x3c, 0xe9, 0xdf, + 0xea, 0xad, 0x3e, 0xca, 0x58, 0xb8, 0x61, 0x36, 0xcc, 0x68, 0x7f, 0xf1, + 0x8f, 0x0c, 0xe0, 0x37, 0x19, 0x3f, 0x0a, 0xeb, 0x68, 0x69, 0x52, 0xa2, + 0x01, 0xae, 0xf5, 0xa3, 0x5d, 0xab, 0x43, 0xd6, 0x52, 0xa4, 0x8f, 0xd2, + 0xf1, 0xaf, 0xa5, 0x48, 0x1b, 0xdd, 0x63, 0x42, 0xb4, 0x85, 0x47, 0x0e, + 0x0c, 0xe7, 0x4d, 0x4f, 0x93, 0xe7, 0xfb, 0x76, 0x11, 0xc3, 0xab, 0x94, + 0x26, 0xe6, 0xdb, 0xac, 0x36, 0x59, 0x91, 0x9d, 0x1b, 0x6d, 0xe6, 0x12, + 0x16, 0x85, 0x8f, 0x51, 0x49, 0x23, 0xff, 0x00, 0x7a, 0xae, 0x81, 0xc3, + 0xbc, 0x1b, 0x5f, 0xea, 0xb5, 0xb3, 0xf7, 0x54, 0xf1, 0x95, 0x70, 0xce, + 0xdf, 0x2a, 0x73, 0xf7, 0x8c, 0x56, 0x51, 0xc6, 0xef, 0x4e, 0x9e, 0x67, + 0x1c, 0x61, 0xb0, 0xa8, 0xb2, 0x95, 0xff, 0x00, 0xd4, 0x31, 0xd1, 0x2b, + 0xdf, 0xe9, 0x0e, 0x55, 0x8f, 0xd2, 0xf0, 0xa5, 0x58, 0xb7, 0x89, 0xd0, + 0xef, 0x28, 0xc7, 0xb2, 0xab, 0x7a, 0x2d, 0x17, 0x85, 0x82, 0x63, 0x94, + 0xb8, 0x57, 0x16, 0x78, 0x1d, 0xea, 0x8e, 0xe1, 0x1d, 0x74, 0x3a, 0x94, + 0x11, 0xcc, 0x91, 0xe9, 0x1e, 0x75, 0x77, 0xd2, 0xad, 0x4e, 0xa6, 0xcd, + 0x24, 0xce, 0x0a, 0xb4, 0x6a, 0x53, 0xdd, 0x36, 0xd1, 0xc3, 0xf8, 0xbb, + 0xc1, 0xbf, 0xb2, 0xd6, 0xcf, 0xdd, 0x51, 0xf8, 0xbb, 0xc1, 0xbf, 0xb2, + 0xd6, 0xcf, 0xdd, 0x53, 0x48, 0xee, 0xa2, 0xba, 0x74, 0x47, 0xc8, 0xe7, + 0xd7, 0x2f, 0x31, 0x0e, 0xf1, 0x88, 0xe3, 0x36, 0x4b, 0xc6, 0x2d, 0x3a, + 0xd1, 0x65, 0x87, 0x06, 0x50, 0xc9, 0x2d, 0xe8, 0xed, 0x59, 0x47, 0x2a, + 0xb9, 0x4b, 0xa0, 0x11, 0xbf, 0x41, 0x15, 0xf4, 0x60, 0xee, 0xaa, 0x3f, + 0x33, 0xf8, 0xd6, 0x2d, 0xfb, 0x4f, 0x6e, 0xfb, 0xe1, 0x57, 0x80, 0xee, + 0xaf, 0x36, 0xed, 0x25, 0x53, 0x63, 0xd2, 0xb5, 0x6d, 0xc3, 0x73, 0x04, + 0x6c, 0xf7, 0xd1, 0x59, 0xa2, 0xb9, 0x4e, 0x90, 0xa4, 0xde, 0x39, 0x7c, + 0x8e, 0xe5, 0xdf, 0x43, 0xc9, 0xfb, 0xb3, 0x4e, 0x54, 0x9b, 0xc7, 0x2f, + 0x91, 0xdc, 0xbb, 0xe8, 0x79, 0x3f, 0x76, 0x68, 0x05, 0xe6, 0x3f, 0x22, + 0xdf, 0xcd, 0x1f, 0x65, 0x68, 0xbb, 0xdc, 0x61, 0x5a, 0x2d, 0x92, 0x2e, + 0x57, 0x29, 0x08, 0x8f, 0x12, 0x3a, 0x39, 0xdd, 0x71, 0x5b, 0xd0, 0x1e, + 0xaf, 0x49, 0x27, 0x40, 0x01, 0xd4, 0x92, 0x05, 0x74, 0x31, 0xf9, 0x26, + 0xc7, 0xa5, 0x20, 0x0e, 0x9e, 0xaa, 0xe0, 0xe1, 0xed, 0xa1, 0x19, 0xbe, + 0x42, 0x8c, 0xba, 0x7a, 0x79, 0xf1, 0xfb, 0x5c, 0x85, 0x26, 0xc6, 0xc2, + 0xbe, 0x0c, 0x99, 0x08, 0x3c, 0xab, 0x98, 0xa1, 0xe2, 0x12, 0xa0, 0x52, + 0xd8, 0x3d, 0x3a, 0x15, 0xf7, 0x94, 0x91, 0xec, 0xd6, 0xaa, 0xa9, 0x47, + 0x27, 0x8f, 0x46, 0x93, 0xa9, 0x2c, 0x1d, 0x18, 0x8e, 0x17, 0x33, 0x29, + 0x97, 0x1f, 0x22, 0xcd, 0xa1, 0xae, 0x3c, 0x26, 0x5c, 0x4b, 0xd6, 0xcb, + 0x0b, 0xa3, 0xa3, 0x64, 0x1d, 0xa5, 0xf9, 0x43, 0xb9, 0x4e, 0xf8, 0x86, + 0xfa, 0xa5, 0xbf, 0x5a, 0xb6, 0x45, 0xb0, 0x07, 0x4a, 0x00, 0x1a, 0xe9, + 0xfe, 0x15, 0x9a, 0xf2, 0x25, 0x27, 0x37, 0x96, 0x7a, 0xd1, 0x82, 0x8a, + 0xc2, 0x0a, 0x2b, 0x92, 0xee, 0xfc, 0xa8, 0xd0, 0x1c, 0x7a, 0x1c, 0x6f, + 0x29, 0x79, 0x23, 0xcd, 0x6f, 0x7a, 0xdd, 0x71, 0xe3, 0xf7, 0xf8, 0x77, + 0x64, 0x72, 0x02, 0x59, 0x92, 0x9e, 0x8e, 0x32, 0xbe, 0x8a, 0x07, 0xfe, + 0xf5, 0xcd, 0x2b, 0x9a, 0x71, 0xaa, 0xa9, 0x49, 0xe1, 0xbe, 0x3d, 0x49, + 0xcf, 0x42, 0x5e, 0x8a, 0xd7, 0x25, 0xf6, 0x63, 0x30, 0xa7, 0x9f, 0x71, + 0x2d, 0xb6, 0x91, 0xb2, 0xa5, 0x1d, 0x0a, 0xae, 0xb2, 0x8c, 0xe9, 0xf7, + 0xd4, 0xb8, 0xd6, 0x8f, 0x7a, 0x6b, 0xb8, 0xbc, 0x7e, 0x12, 0xbd, 0x9e, + 0x8a, 0xe6, 0xbf, 0xed, 0x3a, 0x16, 0x30, 0xd5, 0x55, 0xef, 0xd1, 0x75, + 0x64, 0x4a, 0x49, 0x72, 0x3e, 0x5c, 0xae, 0xd6, 0xdb, 0x72, 0x79, 0xa6, + 0xcc, 0x69, 0x9f, 0x51, 0x3d, 0x7f, 0xb8, 0x75, 0xa5, 0xc9, 0x9c, 0x40, + 0xb4, 0xb4, 0x48, 0x61, 0x97, 0xdf, 0xf5, 0xeb, 0x42, 0xab, 0x17, 0x9d, + 0x75, 0xe7, 0x0b, 0x8f, 0x38, 0xa7, 0x16, 0x7b, 0xd4, 0xa3, 0xb3, 0x5e, + 0x2b, 0xe3, 0xae, 0x7d, 0xab, 0xb9, 0x9b, 0xfe, 0xd4, 0x54, 0x57, 0xe6, + 0xff, 0x00, 0xdf, 0x91, 0x93, 0xaa, 0xfa, 0x0d, 0xef, 0x66, 0x0c, 0xb9, + 0x93, 0xb5, 0x77, 0x30, 0x97, 0xc8, 0xdb, 0x25, 0xb0, 0x8e, 0x71, 0xbe, + 0xbe, 0x34, 0xc7, 0x0f, 0x88, 0x16, 0x87, 0x54, 0x03, 0xed, 0x3e, 0xc6, + 0xfc, 0x48, 0xd8, 0xff, 0x00, 0x0a, 0xab, 0x40, 0x24, 0x80, 0x01, 0x24, + 0xf7, 0x01, 0x5d, 0x73, 0x6d, 0x97, 0x08, 0x4d, 0x21, 0xd9, 0x51, 0x1d, + 0x65, 0x0b, 0xf8, 0x2a, 0x52, 0x7a, 0x1a, 0xe3, 0xb6, 0xed, 0xcb, 0xfa, + 0x5a, 0xa7, 0x1d, 0xd3, 0x79, 0x7b, 0x10, 0xa7, 0x22, 0xea, 0xb7, 0x5d, + 0x20, 0x5c, 0x51, 0xcf, 0x0e, 0x53, 0x6e, 0x8f, 0x40, 0x3d, 0x47, 0xd5, + 0x5d, 0x95, 0x41, 0x46, 0x90, 0xfc, 0x67, 0x43, 0xb1, 0xdd, 0x5b, 0x4b, + 0x1d, 0xca, 0x49, 0xd1, 0xab, 0x07, 0x10, 0xcd, 0xbb, 0x65, 0xa2, 0x15, + 0xdd, 0x41, 0x2b, 0x3d, 0x10, 0xf7, 0x70, 0x3e, 0xda, 0xfa, 0x5e, 0xcd, + 0xf6, 0x9a, 0x95, 0xc4, 0x95, 0x3a, 0xeb, 0x4b, 0x7d, 0x7a, 0x7f, 0x83, + 0x48, 0xd4, 0x4f, 0x91, 0xf2, 0x8a, 0x01, 0x04, 0x6c, 0x1d, 0x83, 0x45, + 0x7d, 0x41, 0xa0, 0x51, 0x45, 0x14, 0x06, 0x0a, 0x41, 0x3b, 0xa8, 0x7c, + 0xbf, 0x1b, 0xb3, 0xe5, 0x56, 0x47, 0xac, 0xf7, 0xa8, 0xbe, 0x51, 0x19, + 0xc5, 0x05, 0xa4, 0xa4, 0xf2, 0xad, 0xa7, 0x13, 0xd5, 0x2e, 0x21, 0x43, + 0xaa, 0x16, 0x93, 0xd4, 0x28, 0x75, 0x15, 0x33, 0x46, 0xa8, 0x0a, 0x45, + 0x2e, 0x5d, 0x71, 0x5b, 0xf3, 0x58, 0xae, 0x51, 0x24, 0x4a, 0x32, 0x36, + 0x2c, 0xf7, 0x52, 0x9e, 0x41, 0x38, 0x24, 0x6c, 0xb4, 0xe0, 0xee, 0x4b, + 0xe9, 0x03, 0x67, 0x5d, 0x16, 0x3c, 0xe1, 0xae, 0xa2, 0xa7, 0xa9, 0xdb, + 0x33, 0xc6, 0xed, 0x99, 0x56, 0x3f, 0x2a, 0xcb, 0x74, 0x6d, 0x65, 0x87, + 0x80, 0x28, 0x71, 0xb5, 0x72, 0xba, 0xcb, 0x89, 0xea, 0x87, 0x1b, 0x50, + 0xea, 0x95, 0xa4, 0x80, 0xa0, 0x7d, 0x35, 0x57, 0xe3, 0x13, 0x2e, 0x3d, + 0xa4, 0xeb, 0x05, 0xfb, 0x93, 0xdd, 0xcb, 0x3b, 0x81, 0x89, 0x6a, 0x42, + 0x42, 0x53, 0x21, 0x24, 0x73, 0x35, 0x21, 0x00, 0x77, 0x25, 0xc4, 0xec, + 0xeb, 0xc1, 0x41, 0x43, 0xc3, 0x55, 0xe9, 0x5a, 0xd7, 0xd5, 0xdd, 0x97, + 0x27, 0x9b, 0x73, 0x43, 0x4f, 0x7a, 0x3c, 0x1a, 0x73, 0x3f, 0x8d, 0x62, + 0xdf, 0xb4, 0xf6, 0xef, 0xbe, 0x15, 0x78, 0x0e, 0xea, 0xa3, 0xb3, 0x3f, + 0x8d, 0x62, 0xdf, 0xb4, 0xf6, 0xdf, 0xbe, 0x15, 0x78, 0x8e, 0xea, 0xe7, + 0xbc, 0xfb, 0xcf, 0x91, 0xd1, 0x69, 0xf7, 0x61, 0x45, 0x14, 0x57, 0x29, + 0xd4, 0x14, 0x9b, 0xc7, 0x2f, 0x91, 0xdc, 0xbb, 0xe8, 0x79, 0x3f, 0x76, + 0x69, 0xca, 0x93, 0x38, 0xe4, 0x7f, 0xf9, 0x3d, 0x97, 0x74, 0xdf, 0xfa, + 0x22, 0x4f, 0xdd, 0x9a, 0x01, 0x03, 0x28, 0x12, 0xae, 0x49, 0xb5, 0x62, + 0x56, 0xd7, 0x96, 0xcc, 0xdb, 0xfb, 0xbe, 0x4c, 0xa7, 0x50, 0x74, 0xa6, + 0x22, 0xa5, 0x1c, 0xd2, 0x1d, 0x1e, 0x82, 0x10, 0x39, 0x47, 0xeb, 0x2d, + 0x35, 0x74, 0x5a, 0xa0, 0x44, 0xb6, 0x5b, 0x23, 0x5b, 0x60, 0x30, 0x88, + 0xf1, 0x22, 0xb4, 0x96, 0x58, 0x69, 0x03, 0xcd, 0x42, 0x12, 0x34, 0x90, + 0x3d, 0x80, 0x0a, 0xac, 0x78, 0x51, 0x10, 0xdc, 0xf8, 0x87, 0x90, 0xdf, + 0x1d, 0x4f, 0x33, 0x56, 0x98, 0xac, 0x59, 0xe2, 0x1d, 0xf4, 0x0b, 0x52, + 0x43, 0xf2, 0x0e, 0xbd, 0x27, 0x99, 0x81, 0xbf, 0xd4, 0xab, 0x60, 0x56, + 0xf7, 0x35, 0x35, 0xcd, 0xfa, 0x18, 0x5b, 0x43, 0x44, 0x17, 0xa8, 0x01, + 0xa1, 0xaa, 0x28, 0xa2, 0xb0, 0x37, 0x0a, 0x81, 0xc8, 0xf1, 0xf8, 0xb3, + 0x41, 0x9a, 0xc3, 0x9e, 0x47, 0x31, 0xa1, 0xcc, 0x97, 0xd1, 0xd3, 0xbb, + 0xd3, 0x53, 0xd4, 0x9d, 0xc4, 0xfb, 0xb9, 0x87, 0x6d, 0x4d, 0xbd, 0x95, + 0x69, 0xd9, 0x3f, 0x0b, 0xd4, 0x8a, 0xf3, 0xfb, 0x52, 0x74, 0x69, 0xda, + 0xca, 0x75, 0x96, 0x52, 0xfa, 0xf4, 0xc1, 0x59, 0x63, 0x1b, 0x89, 0x59, + 0x06, 0x45, 0x73, 0xb9, 0xb4, 0x88, 0x72, 0x5f, 0x4a, 0x9b, 0x6b, 0xcd, + 0x25, 0x1d, 0x03, 0x84, 0x78, 0x9a, 0x84, 0xad, 0x91, 0x63, 0xbd, 0x29, + 0xf4, 0xb3, 0x1d, 0xa5, 0x38, 0xe2, 0xbb, 0x92, 0x91, 0xd4, 0xd7, 0xb9, + 0xd0, 0xa5, 0x41, 0x7b, 0xb1, 0x96, 0xc2, 0xd9, 0x5e, 0xb7, 0xa5, 0x0d, + 0x57, 0xe5, 0xd5, 0xaa, 0x56, 0xb8, 0x6e, 0xad, 0x4c, 0xbe, 0x99, 0xfd, + 0xb2, 0x73, 0xbc, 0xbd, 0xcd, 0x14, 0x51, 0x45, 0x73, 0x10, 0x74, 0x5b, + 0x24, 0x08, 0x97, 0x06, 0x24, 0xa9, 0x1c, 0xe1, 0xa5, 0x85, 0x14, 0xfa, + 0x75, 0x4e, 0x19, 0x9e, 0x59, 0x6f, 0xba, 0xd9, 0x84, 0x38, 0x8d, 0x2c, + 0xad, 0x6a, 0x05, 0x45, 0x69, 0xd7, 0x2e, 0xa9, 0x1e, 0x8a, 0xee, 0xb7, + 0xed, 0x0a, 0xd6, 0xf4, 0x67, 0x46, 0x1c, 0x4b, 0x92, 0x54, 0x9a, 0x58, + 0x0a, 0x28, 0x48, 0x2a, 0x3a, 0x00, 0x92, 0x7c, 0x05, 0x30, 0x59, 0xb1, + 0x3b, 0x9c, 0xf4, 0x87, 0x5d, 0x48, 0x8b, 0x1f, 0xbc, 0xb8, 0xef, 0x4e, + 0x9e, 0xca, 0xc2, 0x85, 0xb5, 0x5b, 0x89, 0x69, 0xa5, 0x16, 0xd8, 0x49, + 0xbe, 0x06, 0xae, 0x1a, 0xe4, 0x0a, 0x96, 0xd1, 0xb5, 0x4b, 0x5e, 0xdd, + 0x6c, 0x6d, 0xa5, 0x1e, 0xf5, 0x27, 0xd1, 0xed, 0xa7, 0x47, 0xdd, 0x6d, + 0x86, 0x56, 0xf3, 0xcb, 0x4a, 0x1b, 0x40, 0xda, 0x94, 0x4e, 0x80, 0x15, + 0x5d, 0xc7, 0x93, 0x8c, 0xe3, 0x0e, 0x85, 0xb0, 0xa5, 0xcf, 0x9a, 0x9e, + 0x9c, 0xe0, 0xf4, 0x4d, 0x4a, 0x71, 0x16, 0xe0, 0x5f, 0xc4, 0xa3, 0xbf, + 0x15, 0x5e, 0xf3, 0x25, 0x69, 0xe6, 0x23, 0xd1, 0xa2, 0x75, 0xff, 0x00, + 0xbf, 0x45, 0x7d, 0xf5, 0x8f, 0x68, 0x3b, 0x6b, 0x29, 0x46, 0xac, 0x94, + 0xa7, 0x4d, 0x70, 0x9e, 0x76, 0xe9, 0x96, 0x6d, 0x19, 0x61, 0x6e, 0x78, + 0x9f, 0xc4, 0x38, 0x4d, 0x3e, 0x5b, 0x8b, 0x0d, 0xc7, 0xd0, 0x0f, 0xc3, + 0x2a, 0xe5, 0x07, 0xd8, 0x2a, 0x6f, 0x1a, 0xc9, 0x60, 0x5f, 0x01, 0x43, + 0x04, 0xb6, 0xfa, 0x46, 0xd4, 0xd2, 0xfb, 0xf5, 0xe9, 0x1e, 0x9a, 0xa6, + 0x6b, 0xb2, 0xcb, 0x3d, 0xcb, 0x65, 0xd1, 0x89, 0xad, 0x13, 0xb6, 0xd5, + 0xb2, 0x07, 0x88, 0xf1, 0x1f, 0xdd, 0x5e, 0x0d, 0xaf, 0xb4, 0xf7, 0x4a, + 0xba, 0x75, 0x9a, 0x71, 0x7c, 0xac, 0x71, 0xf0, 0x28, 0xaa, 0x3c, 0xee, + 0x5e, 0xb4, 0x56, 0xa8, 0x8f, 0xb7, 0x2a, 0x2b, 0x52, 0x1a, 0x50, 0x53, + 0x6e, 0x24, 0x29, 0x24, 0x78, 0x83, 0x5b, 0x6b, 0xf4, 0x24, 0xd4, 0x96, + 0x51, 0xb8, 0x6b, 0xae, 0xea, 0xae, 0xe3, 0x5c, 0x1f, 0x72, 0x2e, 0x16, + 0x9c, 0xfa, 0x38, 0xe5, 0x4c, 0x25, 0xa6, 0x05, 0xdf, 0x47, 0x41, 0x70, + 0x9e, 0x58, 0x01, 0x47, 0xfd, 0xd3, 0xa5, 0x0b, 0x1e, 0x80, 0x57, 0xe9, + 0xab, 0x46, 0xa3, 0xb2, 0x5b, 0x4c, 0x5b, 0xf5, 0x82, 0xe1, 0x64, 0x9a, + 0x9e, 0x68, 0xd3, 0xe3, 0x39, 0x19, 0xdf, 0x9a, 0xb4, 0x94, 0x9d, 0x7a, + 0xfa, 0xd5, 0xe3, 0x2d, 0x2d, 0x34, 0x56, 0x51, 0x52, 0x4d, 0x32, 0xa0, + 0xcc, 0xc1, 0x12, 0xb1, 0x60, 0x7b, 0xc6, 0x4f, 0x6e, 0xfb, 0xe1, 0x57, + 0x80, 0xee, 0xaf, 0x9b, 0xad, 0xd3, 0xa4, 0xcf, 0xc5, 0x70, 0x57, 0x27, + 0x12, 0x67, 0x31, 0x92, 0x5b, 0xe2, 0x4b, 0xdf, 0x7f, 0x6e, 0xcc, 0x8e, + 0xc9, 0xcd, 0xfa, 0xca, 0x90, 0x4f, 0xd7, 0x5f, 0x48, 0x8e, 0xea, 0xde, + 0xed, 0xea, 0x9a, 0x7e, 0x86, 0x16, 0xab, 0x4c, 0x1a, 0xf5, 0x0a, 0x28, + 0xa2, 0xb9, 0x8e, 0x90, 0xa4, 0xde, 0x37, 0x8d, 0xf0, 0x7f, 0x2e, 0x1b, + 0xd7, 0xfa, 0x22, 0x47, 0xdd, 0x9a, 0x72, 0xa4, 0xee, 0x37, 0x74, 0xe1, + 0x06, 0x5c, 0x7d, 0x16, 0x89, 0x1f, 0x76, 0x68, 0x08, 0x8f, 0xc1, 0xcd, + 0x21, 0xee, 0x1b, 0x0b, 0xc7, 0x4e, 0xd2, 0xef, 0x73, 0x9b, 0x35, 0x64, + 0x7a, 0xe4, 0x2d, 0x09, 0xff, 0x00, 0xa1, 0x08, 0x15, 0x64, 0x55, 0x7b, + 0xf8, 0x37, 0xa5, 0x23, 0x81, 0xb8, 0x98, 0x1e, 0x30, 0x42, 0x8f, 0xb4, + 0xad, 0x44, 0xff, 0x00, 0x89, 0x35, 0x61, 0x51, 0xee, 0xf2, 0x42, 0xd8, + 0x28, 0xa2, 0x8a, 0x12, 0x15, 0x4e, 0xf1, 0x02, 0x62, 0xa5, 0xe4, 0xf2, + 0x7c, 0xe2, 0x52, 0xc9, 0xec, 0xd2, 0x3d, 0x1a, 0xab, 0x88, 0xf7, 0x55, + 0x17, 0x7e, 0x25, 0x57, 0xa9, 0xa4, 0xf7, 0xf6, 0xea, 0xfb, 0x6b, 0xe4, + 0xfd, 0xad, 0xa8, 0xe3, 0x6f, 0x08, 0x2e, 0xaf, 0xe8, 0x8c, 0xaa, 0xf0, + 0x76, 0xe1, 0x97, 0x76, 0x6c, 0xd7, 0x94, 0xca, 0x90, 0xd9, 0x5b, 0x65, + 0x25, 0x27, 0x97, 0xbc, 0x6f, 0xc6, 0xbb, 0x33, 0xdb, 0xf4, 0x5b, 0xdc, + 0x96, 0x3c, 0x91, 0xb5, 0x04, 0x34, 0x0f, 0x9e, 0xa1, 0xa2, 0x77, 0x4b, + 0x34, 0x57, 0xc7, 0xc7, 0xb4, 0x2b, 0x46, 0xd5, 0xda, 0xaf, 0x0b, 0x79, + 0x32, 0xd4, 0xf1, 0x80, 0xa2, 0xbd, 0xb2, 0xd3, 0xaf, 0x38, 0x1b, 0x65, + 0xb5, 0x38, 0xb3, 0xdc, 0x12, 0x36, 0x69, 0x9a, 0xd9, 0x87, 0x48, 0x53, + 0x42, 0x4d, 0xd9, 0xf4, 0x41, 0x8e, 0x3a, 0x9e, 0x63, 0xe7, 0x1a, 0xce, + 0xda, 0xce, 0xb5, 0xcb, 0xc5, 0x28, 0xe7, 0xe9, 0xf3, 0x61, 0x26, 0xc5, + 0x74, 0x21, 0x6e, 0x28, 0x25, 0x09, 0x2a, 0x51, 0xee, 0x00, 0x6e, 0x99, + 0x2d, 0x18, 0x7c, 0xf9, 0x2d, 0x89, 0x13, 0x54, 0x98, 0x51, 0xfb, 0xca, + 0x9c, 0x3a, 0x3a, 0xf6, 0x54, 0x83, 0x97, 0xcb, 0x05, 0x89, 0x05, 0xab, + 0x2c, 0x31, 0x25, 0xf1, 0xd0, 0xbe, 0xe0, 0xe9, 0x4b, 0x77, 0x7b, 0xdd, + 0xca, 0xe8, 0xe1, 0x54, 0xb9, 0x2b, 0x52, 0x7c, 0x10, 0x0e, 0x92, 0x3e, + 0xaa, 0xed, 0xf7, 0x36, 0x76, 0xbf, 0x7b, 0x2f, 0x79, 0x2f, 0x28, 0xf1, + 0xf3, 0x7d, 0x7e, 0x44, 0xe1, 0x21, 0x94, 0xdc, 0x71, 0x9c, 0x78, 0x72, + 0xdb, 0xd8, 0xf7, 0x42, 0x58, 0xfe, 0x95, 0x7f, 0x04, 0x1a, 0x5f, 0xbc, + 0xe4, 0x57, 0x4b, 0xa2, 0x88, 0x7e, 0x41, 0x43, 0x5e, 0x0d, 0xa3, 0xa2, + 0x45, 0x44, 0xd1, 0x58, 0x57, 0xed, 0x2a, 0xd5, 0x63, 0xee, 0xe3, 0xdd, + 0x8f, 0x92, 0xd9, 0x7c, 0xfc, 0xfe, 0x64, 0x39, 0x36, 0x14, 0xed, 0x87, + 0x38, 0x8b, 0xd6, 0x39, 0x37, 0x1d, 0x7d, 0x43, 0xb4, 0x4a, 0x7b, 0x48, + 0xe4, 0xf8, 0x1f, 0xff, 0x00, 0xdf, 0xfb, 0xd2, 0x4d, 0x77, 0x58, 0x2e, + 0x0e, 0x5b, 0x2e, 0xcc, 0x4c, 0x6c, 0xfc, 0x05, 0x79, 0xc3, 0xd2, 0x3c, + 0x45, 0x57, 0xb3, 0xae, 0x55, 0xbd, 0x74, 0xe7, 0xe1, 0x7b, 0x3f, 0x83, + 0xe4, 0x45, 0xe1, 0x9c, 0x6e, 0xb6, 0xb6, 0x9d, 0x5b, 0x4e, 0x24, 0xa5, + 0x68, 0x51, 0x4a, 0x81, 0xf0, 0x23, 0xbc, 0x57, 0x9a, 0x6a, 0xe2, 0x35, + 0xbd, 0xb6, 0xae, 0x0d, 0x5d, 0x62, 0xf5, 0x8d, 0x39, 0x21, 0x7b, 0x1e, + 0x0a, 0xd7, 0xfd, 0xc7, 0xfd, 0xe9, 0x56, 0xb2, 0xbc, 0xb6, 0x76, 0xd5, + 0xa5, 0x49, 0xf4, 0xfd, 0x57, 0x47, 0xf9, 0x06, 0xb0, 0xf0, 0x59, 0x7c, + 0x2b, 0xbb, 0x76, 0xf0, 0x1c, 0xb5, 0xba, 0xad, 0xad, 0x8f, 0x39, 0xbd, + 0xf8, 0xa4, 0xff, 0x00, 0xe0, 0xff, 0x00, 0xda, 0x9d, 0xaa, 0x8e, 0xc7, + 0x2e, 0x4b, 0xb5, 0x5e, 0x18, 0x98, 0x92, 0x79, 0x52, 0xad, 0x2c, 0x7a, + 0x52, 0x7b, 0xea, 0xee, 0x65, 0xc4, 0x3c, 0xca, 0x1d, 0x6c, 0x85, 0x21, + 0x60, 0x10, 0x47, 0x88, 0xaf, 0xbe, 0xf6, 0x6a, 0xfb, 0xed, 0x16, 0xbe, + 0xea, 0x4f, 0xbd, 0x0d, 0xbe, 0x5d, 0x3f, 0x83, 0x6a, 0x72, 0xca, 0x3d, + 0xd6, 0x08, 0xeb, 0x59, 0xa3, 0x55, 0xf4, 0x66, 0x87, 0xce, 0x37, 0x58, + 0xc2, 0xdb, 0x9b, 0xcf, 0xb5, 0x05, 0xf3, 0x06, 0xf8, 0x8b, 0x6f, 0x96, + 0x9f, 0x52, 0x64, 0x86, 0x5e, 0x3f, 0xf5, 0x29, 0x75, 0xf4, 0x70, 0xee, + 0xaf, 0x9e, 0x33, 0x51, 0xae, 0x33, 0xdc, 0xff, 0x00, 0x6a, 0x71, 0xd3, + 0xf5, 0xf2, 0x01, 0xff, 0x00, 0x8a, 0xfa, 0x1c, 0x77, 0x56, 0x93, 0x79, + 0xc7, 0xc0, 0xce, 0x1c, 0xbf, 0x8f, 0xf0, 0x14, 0x51, 0x45, 0x66, 0x68, + 0x14, 0x9b, 0xc6, 0xf3, 0xae, 0x10, 0x65, 0xa7, 0xff, 0x00, 0x48, 0x91, + 0xf7, 0x66, 0x9c, 0xa9, 0x33, 0x8e, 0x5f, 0x23, 0xb9, 0x77, 0xd0, 0xf2, + 0x7e, 0xec, 0xd0, 0x11, 0x9f, 0x83, 0x79, 0x2d, 0xf0, 0x82, 0xd5, 0x05, + 0x67, 0xdf, 0x60, 0x3f, 0x2e, 0x1b, 0xa0, 0xf7, 0x82, 0xdc, 0x97, 0x53, + 0xaf, 0xee, 0x03, 0xfb, 0xea, 0xc6, 0xaa, 0xb7, 0x82, 0xb2, 0xbc, 0x87, + 0x22, 0xcb, 0x31, 0xb7, 0x34, 0x92, 0x64, 0xb5, 0x78, 0x8f, 0xb3, 0xf0, + 0x9b, 0x90, 0xd8, 0x4a, 0xf4, 0x3d, 0x01, 0xd6, 0x9c, 0xff, 0x00, 0x98, + 0x7a, 0x6a, 0xd2, 0x1d, 0xd5, 0x69, 0xad, 0x32, 0x68, 0xac, 0x25, 0xaa, + 0x29, 0x85, 0x14, 0x51, 0x55, 0x2c, 0x15, 0x4a, 0xe6, 0x71, 0x8c, 0x4c, + 0x9a, 0x6b, 0x44, 0x10, 0x0b, 0x9c, 0xc3, 0xd8, 0x7a, 0xd5, 0xd5, 0x4a, + 0xd9, 0x8d, 0x86, 0xd7, 0x26, 0x4a, 0x6e, 0xd7, 0x17, 0xd4, 0xcb, 0x4d, + 0x23, 0x95, 0xce, 0x51, 0xf0, 0xbd, 0x15, 0xe0, 0xfb, 0x43, 0x61, 0x2b, + 0xcb, 0x65, 0xa3, 0x98, 0xbc, 0xef, 0xb6, 0xdd, 0x4a, 0x4e, 0x39, 0x45, + 0x59, 0x16, 0x34, 0x89, 0x4e, 0x86, 0xa3, 0xb2, 0xb7, 0x56, 0x7b, 0x82, + 0x46, 0xe9, 0xa6, 0x06, 0x1e, 0x19, 0x68, 0x4a, 0xbe, 0xcb, 0x6e, 0x1b, + 0x3d, 0xfc, 0x9b, 0xf3, 0x8d, 0x7b, 0x97, 0x96, 0x43, 0xb7, 0xb4, 0x63, + 0x63, 0xd0, 0x50, 0xca, 0x7b, 0xbb, 0x65, 0x8f, 0x38, 0xfa, 0xe9, 0x5a, + 0x7c, 0xe9, 0x73, 0x9e, 0x2e, 0xcb, 0x90, 0xb7, 0x54, 0x7f, 0x48, 0xd7, + 0xc5, 0x62, 0xca, 0xd7, 0x9f, 0xee, 0xcb, 0xf2, 0x8f, 0xf2, 0xcc, 0x76, + 0x43, 0x5b, 0xf9, 0x2d, 0xa6, 0xd0, 0xd9, 0x63, 0x1f, 0x82, 0x92, 0xbe, + 0xe2, 0xfb, 0x83, 0xad, 0x2c, 0x5c, 0xee, 0x93, 0xee, 0x4e, 0x97, 0x26, + 0x49, 0x5b, 0x87, 0xd1, 0xbe, 0x83, 0xea, 0xae, 0x3a, 0x2b, 0x9a, 0xe7, + 0xb4, 0x2b, 0xdc, 0x2d, 0x2d, 0xe2, 0x3e, 0x4b, 0x65, 0xf9, 0x10, 0xe4, + 0xd8, 0x51, 0x45, 0x15, 0xc2, 0x40, 0x51, 0x45, 0x14, 0x01, 0x45, 0x14, + 0x50, 0x0f, 0x18, 0xe2, 0x86, 0x41, 0x88, 0xca, 0xb2, 0x38, 0x41, 0x93, + 0x18, 0x76, 0x91, 0xc9, 0xff, 0x00, 0x0f, 0xfc, 0x7d, 0x74, 0x8e, 0xa0, + 0x52, 0xa2, 0x95, 0x02, 0x08, 0x3a, 0x20, 0xf8, 0x54, 0x9e, 0x2f, 0x73, + 0x55, 0xaa, 0xf4, 0xc4, 0xa0, 0x4f, 0x20, 0x57, 0x2b, 0x83, 0xd2, 0x93, + 0xdf, 0x52, 0x5c, 0x42, 0xb6, 0x26, 0x1d, 0xe0, 0x4c, 0x8e, 0x37, 0x1a, + 0x6a, 0x7b, 0x54, 0x11, 0xdd, 0xbf, 0x1f, 0xfc, 0xfd, 0x75, 0xec, 0x57, + 0xff, 0x00, 0x95, 0x67, 0x1a, 0xdf, 0x8a, 0x9f, 0x75, 0xfc, 0x3f, 0x0b, + 0xfd, 0xbf, 0x22, 0xcf, 0x75, 0x91, 0x6a, 0xad, 0x2e, 0x18, 0x5d, 0xbc, + 0xb2, 0xd0, 0x60, 0x3a, 0xad, 0xbb, 0x17, 0xa2, 0x77, 0xe2, 0x83, 0xdd, + 0xfd, 0xdd, 0xd5, 0x56, 0xd4, 0xc6, 0x19, 0x71, 0x5d, 0xb7, 0x21, 0x8c, + 0xea, 0x49, 0xe4, 0x71, 0x41, 0xb7, 0x07, 0xa4, 0x13, 0xaf, 0xfc, 0x1a, + 0xaf, 0x62, 0xde, 0xbb, 0x3b, 0xb8, 0xc9, 0xf0, 0xf6, 0x7f, 0x07, 0xfc, + 0x08, 0x3c, 0x32, 0xe9, 0xac, 0x13, 0xaa, 0xcd, 0x68, 0x9b, 0x25, 0x98, + 0x91, 0x9e, 0x95, 0x25, 0xc4, 0xb4, 0xc3, 0x2d, 0xa9, 0xc7, 0x16, 0xa3, + 0xa0, 0x94, 0xa4, 0x6c, 0x93, 0xec, 0x15, 0xfa, 0xa1, 0xd2, 0x7c, 0xf9, + 0x92, 0x3c, 0xdc, 0xde, 0x27, 0x5c, 0xee, 0x0c, 0xab, 0x99, 0x07, 0x3b, + 0xb3, 0xc3, 0xdf, 0xeb, 0x32, 0xdb, 0x01, 0x5f, 0xf5, 0x28, 0xd7, 0xd1, + 0x43, 0xba, 0xbe, 0x69, 0xb3, 0xa1, 0xd7, 0x71, 0xfc, 0x56, 0xef, 0x25, + 0xb2, 0xdc, 0x9b, 0xde, 0x63, 0x16, 0xec, 0xf2, 0x7d, 0x06, 0x44, 0xa2, + 0xb4, 0x8f, 0xa9, 0x05, 0x03, 0xea, 0xaf, 0xa5, 0x85, 0x6b, 0x56, 0x3a, + 0x70, 0xbd, 0x0c, 0x68, 0xcb, 0x56, 0x5f, 0xa8, 0x51, 0x45, 0x15, 0x91, + 0xb0, 0x52, 0x67, 0x1c, 0xbe, 0x47, 0x72, 0xef, 0xa1, 0xe4, 0xfd, 0xd9, + 0xa7, 0x3a, 0x4c, 0xe3, 0x91, 0xd7, 0x07, 0x72, 0xfd, 0xf7, 0x7b, 0x91, + 0x27, 0xee, 0xcd, 0x00, 0x83, 0x74, 0x98, 0x31, 0x9c, 0x8e, 0xc3, 0x9a, + 0x95, 0x72, 0x44, 0x86, 0x0c, 0x1b, 0xa9, 0x1e, 0x10, 0xde, 0x29, 0xf7, + 0xc5, 0x78, 0xe9, 0xb7, 0x03, 0x6b, 0xf9, 0xbc, 0xf5, 0x78, 0xa7, 0xb8, + 0x55, 0x65, 0x1a, 0xd0, 0xf5, 0xde, 0x12, 0xa2, 0x18, 0x7e, 0x53, 0x1d, + 0xe6, 0xbb, 0x37, 0x50, 0xe0, 0xd2, 0x16, 0x92, 0x34, 0x52, 0x49, 0xf0, + 0x20, 0x91, 0x5d, 0x5c, 0x36, 0x9f, 0x37, 0x19, 0x9c, 0x9e, 0x1c, 0xe4, + 0x6f, 0xf3, 0xbf, 0x19, 0x92, 0xbb, 0x1c, 0xc5, 0x9d, 0xf9, 0x7c, 0x34, + 0xfe, 0x66, 0xfc, 0x5e, 0x6b, 0xa2, 0x54, 0x3b, 0xca, 0x79, 0x55, 0xd7, + 0x6a, 0xd7, 0x5d, 0xdc, 0x62, 0xa5, 0x94, 0xce, 0x4b, 0x49, 0x4b, 0x4e, + 0x1a, 0xe0, 0xb0, 0xea, 0x37, 0x23, 0xbb, 0xb3, 0x65, 0xb6, 0x2e, 0x63, + 0xc3, 0x98, 0x8e, 0x88, 0x46, 0xf5, 0xcc, 0xaf, 0x45, 0x49, 0x0a, 0x4a, + 0xe2, 0xd3, 0x2e, 0xae, 0xd5, 0x15, 0xd4, 0x6c, 0xa1, 0x0e, 0x9e, 0x7d, + 0x7a, 0xc7, 0x4a, 0xf1, 0xbb, 0x4e, 0xe2, 0x76, 0xf6, 0x93, 0xab, 0x4f, + 0x94, 0x8e, 0xa9, 0x3c, 0x21, 0x5a, 0x5e, 0x69, 0x7e, 0x7d, 0xf2, 0xe2, + 0x25, 0x06, 0x53, 0xbe, 0x88, 0x42, 0x46, 0xa9, 0x8b, 0x1b, 0xc8, 0x7f, + 0x94, 0x71, 0x9e, 0xb2, 0x5d, 0x12, 0x8e, 0xd9, 0xc6, 0xc8, 0x42, 0xc0, + 0xd0, 0x57, 0xff, 0x00, 0xba, 0xae, 0xab, 0x7d, 0xbe, 0x53, 0x90, 0xa6, + 0xb3, 0x2d, 0xa3, 0xa5, 0xb4, 0xb0, 0xa1, 0x5f, 0x9d, 0x5a, 0xf6, 0xc5, + 0xcd, 0x3a, 0xba, 0xaa, 0xcd, 0xca, 0x2f, 0x94, 0xf7, 0xca, 0xea, 0x73, + 0xa9, 0xbc, 0xee, 0x17, 0x08, 0xae, 0x42, 0x9a, 0xf4, 0x57, 0x46, 0x96, + 0xd2, 0x8a, 0x4d, 0x68, 0xa7, 0x1e, 0x20, 0x45, 0x6e, 0x5c, 0x78, 0x99, + 0x04, 0x50, 0x0b, 0x72, 0x10, 0x03, 0x9a, 0xf0, 0x55, 0x27, 0x57, 0x25, + 0xf5, 0xb7, 0xd9, 0xab, 0xca, 0x9a, 0xe3, 0x95, 0xea, 0x9f, 0x04, 0x49, + 0x61, 0x85, 0x14, 0x51, 0x5c, 0x64, 0x05, 0x14, 0x51, 0x40, 0x14, 0x51, + 0x45, 0x00, 0x51, 0x45, 0x14, 0x01, 0x4f, 0x56, 0x84, 0xff, 0x00, 0x29, + 0xf0, 0xb7, 0x2d, 0xa4, 0x83, 0x36, 0x11, 0xe6, 0x64, 0x9e, 0xf2, 0x3c, + 0x07, 0xda, 0x3e, 0xb1, 0x48, 0xb5, 0x33, 0x86, 0xdd, 0x0d, 0xa6, 0xfa, + 0xcb, 0xe4, 0x9e, 0xc9, 0x67, 0x91, 0xc1, 0xea, 0x35, 0xe9, 0x76, 0x65, + 0x78, 0x52, 0xad, 0xa2, 0xa7, 0x82, 0x7d, 0xd7, 0xf0, 0x7d, 0x7e, 0x5c, + 0x96, 0x8b, 0xc3, 0x22, 0xa4, 0xc7, 0x7a, 0x33, 0xca, 0x65, 0xf6, 0x94, + 0xdb, 0x89, 0x3a, 0x29, 0x50, 0xd5, 0x32, 0xf0, 0xff, 0x00, 0x1f, 0x7e, + 0xe3, 0x74, 0x6e, 0x6b, 0xc8, 0x28, 0x89, 0x1d, 0x41, 0x44, 0x91, 0xf0, + 0xd4, 0x3b, 0x92, 0x3f, 0xef, 0x56, 0x83, 0xd0, 0xe1, 0x4a, 0xe5, 0x71, + 0xd8, 0xcc, 0xbb, 0xe2, 0x14, 0xa4, 0x03, 0x5b, 0x9b, 0x42, 0x1b, 0x40, + 0x43, 0x68, 0x4a, 0x12, 0x3b, 0x82, 0x46, 0x80, 0xaf, 0xab, 0xb4, 0xf6, + 0x5a, 0x14, 0xab, 0xaa, 0x93, 0x9e, 0xa8, 0xad, 0xd2, 0xc7, 0xd4, 0xd1, + 0x53, 0xc3, 0x3d, 0x55, 0x75, 0xc7, 0x7b, 0x82, 0xd7, 0x8e, 0x47, 0xc4, + 0x22, 0x3a, 0x51, 0x37, 0x25, 0x7b, 0xc8, 0xc9, 0x49, 0xf3, 0x9b, 0x88, + 0x00, 0x54, 0xa7, 0x3d, 0x81, 0xbd, 0xa3, 0xda, 0xe2, 0x69, 0xea, 0xf1, + 0x72, 0x83, 0x67, 0xb5, 0xca, 0xba, 0x5c, 0xa5, 0x37, 0x16, 0x14, 0x46, + 0x94, 0xf3, 0xef, 0x38, 0x74, 0x94, 0x21, 0x23, 0x64, 0x93, 0x54, 0xed, + 0x95, 0xc9, 0xb9, 0x0d, 0xfe, 0x6e, 0x6f, 0x75, 0x8c, 0xe4, 0x77, 0xa6, + 0xb6, 0x23, 0xdb, 0x62, 0x3a, 0x34, 0xb8, 0x70, 0x41, 0xda, 0x42, 0xc7, + 0x83, 0x8e, 0x1f, 0x3d, 0x43, 0xbc, 0x6d, 0x29, 0xfc, 0xda, 0xfb, 0x3a, + 0x14, 0x9d, 0x49, 0xe0, 0x8a, 0xd5, 0x55, 0x38, 0xe7, 0xa9, 0xa7, 0x2e, + 0x43, 0x6d, 0x3d, 0x8a, 0xb4, 0xd2, 0x02, 0x1b, 0x4e, 0x4b, 0x6d, 0x4a, + 0x52, 0x06, 0x82, 0x40, 0x78, 0x68, 0x0f, 0x65, 0x5e, 0x63, 0xba, 0xa8, + 0xec, 0xcb, 0xe3, 0x38, 0xb7, 0x7f, 0xfa, 0xcf, 0x6e, 0xfb, 0xe1, 0x57, + 0x88, 0xee, 0xad, 0x2f, 0x3e, 0xf3, 0xe4, 0x67, 0x67, 0xf7, 0x61, 0x45, + 0x14, 0x57, 0x29, 0xd4, 0x14, 0x99, 0xc7, 0x1f, 0x91, 0xec, 0xb7, 0xe8, + 0x89, 0x1f, 0xc0, 0x69, 0xce, 0x93, 0x38, 0xe3, 0xf2, 0x3d, 0x97, 0x7d, + 0x11, 0x23, 0xf8, 0x0d, 0x00, 0xdb, 0x11, 0x09, 0x4c, 0x56, 0x92, 0x90, + 0x00, 0x09, 0x1d, 0x00, 0xa8, 0x5c, 0xe7, 0x17, 0x81, 0x95, 0xd9, 0xc4, + 0x09, 0x6b, 0x76, 0x3b, 0xec, 0xb8, 0x99, 0x10, 0x66, 0xb2, 0x74, 0xf4, + 0x39, 0x09, 0xf8, 0x0e, 0xb6, 0x7f, 0x48, 0x1f, 0x0e, 0xe2, 0x36, 0x08, + 0x20, 0x9a, 0x9c, 0x8d, 0xf1, 0x76, 0xfe, 0x60, 0xfb, 0x2b, 0x61, 0x1b, + 0xa0, 0x11, 0xf1, 0x3c, 0xb6, 0x74, 0x7b, 0xab, 0x78, 0x96, 0x6c, 0xdb, + 0x30, 0xaf, 0xda, 0x3e, 0x47, 0x29, 0x03, 0x96, 0x35, 0xd9, 0x03, 0xf3, + 0xd9, 0x27, 0xb9, 0xc0, 0x3a, 0xa9, 0xa3, 0xe7, 0x0e, 0xa4, 0x6d, 0x3d, + 0x69, 0xbe, 0xe1, 0x12, 0x3d, 0xc2, 0x13, 0x91, 0x24, 0xa0, 0x2d, 0xa7, + 0x06, 0x88, 0xae, 0x3c, 0x9b, 0x1f, 0xb3, 0xe4, 0x96, 0x87, 0x2d, 0x57, + 0xb8, 0x2d, 0xcc, 0x88, 0xb2, 0x15, 0xc8, 0xad, 0x82, 0x85, 0x0e, 0xe5, + 0xa1, 0x43, 0xaa, 0x14, 0x3b, 0xc2, 0x92, 0x41, 0x07, 0xba, 0x93, 0xfb, + 0x4c, 0xd7, 0x05, 0x46, 0x9f, 0x4c, 0xbc, 0xd3, 0x1e, 0x47, 0x73, 0xad, + 0x80, 0x6e, 0xb1, 0x13, 0xd7, 0xe1, 0x27, 0xa2, 0x64, 0xa4, 0x00, 0x3a, + 0x8e, 0x57, 0x3d, 0x4b, 0x3d, 0x4d, 0x65, 0x15, 0x24, 0xe3, 0x25, 0x94, + 0xc1, 0x1d, 0x91, 0xe1, 0x17, 0x1b, 0x79, 0x53, 0xf0, 0x42, 0xa6, 0xc6, + 0xef, 0xf3, 0x47, 0xbe, 0x27, 0xda, 0x3c, 0x7d, 0xa2, 0x95, 0x08, 0x20, + 0x90, 0x41, 0x04, 0x74, 0x20, 0xd5, 0xd1, 0x8a, 0xe4, 0xf6, 0x2c, 0xa2, + 0x07, 0x97, 0x58, 0xae, 0x6c, 0x4d, 0x69, 0x27, 0x95, 0xc0, 0x82, 0x43, + 0x8c, 0xab, 0xf4, 0x5c, 0x41, 0xd2, 0x90, 0xaf, 0xd5, 0x50, 0x07, 0xd5, + 0x5e, 0xaf, 0x58, 0xed, 0xa2, 0xef, 0xb5, 0x4a, 0x8a, 0x90, 0xef, 0xf5, + 0xad, 0xf9, 0xab, 0xfe, 0xf1, 0xdf, 0xf5, 0xee, 0xbe, 0x46, 0xff, 0x00, + 0xd9, 0x58, 0xcd, 0xb9, 0xda, 0xcb, 0x1e, 0x8f, 0x8f, 0x93, 0x32, 0x95, + 0x3f, 0x21, 0x27, 0x06, 0x7d, 0xbb, 0xa5, 0xa2, 0x66, 0x3b, 0x24, 0xef, + 0x99, 0x25, 0x6c, 0x6f, 0xc0, 0xd2, 0x7c, 0xa6, 0x1c, 0x8d, 0x25, 0xc6, + 0x1d, 0x1a, 0x5b, 0x6a, 0x29, 0x22, 0xac, 0x04, 0x61, 0x13, 0xad, 0x77, + 0x26, 0x67, 0xda, 0x26, 0xa1, 0xde, 0xc9, 0x7b, 0xec, 0xdd, 0x1c, 0xaa, + 0x23, 0xc4, 0x6c, 0x74, 0x3f, 0xe1, 0x5c, 0x5c, 0x48, 0xb1, 0xc9, 0xf2, + 0xe4, 0x5c, 0xe3, 0x45, 0x71, 0x49, 0x79, 0x3e, 0xfc, 0x10, 0x92, 0xae, + 0x55, 0x7a, 0xf5, 0x5e, 0x55, 0xef, 0x67, 0x5c, 0xfd, 0x8d, 0x3a, 0xb0, + 0x7a, 0xa9, 0xed, 0xe7, 0x98, 0xbe, 0x38, 0xf2, 0x7f, 0xa1, 0x57, 0x17, + 0x8d, 0xc4, 0x8a, 0x28, 0x20, 0x82, 0x41, 0x04, 0x11, 0xe0, 0x68, 0xaf, + 0x9a, 0x33, 0x0a, 0x28, 0xa2, 0x80, 0x28, 0xa2, 0xbd, 0x34, 0xd3, 0x8f, + 0x2c, 0x21, 0xa6, 0xd6, 0xe2, 0x8f, 0x72, 0x52, 0x9d, 0x9a, 0x94, 0x9b, + 0xd9, 0x03, 0xcd, 0x15, 0x39, 0x6f, 0xc4, 0xaf, 0xf3, 0x48, 0x29, 0x80, + 0xb6, 0x50, 0x7f, 0x39, 0xff, 0x00, 0x33, 0x5f, 0x51, 0xeb, 0xfe, 0x14, + 0xd3, 0x69, 0xe1, 0xdb, 0x28, 0x21, 0x77, 0x49, 0xa5, 0xc3, 0xe2, 0xdb, + 0x03, 0x43, 0xfe, 0x63, 0xd4, 0xff, 0x00, 0x70, 0xaf, 0x4e, 0xdb, 0xb1, + 0xaf, 0x6e, 0x5f, 0x76, 0x9b, 0x4b, 0xcd, 0xec, 0xbf, 0x5f, 0xd8, 0xb2, + 0x83, 0x65, 0x7d, 0x16, 0x3b, 0xf2, 0x9f, 0x4b, 0x11, 0x99, 0x5b, 0xce, + 0xab, 0xb9, 0x28, 0x4e, 0xc9, 0xa7, 0xbc, 0x67, 0x02, 0x51, 0x52, 0x24, + 0xde, 0xd5, 0xca, 0x06, 0x88, 0x8e, 0x85, 0x75, 0x3f, 0x38, 0x8f, 0xb0, + 0x7f, 0x7d, 0x3b, 0xdb, 0x2d, 0x90, 0x2d, 0x8c, 0xf6, 0x50, 0x62, 0xb6, + 0xc2, 0x7c, 0x4a, 0x47, 0x53, 0xed, 0x3d, 0xe7, 0xeb, 0xae, 0xa2, 0x48, + 0xaf, 0xad, 0xec, 0xff, 0x00, 0x66, 0x28, 0xd1, 0x6a, 0x77, 0x0f, 0x53, + 0xf2, 0xe9, 0xfe, 0x7f, 0xdd, 0x8d, 0x63, 0x4d, 0x2e, 0x41, 0xb4, 0x25, + 0xb6, 0xd2, 0xda, 0x12, 0x12, 0x94, 0x80, 0x12, 0x07, 0x70, 0x02, 0xb9, + 0x6e, 0xd7, 0x18, 0x36, 0x9b, 0x74, 0x8b, 0x95, 0xca, 0x5b, 0x10, 0xe1, + 0x46, 0x6c, 0xb8, 0xfb, 0xef, 0x2c, 0x25, 0x0d, 0xa4, 0x77, 0x92, 0x4f, + 0x41, 0x4b, 0x79, 0x46, 0x7d, 0x6c, 0xb5, 0x5c, 0x7d, 0xc4, 0xb5, 0xc7, + 0x7f, 0x20, 0xc8, 0x54, 0x3c, 0xcb, 0x5d, 0xbf, 0x4b, 0x71, 0x1e, 0x85, + 0x3c, 0xb2, 0x79, 0x18, 0x47, 0x77, 0x9c, 0xb2, 0x3d, 0x41, 0x47, 0xa5, + 0x70, 0x5b, 0xf0, 0xeb, 0xa5, 0xfe, 0xe6, 0xc5, 0xef, 0x88, 0x72, 0x62, + 0xcd, 0x5c, 0x77, 0x03, 0xb0, 0xac, 0xb1, 0x49, 0x30, 0x21, 0xac, 0x1f, + 0x35, 0x6a, 0xe6, 0x00, 0xbe, 0xe8, 0xfd, 0x25, 0x00, 0x94, 0x9f, 0x82, + 0x90, 0x7a, 0xd7, 0xd4, 0xf0, 0x68, 0x72, 0x44, 0x8b, 0x33, 0x89, 0x37, + 0x18, 0xd7, 0x7b, 0xc4, 0x57, 0xe0, 0xe2, 0x11, 0x1e, 0x43, 0xf6, 0xeb, + 0x73, 0xc9, 0x28, 0x76, 0xe8, 0xea, 0x4e, 0xd3, 0x21, 0xf4, 0x9e, 0xa9, + 0x65, 0x24, 0x02, 0xdb, 0x67, 0xaa, 0x88, 0xe7, 0x5e, 0x86, 0x93, 0x4e, + 0xf7, 0x3b, 0x34, 0x19, 0xe9, 0xdb, 0x8d, 0xf2, 0x39, 0xe0, 0xb4, 0x74, + 0x23, 0xff, 0x00, 0x35, 0x22, 0x00, 0xd5, 0x1a, 0x1a, 0xa9, 0x8c, 0x9c, + 0x5e, 0x51, 0x59, 0x45, 0x49, 0x61, 0x95, 0x17, 0x12, 0xec, 0xd2, 0x6d, + 0xd2, 0x31, 0x67, 0x54, 0xa4, 0xb8, 0xc9, 0xca, 0x2d, 0xa0, 0x2c, 0x74, + 0x23, 0xdf, 0x87, 0x78, 0xab, 0x78, 0x77, 0x52, 0x27, 0x19, 0x47, 0xf3, + 0x2c, 0x5f, 0xf6, 0xaa, 0xd9, 0xf7, 0xe2, 0x9e, 0xea, 0xd5, 0x2a, 0x4a, + 0xa3, 0xcc, 0x88, 0xa7, 0x4d, 0x53, 0x58, 0x41, 0x45, 0x79, 0x24, 0x83, + 0xa0, 0x01, 0xfa, 0xe8, 0xaa, 0x17, 0x3d, 0x52, 0x67, 0x1c, 0x7e, 0x47, + 0xb2, 0xef, 0xa2, 0x24, 0x7f, 0x01, 0xa7, 0x3a, 0x4c, 0xe3, 0x8f, 0xc8, + 0xf6, 0x5d, 0xf4, 0x44, 0x8f, 0xe0, 0x34, 0x03, 0x7c, 0x6f, 0x8b, 0xb7, + 0xf3, 0x07, 0xd9, 0x5b, 0x2b, 0x5c, 0x6f, 0x8b, 0xb7, 0xf3, 0x07, 0xd9, + 0x5b, 0x28, 0x03, 0x5d, 0x68, 0xd5, 0x14, 0x50, 0x0a, 0xd9, 0x3e, 0x07, + 0x8e, 0xdf, 0xa7, 0x7b, 0xa6, 0xe4, 0x77, 0xad, 0xf7, 0x74, 0x8d, 0x22, + 0xe9, 0x6e, 0x78, 0xc6, 0x96, 0x9f, 0x57, 0x68, 0x8d, 0x15, 0x0f, 0xd5, + 0x5f, 0x32, 0x7d, 0x55, 0x14, 0x22, 0xf1, 0x37, 0x1e, 0xf8, 0x94, 0xfb, + 0x5e, 0x63, 0x0d, 0x00, 0xe9, 0xa9, 0xc3, 0xc8, 0x66, 0x81, 0xe0, 0x03, + 0xa8, 0x49, 0x69, 0x67, 0xda, 0x84, 0x7b, 0x7c, 0x69, 0xf7, 0x5d, 0x77, + 0x47, 0x28, 0xa0, 0x11, 0x07, 0x13, 0x6d, 0xd6, 0xf0, 0x53, 0x95, 0xd8, + 0x72, 0x1c, 0x64, 0xa0, 0x00, 0xa7, 0x66, 0x40, 0x53, 0xb1, 0xb7, 0xea, + 0x7d, 0x9e, 0x76, 0xf5, 0xeb, 0x51, 0x4f, 0xd5, 0x4c, 0xb6, 0x2c, 0x9f, + 0x1d, 0xbf, 0xa4, 0xaa, 0xc7, 0x7d, 0xb6, 0x5c, 0xf4, 0x36, 0x44, 0x59, + 0x48, 0x70, 0x81, 0xeb, 0x09, 0x24, 0x8a, 0x96, 0xd5, 0x2e, 0x5f, 0xf0, + 0x2c, 0x2a, 0xfc, 0xb5, 0x39, 0x77, 0xc5, 0xad, 0x12, 0xde, 0x57, 0x7b, + 0xca, 0x8a, 0x80, 0xef, 0xd4, 0xb0, 0x02, 0x87, 0xd4, 0x68, 0x09, 0xc9, + 0x31, 0x22, 0x49, 0x1f, 0xce, 0x62, 0x30, 0xf8, 0xff, 0x00, 0x68, 0xd8, + 0x57, 0xdb, 0x51, 0xee, 0xe3, 0x36, 0x07, 0xb7, 0xcf, 0x69, 0x8c, 0x3e, + 0x6a, 0x79, 0x7e, 0xca, 0x5b, 0x5f, 0x0b, 0xac, 0x6c, 0x21, 0x2d, 0xd9, + 0x6f, 0x59, 0x55, 0x91, 0x29, 0xee, 0x4c, 0x2b, 0xe4, 0x8e, 0x41, 0xff, + 0x00, 0x03, 0x8a, 0x5a, 0x7f, 0xc2, 0xa1, 0x32, 0xf0, 0x30, 0xb8, 0x61, + 0xdb, 0x9f, 0x1c, 0xa6, 0xd9, 0x5a, 0xd7, 0x98, 0x2e, 0xc8, 0x82, 0xea, + 0x97, 0xec, 0x05, 0xa4, 0xa9, 0x5f, 0x56, 0xeb, 0x1a, 0x96, 0xd4, 0x6a, + 0x78, 0xe0, 0x9f, 0xc5, 0x26, 0x46, 0x10, 0xee, 0xac, 0x37, 0x1b, 0x27, + 0xad, 0xb0, 0x7d, 0x4f, 0x38, 0x3f, 0xfe, 0xd5, 0x94, 0x61, 0xf8, 0xda, + 0x4f, 0x4b, 0x62, 0x4f, 0xb5, 0xd5, 0x9f, 0xb5, 0x55, 0x4e, 0x5a, 0xb8, + 0x99, 0x16, 0x74, 0xd1, 0x16, 0x0f, 0xe1, 0x11, 0x8b, 0x29, 0xd3, 0xf0, + 0x53, 0x70, 0xb0, 0x25, 0x90, 0xaf, 0x62, 0x94, 0xeb, 0x60, 0xfd, 0x54, + 0xfd, 0x0a, 0xdb, 0xc4, 0xc9, 0xb1, 0xd1, 0x26, 0x1f, 0x12, 0x31, 0xb9, + 0x0c, 0x2c, 0x6d, 0x0e, 0x35, 0x8f, 0x73, 0x21, 0x43, 0xd2, 0x08, 0x93, + 0xa3, 0x58, 0x7f, 0x4e, 0xb3, 0xff, 0x00, 0xaa, 0x3f, 0xf9, 0x5f, 0xc0, + 0xd2, 0xbc, 0x87, 0x06, 0x31, 0xeb, 0x13, 0x3d, 0x5b, 0xb4, 0xc4, 0xd8, + 0xf1, 0x53, 0x41, 0x5f, 0x6e, 0xea, 0x41, 0x96, 0x9a, 0x65, 0x1c, 0xac, + 0xb4, 0xdb, 0x49, 0xf4, 0x21, 0x3a, 0x14, 0x88, 0x31, 0xae, 0x26, 0x39, + 0xf9, 0x7e, 0x27, 0xc5, 0x6f, 0xff, 0x00, 0xc7, 0xc7, 0x1a, 0x4e, 0xbf, + 0xe7, 0x71, 0x75, 0xb5, 0x58, 0x3e, 0x41, 0x2d, 0xbe, 0x5b, 0xa7, 0x13, + 0xb2, 0x97, 0x7f, 0xfc, 0x36, 0xe2, 0x45, 0x1f, 0xf4, 0x33, 0xbf, 0xf1, + 0xae, 0x8a, 0x74, 0x29, 0xd3, 0xf0, 0x45, 0x2f, 0x82, 0x27, 0x08, 0x79, + 0x27, 0xd5, 0x50, 0xf9, 0x56, 0x51, 0x60, 0xc5, 0xa1, 0x22, 0x66, 0x43, + 0x75, 0x8d, 0x6f, 0x65, 0xc5, 0x72, 0x34, 0x5d, 0x57, 0x9c, 0xe2, 0xb5, + 0xbe, 0x54, 0x24, 0x75, 0x51, 0xd7, 0x80, 0x04, 0xd2, 0x3d, 0x8e, 0xcc, + 0xdd, 0x87, 0x8c, 0xf0, 0x6d, 0x71, 0x2f, 0x77, 0xe9, 0xe3, 0xf9, 0x3f, + 0x26, 0x4c, 0xb1, 0x71, 0xb9, 0xbd, 0x27, 0x9b, 0x6f, 0xb0, 0x86, 0xcf, + 0x2a, 0x89, 0x48, 0xf8, 0x2e, 0x6b, 0x40, 0x78, 0xd4, 0x8c, 0x12, 0x32, + 0x6e, 0x31, 0xbf, 0x3d, 0xb0, 0x95, 0xdb, 0xb1, 0x58, 0x8b, 0x84, 0xdb, + 0x9e, 0x06, 0x7c, 0x8e, 0x55, 0x3a, 0x12, 0x7c, 0x7b, 0x36, 0x92, 0xda, + 0x4f, 0xa0, 0xba, 0x47, 0x78, 0xad, 0x41, 0xb1, 0x59, 0xf5, 0xd2, 0xea, + 0x4a, 0x31, 0x0c, 0x22, 0xf5, 0x73, 0x04, 0x8e, 0x59, 0x77, 0x04, 0x7b, + 0x9b, 0x17, 0x94, 0xfe, 0x76, 0xde, 0x1d, 0xaa, 0x87, 0xcd, 0x6c, 0xd7, + 0x93, 0x8a, 0xe6, 0x39, 0x01, 0xde, 0x5f, 0x96, 0x98, 0x90, 0xd4, 0x7c, + 0xeb, 0x66, 0x3e, 0x95, 0x46, 0x42, 0x87, 0x5e, 0x8b, 0x90, 0xad, 0xbc, + 0xa0, 0x47, 0x7f, 0x2f, 0x67, 0xec, 0xa7, 0xd0, 0x01, 0x1e, 0x04, 0x1f, + 0x55, 0x67, 0x42, 0x80, 0x8a, 0xc6, 0x71, 0xdb, 0x1e, 0x37, 0x6d, 0x16, + 0xfb, 0x0d, 0xae, 0x35, 0xba, 0x30, 0x3c, 0xca, 0x43, 0x28, 0xd7, 0x3a, + 0xbc, 0x54, 0xa3, 0xde, 0xa5, 0x1f, 0x15, 0x12, 0x49, 0xf4, 0xd4, 0xae, + 0xba, 0x6a, 0x8a, 0x28, 0x02, 0x8a, 0x28, 0xa0, 0x11, 0x38, 0xcb, 0xf1, + 0x2c, 0x5f, 0xf6, 0xaa, 0xd9, 0xf7, 0xe2, 0x9e, 0xe9, 0x13, 0x8c, 0xbf, + 0x12, 0xc5, 0xff, 0x00, 0x6a, 0xad, 0x9f, 0x7e, 0x29, 0xee, 0x80, 0xc1, + 0xef, 0xdd, 0x14, 0x1a, 0x2a, 0x51, 0x56, 0xcc, 0xd2, 0x67, 0x1c, 0x7e, + 0x47, 0xb2, 0xef, 0xa2, 0x24, 0x7f, 0x01, 0xa7, 0x3a, 0x4c, 0xe3, 0x8f, + 0xc8, 0xf6, 0x5d, 0xf4, 0x44, 0x8f, 0xe0, 0x35, 0x05, 0x86, 0xf8, 0xdf, + 0x17, 0x6f, 0xe6, 0x0f, 0xb2, 0xb6, 0x56, 0xb8, 0xdf, 0x17, 0x6f, 0xe6, + 0x0f, 0xb2, 0xb6, 0x50, 0x05, 0x14, 0x56, 0x09, 0x3b, 0xee, 0xa0, 0x33, + 0x58, 0x2a, 0xd7, 0x7d, 0x28, 0xe7, 0x3c, 0x48, 0xc3, 0xf0, 0xd7, 0x51, + 0x16, 0xf5, 0x77, 0x47, 0xba, 0x2e, 0xfe, 0x42, 0xdb, 0x15, 0xb5, 0x48, + 0x98, 0xf1, 0xf0, 0x09, 0x65, 0xb0, 0x56, 0x77, 0xd3, 0xa9, 0x00, 0x7a, + 0xe9, 0x64, 0xe4, 0x1c, 0x56, 0xcc, 0x55, 0xc9, 0x8c, 0xe3, 0x4c, 0xe1, + 0x16, 0xc5, 0x74, 0xf7, 0x4f, 0x21, 0x48, 0x7a, 0x62, 0x87, 0xfb, 0x38, + 0x8d, 0xab, 0x49, 0x3f, 0xef, 0x56, 0x3d, 0x94, 0x05, 0x8d, 0x7d, 0xbd, + 0x5a, 0x6c, 0x36, 0xc7, 0x6e, 0x77, 0xbb, 0x94, 0x4b, 0x6c, 0x26, 0x46, + 0xdc, 0x91, 0x29, 0xd4, 0xb6, 0x84, 0xff, 0x00, 0xc4, 0xa2, 0x05, 0x57, + 0x6b, 0xe2, 0xe4, 0x8b, 0xfa, 0x8b, 0x3c, 0x34, 0xc2, 0xaf, 0x19, 0x60, + 0x3b, 0xd5, 0xc5, 0xd1, 0xee, 0x7d, 0xb8, 0x6b, 0xd0, 0xfb, 0xc3, 0x6b, + 0xeb, 0xfa, 0x09, 0x57, 0xb6, 0xba, 0xac, 0x7c, 0x1d, 0xc7, 0xd3, 0x76, + 0x66, 0xff, 0x00, 0x97, 0xcf, 0xb8, 0x66, 0xb7, 0xc6, 0xba, 0xb7, 0x2a, + 0xf2, 0xb0, 0xb6, 0x59, 0x3f, 0xec, 0xa3, 0xa4, 0x06, 0x9b, 0x1f, 0xf0, + 0x93, 0xeb, 0xab, 0x19, 0x29, 0x4a, 0x3c, 0xd4, 0x80, 0x00, 0x1d, 0x00, + 0x1d, 0x05, 0x01, 0x56, 0x1c, 0x3b, 0x89, 0xb9, 0x67, 0xfa, 0xeb, 0x9d, + 0x33, 0x62, 0x80, 0xb1, 0xe7, 0xdb, 0x31, 0x56, 0x94, 0xd2, 0xd4, 0x35, + 0xf0, 0x57, 0x2d, 0xcd, 0xb9, 0xed, 0xe4, 0x4a, 0x37, 0x4c, 0x18, 0x8f, + 0x0a, 0x38, 0x77, 0x8b, 0xc8, 0x13, 0x2d, 0x38, 0xa5, 0xbc, 0x4e, 0xef, + 0x33, 0x64, 0xa4, 0xc9, 0x90, 0x4f, 0xa4, 0xba, 0xe9, 0x52, 0xf7, 0xf5, + 0xd3, 0xa8, 0x03, 0x54, 0x6f, 0xc3, 0x54, 0x07, 0x0d, 0xda, 0xc9, 0x67, + 0xbb, 0xb1, 0xe4, 0xf7, 0x6b, 0x54, 0x1b, 0x83, 0x3a, 0xd7, 0x67, 0x2a, + 0x3a, 0x1d, 0x4e, 0xbd, 0x1a, 0x50, 0x22, 0xaa, 0x9c, 0xb3, 0x0d, 0x77, + 0x86, 0x12, 0x3f, 0x96, 0xdc, 0x31, 0xb7, 0x3e, 0xd4, 0x16, 0x17, 0xcf, + 0x7d, 0xc6, 0xe2, 0x28, 0xf9, 0x3c, 0xd8, 0xff, 0x00, 0x9e, 0xeb, 0x2d, + 0x1e, 0x88, 0x7d, 0x03, 0xa8, 0xe5, 0xd0, 0x50, 0x04, 0x6b, 0x75, 0x70, + 0xbc, 0xf3, 0x6c, 0xb4, 0xb7, 0x5d, 0x71, 0x08, 0x6d, 0x00, 0x95, 0x29, + 0x6a, 0xd0, 0x48, 0x1e, 0x24, 0xf8, 0x57, 0x05, 0x9a, 0xf9, 0x65, 0xbd, + 0xa1, 0xc7, 0xac, 0xd7, 0x68, 0x17, 0x24, 0x34, 0xa0, 0x97, 0x15, 0x16, + 0x4a, 0x1d, 0x4a, 0x0f, 0xa0, 0xf2, 0x93, 0xa3, 0x40, 0x18, 0xcd, 0xf2, + 0xd7, 0x91, 0xe3, 0xf0, 0x6f, 0xb6, 0x59, 0xad, 0xcc, 0xb7, 0x4d, 0x64, + 0x3c, 0xc3, 0xe8, 0xee, 0x52, 0x4f, 0xd8, 0x7c, 0x08, 0x3d, 0xc4, 0x1a, + 0x90, 0xd9, 0x1b, 0xd8, 0xaa, 0x93, 0x07, 0xe5, 0xe1, 0xd7, 0x16, 0xae, + 0x58, 0x33, 0xde, 0xf3, 0x60, 0xc9, 0x9c, 0x76, 0xed, 0x8f, 0x1e, 0xe4, + 0x35, 0x23, 0xbe, 0x54, 0x51, 0xd3, 0x43, 0xaf, 0xbe, 0xa4, 0x0f, 0x05, + 0x2b, 0xd1, 0x56, 0x85, 0xf2, 0x1b, 0xd7, 0x1b, 0x34, 0xeb, 0x7c, 0x79, + 0x8e, 0xc2, 0x7a, 0x4c, 0x77, 0x19, 0x44, 0x96, 0xc6, 0xd6, 0xca, 0x94, + 0x92, 0x02, 0xc0, 0xf4, 0x8d, 0xef, 0xea, 0xa0, 0x29, 0xd8, 0x99, 0x2d, + 0xc2, 0xe5, 0x95, 0xe4, 0x17, 0x1c, 0x6c, 0xb7, 0x2a, 0xfb, 0x90, 0x3a, + 0x9b, 0x6d, 0x88, 0x2c, 0x05, 0xb7, 0x0e, 0xdf, 0x14, 0xad, 0x0e, 0x4e, + 0x70, 0x8e, 0xe6, 0x8b, 0xca, 0x78, 0xa4, 0x7f, 0x48, 0x42, 0x12, 0x37, + 0xbd, 0x8b, 0x5f, 0x0d, 0xc7, 0x60, 0xe3, 0x18, 0xe4, 0x5b, 0x34, 0x12, + 0xb5, 0xa1, 0x90, 0x54, 0xeb, 0xce, 0x1d, 0xb8, 0xfb, 0xaa, 0x3c, 0xce, + 0x3a, 0xb3, 0xe2, 0xb5, 0x28, 0x95, 0x13, 0xe9, 0x3e, 0x8d, 0x0a, 0x89, + 0xe1, 0x6e, 0x01, 0x69, 0xc0, 0xac, 0x29, 0x83, 0x0d, 0xc7, 0x26, 0x4d, + 0x75, 0x0d, 0x89, 0x93, 0xde, 0xfc, 0xa4, 0x82, 0x84, 0xf2, 0xa0, 0x7a, + 0x12, 0x84, 0x8e, 0x89, 0x40, 0xe8, 0x3a, 0x9e, 0xa4, 0x92, 0x5b, 0xc7, + 0x41, 0x40, 0x14, 0x51, 0x45, 0x00, 0x51, 0x45, 0x14, 0x01, 0x45, 0x14, + 0x50, 0x08, 0x9c, 0x65, 0xf8, 0x96, 0x2f, 0xfb, 0x55, 0x6c, 0xfb, 0xf1, + 0x4f, 0x74, 0x89, 0xc6, 0x5f, 0x89, 0x62, 0xff, 0x00, 0xb5, 0x56, 0xcf, + 0xbf, 0x14, 0xf7, 0x40, 0x79, 0x51, 0xeb, 0x45, 0x64, 0x8d, 0x9e, 0xf3, + 0x45, 0x0a, 0xb4, 0xf2, 0x66, 0x93, 0x38, 0xe3, 0xf2, 0x3d, 0x97, 0x7d, + 0x11, 0x23, 0xf8, 0x0d, 0x39, 0xd2, 0x67, 0x1c, 0x7e, 0x47, 0xb2, 0xef, + 0xa2, 0x24, 0x7f, 0x01, 0xa1, 0x61, 0xbe, 0x37, 0xc5, 0xdb, 0xf9, 0x83, + 0xec, 0xaf, 0x64, 0xfb, 0x2a, 0x2e, 0xf5, 0x7b, 0xb5, 0xe3, 0xb8, 0xeb, + 0xd7, 0x9b, 0xd4, 0xe6, 0x60, 0xdb, 0xe2, 0x32, 0x1c, 0x7d, 0xf7, 0x4e, + 0x92, 0x81, 0xd0, 0x0f, 0x59, 0x24, 0x90, 0x00, 0x1d, 0x49, 0x20, 0x0d, + 0x93, 0x55, 0xea, 0xa6, 0x71, 0x03, 0x89, 0x40, 0xa2, 0xd6, 0x64, 0x60, + 0x98, 0xaa, 0xf5, 0xb9, 0x6f, 0x37, 0xfe, 0x98, 0x98, 0x8f, 0x1e, 0xcd, + 0xb3, 0xd2, 0x32, 0x4f, 0x82, 0x95, 0xb5, 0xeb, 0x44, 0x04, 0xee, 0x80, + 0x60, 0xce, 0xb8, 0x9d, 0x8d, 0x62, 0x93, 0x5b, 0xb5, 0x28, 0xca, 0xbc, + 0xdf, 0x9e, 0xd8, 0x62, 0xcb, 0x69, 0x6b, 0xca, 0x66, 0xb9, 0xeb, 0x28, + 0x07, 0xcc, 0x4f, 0xeb, 0x2c, 0xa4, 0x7a, 0xfa, 0x54, 0x0f, 0xb9, 0x5c, + 0x5b, 0xcd, 0x00, 0x55, 0xea, 0xf1, 0x13, 0x01, 0xb4, 0x3a, 0x36, 0x61, + 0x5a, 0x08, 0x93, 0x72, 0x52, 0x4f, 0xe6, 0xb9, 0x21, 0x63, 0x91, 0xa5, + 0x6b, 0xc5, 0xb4, 0xa8, 0x8d, 0xfc, 0x2a, 0x73, 0xc1, 0xf0, 0xac, 0x5f, + 0x0c, 0xb7, 0x18, 0x98, 0xe5, 0xa1, 0x88, 0x7d, 0xa7, 0x57, 0xdf, 0xd7, + 0x33, 0xf2, 0x15, 0xbd, 0x95, 0x3a, 0xe9, 0xda, 0xdc, 0x56, 0xc9, 0xea, + 0xa2, 0x69, 0x87, 0x42, 0x80, 0x56, 0xc1, 0xb0, 0x0c, 0x4f, 0x0c, 0x69, + 0xc3, 0x60, 0xb4, 0x32, 0xcc, 0xb7, 0xb6, 0x64, 0x4d, 0x74, 0x97, 0x65, + 0x48, 0x51, 0xea, 0x4b, 0x8f, 0x2b, 0x6b, 0x56, 0xcf, 0xa4, 0xeb, 0xd0, + 0x05, 0x77, 0xe5, 0xf9, 0x56, 0x3b, 0x88, 0xda, 0xcd, 0xcf, 0x24, 0xbc, + 0x44, 0xb6, 0x45, 0xdf, 0x2a, 0x56, 0xf2, 0xf4, 0xa7, 0x15, 0xfa, 0x28, + 0x48, 0xf3, 0x96, 0xaf, 0xd5, 0x48, 0x24, 0xfa, 0x2b, 0xb2, 0xf0, 0xdd, + 0xcd, 0xd4, 0x47, 0x4d, 0xae, 0x54, 0x78, 0xea, 0x12, 0x5b, 0x53, 0xe5, + 0xe6, 0x4b, 0x9c, 0xcc, 0x83, 0xe7, 0xa5, 0x3a, 0x50, 0xd2, 0x88, 0xee, + 0x27, 0x60, 0x7a, 0x0d, 0x7b, 0x93, 0x6f, 0x81, 0x2e, 0x5c, 0x69, 0x72, + 0xa0, 0xc6, 0x7a, 0x4c, 0x55, 0x15, 0x47, 0x75, 0xc6, 0x92, 0xa5, 0xb2, + 0x4f, 0x42, 0x52, 0x48, 0xda, 0x77, 0xea, 0xa0, 0x12, 0x71, 0x9c, 0xdf, + 0x27, 0xcb, 0x2f, 0xd1, 0x95, 0x63, 0xc3, 0x26, 0x5b, 0xf1, 0x94, 0xa8, + 0x99, 0x17, 0x4b, 0xde, 0xe3, 0x3b, 0x21, 0x3a, 0xe8, 0x23, 0xc7, 0xf8, + 0x7d, 0x49, 0x07, 0x99, 0xce, 0x51, 0xaf, 0x0a, 0x71, 0x9b, 0x69, 0x89, + 0x32, 0xed, 0x6f, 0xbb, 0x3c, 0x5f, 0x12, 0x60, 0x07, 0x7b, 0x00, 0x87, + 0x96, 0x94, 0x7b, 0xe2, 0x42, 0x54, 0x54, 0x90, 0x74, 0xae, 0x83, 0xa7, + 0x30, 0x3a, 0xeb, 0x50, 0x39, 0x7f, 0x11, 0x71, 0x1c, 0x5e, 0x4f, 0x90, + 0x4e, 0xb9, 0xf9, 0x4d, 0xd5, 0x63, 0x6d, 0xdb, 0x20, 0xb6, 0x64, 0xcc, + 0x70, 0xeb, 0x63, 0x4d, 0x23, 0x6a, 0x03, 0xf5, 0x95, 0xa4, 0xfa, 0xe9, + 0x32, 0xe3, 0x96, 0x71, 0x13, 0x26, 0xda, 0x2d, 0xb1, 0xa3, 0x61, 0x56, + 0xe5, 0x7f, 0x4d, 0x24, 0x22, 0x65, 0xc5, 0x43, 0xd4, 0xd8, 0x3d, 0x93, + 0x47, 0xe7, 0x17, 0x08, 0xf4, 0x78, 0x55, 0xe1, 0x4e, 0x53, 0xf0, 0xa2, + 0x93, 0xa9, 0x18, 0x78, 0x99, 0x67, 0x64, 0x79, 0x05, 0x8f, 0x1a, 0xb5, + 0xae, 0xe5, 0x7f, 0xba, 0xc2, 0xb5, 0x42, 0x6f, 0xbd, 0xe9, 0x6f, 0xa5, + 0xb4, 0x7b, 0x01, 0x27, 0xa9, 0xf5, 0x0e, 0xb5, 0x40, 0x66, 0x9f, 0x85, + 0x35, 0xad, 0x4d, 0xb8, 0xd6, 0x01, 0x66, 0x55, 0xdf, 0x5c, 0xc9, 0xf7, + 0x42, 0x66, 0xda, 0x8e, 0x08, 0x3a, 0x25, 0x28, 0x1e, 0x7a, 0x87, 0xb7, + 0x93, 0xeb, 0x15, 0x3a, 0xce, 0x03, 0x89, 0xcc, 0xf2, 0xa1, 0x94, 0x44, + 0x93, 0x92, 0xbb, 0x3a, 0x3a, 0xa3, 0xc9, 0x9d, 0x75, 0x78, 0xc8, 0x92, + 0x94, 0x2b, 0xc5, 0xb5, 0x2b, 0xa3, 0x5a, 0xef, 0x01, 0x01, 0x35, 0xf2, + 0xb7, 0x10, 0x30, 0xeb, 0xcf, 0xe0, 0xff, 0x00, 0xc4, 0x98, 0xee, 0xa5, + 0x5e, 0xe9, 0x62, 0xd7, 0x25, 0x15, 0x42, 0x98, 0xa6, 0xd2, 0xa2, 0xe3, + 0x40, 0x8e, 0x61, 0xd4, 0x69, 0x0f, 0xa0, 0x11, 0xd4, 0x68, 0xf5, 0x04, + 0x6b, 0x7d, 0x34, 0x9d, 0x19, 0x53, 0x6b, 0x51, 0x48, 0x56, 0x55, 0x13, + 0xd2, 0x36, 0xe4, 0x57, 0x2c, 0xeb, 0x88, 0x6e, 0x89, 0x19, 0x9d, 0xfe, + 0x6c, 0x88, 0x0b, 0x21, 0x4d, 0xc0, 0x1e, 0xf1, 0x11, 0x43, 0x7f, 0x9a, + 0xd2, 0x01, 0x2e, 0xfb, 0x40, 0x77, 0x5e, 0x24, 0x77, 0xd3, 0x9e, 0x26, + 0xfc, 0xdc, 0x32, 0x74, 0x5c, 0xb6, 0xd7, 0xda, 0x49, 0x55, 0x95, 0xb0, + 0x6e, 0x6d, 0x34, 0x94, 0xa4, 0x3d, 0x6d, 0xd0, 0x4b, 0x88, 0x3a, 0x51, + 0x0a, 0x28, 0x00, 0x2d, 0x03, 0x99, 0x44, 0x14, 0x7c, 0x14, 0x6e, 0xba, + 0x20, 0x45, 0x81, 0x21, 0xbf, 0x2d, 0x66, 0x42, 0x66, 0x25, 0xd4, 0x25, + 0xe5, 0x3c, 0x09, 0x29, 0x29, 0x50, 0x04, 0x29, 0x44, 0xe8, 0x9e, 0x84, + 0x69, 0x4b, 0xec, 0xfe, 0x79, 0xa6, 0x2b, 0x25, 0xb8, 0xb8, 0xa6, 0xa4, + 0x06, 0x10, 0x88, 0xa1, 0xc4, 0xee, 0x43, 0x9c, 0x9c, 0xbd, 0x7f, 0x45, + 0x4a, 0x1c, 0xbc, 0xde, 0xb4, 0x25, 0x64, 0xef, 0xf2, 0x86, 0xb7, 0x8d, + 0x14, 0xd6, 0x0e, 0x79, 0x56, 0x79, 0xc9, 0x6c, 0x71, 0x5b, 0x19, 0x6f, + 0x3f, 0xe1, 0xfb, 0x4e, 0xd8, 0xa6, 0x36, 0x9b, 0xa4, 0x62, 0xdd, 0xdb, + 0x1f, 0x9e, 0xda, 0xb6, 0x1b, 0x92, 0x81, 0xce, 0xd2, 0xc2, 0xbf, 0x41, + 0x40, 0xf2, 0x9f, 0x4a, 0x56, 0x6a, 0x53, 0x85, 0x99, 0x6b, 0x19, 0xce, + 0x05, 0x6b, 0xc9, 0x1a, 0x6b, 0xb0, 0x72, 0x4b, 0x5c, 0xb2, 0xa3, 0x9e, + 0xf6, 0x24, 0x20, 0x94, 0x3a, 0xd9, 0x1f, 0xaa, 0xb4, 0xa8, 0x75, 0xf0, + 0xd1, 0xf1, 0xa4, 0xee, 0x09, 0x5c, 0x06, 0x3f, 0x73, 0x9b, 0xc3, 0x49, + 0x4e, 0x28, 0xb1, 0x15, 0x06, 0x75, 0x85, 0x6b, 0x3d, 0x5c, 0x84, 0xa5, + 0x79, 0xcc, 0xef, 0xc4, 0xb2, 0xe1, 0xd7, 0x7f, 0xc0, 0x52, 0x3d, 0x15, + 0xaa, 0x12, 0x87, 0x0d, 0x78, 0xd8, 0xfc, 0x27, 0x13, 0xd9, 0xe3, 0x19, + 0xe4, 0x8e, 0xda, 0x2a, 0x87, 0xc0, 0x8b, 0x76, 0x08, 0xf7, 0xc4, 0x1f, + 0x40, 0x7d, 0x29, 0xe6, 0x1f, 0xac, 0x92, 0x3c, 0x6b, 0x8e, 0x51, 0x71, + 0x78, 0x67, 0x64, 0x64, 0xa4, 0xb2, 0x8b, 0x7c, 0x74, 0x14, 0x56, 0x12, + 0x76, 0x90, 0x6b, 0x35, 0x52, 0xc1, 0x45, 0x14, 0x50, 0x05, 0x14, 0x51, + 0x40, 0x1d, 0x68, 0xa2, 0x8a, 0x01, 0x13, 0x8c, 0xbf, 0x12, 0xc5, 0xff, + 0x00, 0x6a, 0xad, 0x9f, 0x7e, 0x29, 0xee, 0x91, 0x38, 0xcb, 0xf1, 0x2c, + 0x5f, 0xf6, 0xaa, 0xd9, 0xf7, 0xe2, 0x9e, 0xe8, 0x02, 0x8a, 0xf2, 0xa2, + 0x41, 0xe8, 0x07, 0xd6, 0x75, 0x45, 0x01, 0xea, 0x93, 0x38, 0xe3, 0xbf, + 0xc4, 0xfe, 0x5c, 0x3f, 0xf4, 0x89, 0x1a, 0xff, 0x00, 0x90, 0xd3, 0x9d, + 0x26, 0x71, 0xc7, 0xe4, 0x7b, 0x2e, 0xfa, 0x22, 0x47, 0xf0, 0x1a, 0x02, + 0xbe, 0x9f, 0x7f, 0x63, 0x27, 0xe2, 0x52, 0xfd, 0xd2, 0x61, 0x4f, 0xdb, + 0xac, 0x37, 0x37, 0x2d, 0xb6, 0x78, 0xab, 0xd7, 0x66, 0x67, 0x30, 0xca, + 0x5c, 0x7e, 0x63, 0xa9, 0x3d, 0xe5, 0x1c, 0xe8, 0x43, 0x60, 0xef, 0x44, + 0x95, 0x0e, 0xa7, 0xa5, 0xdb, 0x05, 0x96, 0xd9, 0x8a, 0x84, 0xa0, 0xa8, + 0x82, 0x36, 0x54, 0x7b, 0xd4, 0x4f, 0x79, 0x3e, 0xb3, 0x5f, 0x38, 0x71, + 0x36, 0x1c, 0xcc, 0x3f, 0x8d, 0xb6, 0xf9, 0x69, 0x98, 0xd4, 0x7b, 0x3e, + 0x50, 0xfa, 0x6e, 0x2c, 0xad, 0xf0, 0x7b, 0x36, 0x67, 0x32, 0xcf, 0x65, + 0x25, 0xae, 0x80, 0xeb, 0xb6, 0x60, 0x82, 0x0e, 0xba, 0xb8, 0xda, 0x47, + 0xa6, 0xaf, 0x5c, 0x16, 0xf0, 0xc5, 0xdf, 0x1f, 0x8c, 0xf3, 0x0f, 0x07, + 0x51, 0xd9, 0x25, 0x6d, 0xab, 0x44, 0x15, 0x36, 0xa1, 0xb4, 0xab, 0x47, + 0xaf, 0x71, 0xf1, 0xeb, 0x5a, 0xe3, 0x34, 0xf2, 0xba, 0x33, 0x27, 0x2c, + 0x54, 0xc3, 0xea, 0x86, 0x02, 0x75, 0xe1, 0xd2, 0xbc, 0xb8, 0xe2, 0x5b, + 0x6d, 0x4b, 0x71, 0x49, 0x42, 0x52, 0x36, 0xa2, 0x4e, 0x80, 0x1e, 0x93, + 0x54, 0x1f, 0x1c, 0x7f, 0x08, 0x85, 0x61, 0xb9, 0x14, 0x9c, 0x4f, 0x14, + 0xc5, 0x9f, 0xbe, 0x5e, 0x59, 0x21, 0xb7, 0x64, 0x3c, 0x4a, 0x22, 0xb4, + 0xb2, 0x90, 0xae, 0x5f, 0x37, 0x6a, 0x70, 0x80, 0x46, 0xc0, 0xe5, 0x03, + 0x7a, 0xdf, 0x7d, 0x7c, 0x77, 0xc6, 0xee, 0x29, 0x71, 0x47, 0x29, 0xbb, + 0x48, 0xb4, 0xe6, 0x17, 0xf9, 0x09, 0x8e, 0x9d, 0x1f, 0x20, 0x8b, 0xef, + 0x51, 0x8a, 0x48, 0xd8, 0xf3, 0x52, 0x7c, 0xee, 0x84, 0x7c, 0x22, 0xa2, + 0x3b, 0xab, 0x34, 0x8d, 0x32, 0x8f, 0xbe, 0xaf, 0x3c, 0x60, 0xc7, 0x3c, + 0xb6, 0x45, 0xaf, 0x11, 0x62, 0x46, 0x63, 0x75, 0x60, 0xf2, 0xbc, 0xd5, + 0xad, 0x49, 0x31, 0xd8, 0x57, 0x87, 0x6b, 0x21, 0x47, 0xb3, 0x47, 0x71, + 0xe8, 0x0a, 0x95, 0xea, 0xa5, 0x6b, 0xa3, 0xb9, 0xb6, 0x44, 0xdb, 0xb2, + 0x32, 0xec, 0x95, 0x18, 0xfd, 0xaf, 0xbc, 0xdb, 0xac, 0x8e, 0x96, 0x7c, + 0xcf, 0x43, 0xd2, 0xd4, 0x03, 0x87, 0xbf, 0x44, 0x36, 0x10, 0x3d, 0xb5, + 0xf3, 0x77, 0xe0, 0x29, 0x74, 0x4b, 0x79, 0x16, 0x4b, 0x66, 0x5b, 0x89, + 0xdc, 0x88, 0x8d, 0x49, 0x42, 0x09, 0xe8, 0x7b, 0x35, 0x94, 0x9e, 0x9f, + 0xff, 0x00, 0x20, 0xfe, 0xea, 0xfa, 0x1b, 0x89, 0x39, 0x26, 0x3f, 0x89, + 0xdb, 0x5b, 0xbc, 0xde, 0xd2, 0xec, 0xb7, 0xb9, 0xbb, 0x2b, 0x7c, 0x06, + 0x93, 0xda, 0x3b, 0x25, 0xef, 0x00, 0xdb, 0x7e, 0x2a, 0xfd, 0x63, 0xdd, + 0xea, 0xf1, 0xee, 0xa1, 0x46, 0x1a, 0x35, 0xb3, 0x8a, 0xbd, 0x59, 0xeb, + 0xd0, 0x89, 0x1c, 0x42, 0xdd, 0x8e, 0xdb, 0x61, 0x29, 0xac, 0x72, 0xdc, + 0xcc, 0x46, 0x15, 0xd5, 0x6b, 0x6d, 0x82, 0x92, 0xf1, 0xfd, 0x25, 0x2c, + 0x8d, 0xb8, 0x77, 0xe2, 0x49, 0xf6, 0xd4, 0xd8, 0xef, 0xdf, 0xd5, 0xeb, + 0xff, 0x00, 0xdf, 0xfe, 0xfa, 0xd5, 0x28, 0xde, 0x47, 0xc7, 0x4b, 0xc5, + 0xd1, 0x0b, 0x6e, 0x0e, 0x2f, 0x8c, 0x32, 0xe8, 0xe7, 0x8f, 0x6d, 0x98, + 0xdb, 0xb3, 0x26, 0x2d, 0x1e, 0x1c, 0xc8, 0x68, 0x29, 0x43, 0xfb, 0x93, + 0x56, 0x74, 0x1b, 0xe2, 0xd9, 0x87, 0x1e, 0x3d, 0xd1, 0xb7, 0x5c, 0xbd, + 0xa5, 0x09, 0x12, 0x22, 0xc7, 0x86, 0xeb, 0x6a, 0xe7, 0x00, 0x15, 0x28, + 0x21, 0xcd, 0x29, 0x0d, 0xf5, 0xd8, 0x52, 0xb4, 0x3c, 0x37, 0xba, 0xea, + 0x85, 0x58, 0xbd, 0x8e, 0x49, 0xd3, 0x97, 0x2c, 0x52, 0xce, 0xf8, 0x83, + 0x73, 0x4d, 0xe9, 0x78, 0xa6, 0x05, 0x6f, 0x66, 0xe7, 0x7b, 0x6d, 0xc0, + 0xdc, 0xb9, 0x92, 0x12, 0x7c, 0x92, 0x0a, 0x94, 0x36, 0x12, 0x79, 0x7c, + 0xe7, 0x5d, 0xd7, 0x5e, 0xcd, 0x20, 0x91, 0xe3, 0xdc, 0x45, 0x42, 0x71, + 0x1f, 0x0c, 0xcd, 0xee, 0xdc, 0x10, 0xc9, 0x23, 0x66, 0xf7, 0x0b, 0xcd, + 0xde, 0x40, 0x69, 0x12, 0xed, 0xe5, 0x56, 0xb6, 0x23, 0xc5, 0x86, 0xf3, + 0x67, 0x98, 0x94, 0x84, 0x92, 0xf0, 0x0a, 0x4f, 0x32, 0x36, 0xa0, 0x07, + 0x9d, 0xb2, 0x07, 0x78, 0xb5, 0xb8, 0x67, 0x62, 0xf2, 0x0c, 0x4a, 0xf6, + 0xee, 0x2b, 0x6b, 0x89, 0x03, 0x23, 0x5d, 0xc5, 0xe6, 0x82, 0xa7, 0x28, + 0x2c, 0x47, 0x0b, 0x58, 0x57, 0x3a, 0xb4, 0x4f, 0x36, 0xd2, 0xb0, 0xb3, + 0xa2, 0x79, 0x95, 0xa0, 0x4e, 0x80, 0xd4, 0x8e, 0x2f, 0x8d, 0xdc, 0x71, + 0xbc, 0x22, 0xf5, 0x74, 0xcf, 0x6e, 0xfe, 0xe8, 0xdd, 0x5d, 0x8c, 0xff, + 0x00, 0x96, 0x48, 0x12, 0xde, 0x71, 0x92, 0xc9, 0x4f, 0x41, 0xd9, 0xa8, + 0x84, 0x03, 0xd0, 0x9f, 0x35, 0x09, 0xd7, 0x37, 0x28, 0xf4, 0x9f, 0x3a, + 0xbd, 0x69, 0x4d, 0xb4, 0xcf, 0x46, 0x85, 0x18, 0xc1, 0x26, 0x8a, 0x43, + 0xf0, 0x66, 0x4a, 0xae, 0x3c, 0x26, 0xb2, 0xc6, 0x90, 0x1b, 0x66, 0xec, + 0xd0, 0x71, 0x71, 0x15, 0x29, 0xb5, 0x3a, 0x04, 0x60, 0xe9, 0x48, 0x75, + 0xa4, 0x28, 0x84, 0x92, 0x0e, 0xd2, 0x48, 0xee, 0x29, 0xeb, 0xd7, 0xa5, + 0x59, 0x4c, 0xdb, 0x2e, 0x2b, 0x9c, 0x5e, 0x4b, 0x49, 0x0e, 0x21, 0x5a, + 0x33, 0xae, 0x0b, 0x12, 0x1e, 0x23, 0x7f, 0xd1, 0xb6, 0x9f, 0x31, 0x03, + 0xbf, 0xd1, 0xf3, 0x69, 0x62, 0xdd, 0x65, 0x5e, 0x15, 0x87, 0x18, 0x32, + 0x16, 0x96, 0xdd, 0xc7, 0x6e, 0x91, 0xee, 0xb1, 0x47, 0x2f, 0x9c, 0xcc, + 0x19, 0xea, 0x4a, 0x1f, 0x6b, 0xe6, 0x85, 0xad, 0xcd, 0x81, 0xd3, 0x6d, + 0x83, 0xe1, 0x56, 0x3a, 0x18, 0x6d, 0xb7, 0x9d, 0x71, 0x3c, 0xdc, 0xee, + 0x1f, 0x3c, 0x95, 0x13, 0xdd, 0xd3, 0xa0, 0x3d, 0x00, 0xe9, 0xdc, 0x34, + 0x2b, 0xb6, 0xdd, 0xeb, 0x8f, 0xc0, 0xe3, 0xb8, 0x5a, 0x65, 0xb7, 0x52, + 0x07, 0x37, 0xb7, 0xdc, 0x9c, 0x89, 0x0a, 0xf9, 0x61, 0xda, 0xaf, 0xf6, + 0x27, 0xbc, 0xb6, 0x07, 0x5d, 0x76, 0xe4, 0x27, 0x4e, 0xb0, 0xaf, 0xd5, + 0x75, 0xbe, 0x64, 0x6b, 0xa6, 0x8f, 0x29, 0xf0, 0xa7, 0x3c, 0x9a, 0xdd, + 0x67, 0xe2, 0xff, 0x00, 0x08, 0x35, 0x06, 0x49, 0x43, 0x57, 0x38, 0xc8, + 0x97, 0x6e, 0x97, 0xdc, 0xe4, 0x49, 0x29, 0x3c, 0xcd, 0x2f, 0xd2, 0x95, + 0x21, 0xc4, 0x80, 0x47, 0xa9, 0x42, 0xa3, 0x7c, 0x7f, 0xfd, 0xd4, 0x77, + 0x0d, 0xa7, 0x7f, 0x24, 0x78, 0x83, 0x2f, 0x16, 0x78, 0xf2, 0xd9, 0xf2, + 0x45, 0xb9, 0x3e, 0xd4, 0x4f, 0x44, 0xb3, 0x30, 0x0e, 0x69, 0x2c, 0x0f, + 0x40, 0x58, 0xf7, 0xd4, 0x8e, 0x9d, 0x7b, 0x5a, 0xca, 0xf2, 0x96, 0x7b, + 0xe8, 0xd2, 0xce, 0xa6, 0x3b, 0x8c, 0x6b, 0xe0, 0xce, 0x62, 0xfe, 0x5f, + 0x86, 0x21, 0xeb, 0xa2, 0x11, 0x1e, 0xff, 0x00, 0x6d, 0x75, 0x76, 0xfb, + 0xdc, 0x6d, 0xe8, 0xb1, 0x31, 0xa3, 0xca, 0xe7, 0x41, 0xf9, 0xaa, 0xe8, + 0xb4, 0xfa, 0x42, 0x85, 0x3b, 0x8e, 0xea, 0xa7, 0xf3, 0x34, 0xa3, 0x86, + 0xdc, 0x5d, 0x83, 0x9e, 0x35, 0xef, 0x58, 0xf6, 0x50, 0xb6, 0xad, 0x59, + 0x10, 0x1d, 0x10, 0xc4, 0xae, 0xe8, 0x92, 0xc8, 0xde, 0x86, 0xff, 0x00, + 0x24, 0xa3, 0xdc, 0x01, 0x49, 0xab, 0x80, 0x77, 0x77, 0x6a, 0xbc, 0xf3, + 0xd0, 0x0a, 0x28, 0xa2, 0x80, 0x28, 0xa2, 0x8a, 0x00, 0xa2, 0x8a, 0x28, + 0x04, 0x4e, 0x32, 0xfc, 0x4b, 0x17, 0xfd, 0xaa, 0xb6, 0x7d, 0xf8, 0xa7, + 0xba, 0x44, 0xe3, 0x2f, 0xc4, 0xb1, 0x7f, 0xda, 0xab, 0x67, 0xdf, 0x8a, + 0x7b, 0xa0, 0x30, 0x40, 0x3d, 0xe2, 0x8a, 0xcd, 0x15, 0x00, 0x29, 0x33, + 0x8e, 0x3f, 0x23, 0xd9, 0x77, 0xd1, 0x12, 0x3f, 0x80, 0xd3, 0x9d, 0x26, + 0x71, 0xc7, 0xe4, 0x7b, 0x2e, 0xfa, 0x22, 0x47, 0xf0, 0x1a, 0x90, 0x47, + 0x71, 0xcf, 0x06, 0x56, 0x7d, 0xc3, 0x27, 0xed, 0x70, 0xdc, 0x2c, 0x5e, + 0x22, 0xf6, 0x73, 0xad, 0x12, 0x12, 0x74, 0xa6, 0x65, 0xb5, 0xd5, 0xb2, + 0x0f, 0x86, 0xfa, 0xa7, 0xfe, 0x2a, 0xae, 0xb8, 0x23, 0xc4, 0xc9, 0x37, + 0x9b, 0xb3, 0x6f, 0xdd, 0x96, 0xb4, 0xc8, 0xb8, 0xb6, 0xb7, 0x54, 0xd2, + 0xc6, 0xbb, 0x09, 0x0c, 0xf2, 0xb7, 0x36, 0x31, 0xf4, 0x14, 0x2f, 0x95, + 0xd4, 0x83, 0xbf, 0x31, 0xe1, 0xd4, 0xf2, 0x93, 0x5f, 0x42, 0xc6, 0x1f, + 0xcd, 0xdb, 0xf9, 0x83, 0xec, 0xaf, 0x97, 0x38, 0xd5, 0x8d, 0x3b, 0x85, + 0x71, 0xa2, 0x2d, 0xda, 0xdc, 0x43, 0x16, 0xec, 0xb2, 0x5a, 0x24, 0x44, + 0x70, 0xab, 0x95, 0xb8, 0xd7, 0xa6, 0xc1, 0x00, 0x2c, 0x9e, 0xe4, 0x48, + 0x42, 0x94, 0xda, 0xba, 0x8e, 0xae, 0x15, 0x77, 0x23, 0x55, 0x7a, 0x72, + 0xd2, 0xf7, 0xe0, 0xa5, 0x48, 0x6a, 0x5e, 0xa6, 0x38, 0xf7, 0x89, 0x7b, + 0x93, 0xc4, 0x77, 0x6e, 0x03, 0xb4, 0x5c, 0x2c, 0x97, 0xdf, 0xe1, 0xba, + 0x56, 0x02, 0x1a, 0x94, 0x84, 0xfb, 0xf3, 0x2a, 0x3a, 0x27, 0x4a, 0x4a, + 0x52, 0xea, 0x7b, 0x81, 0xe5, 0x70, 0x78, 0x0a, 0xa0, 0xb8, 0xfb, 0x83, + 0xad, 0x8b, 0x14, 0x3c, 0xa2, 0x31, 0x71, 0xd5, 0xb6, 0xbe, 0xc2, 0x52, + 0x95, 0xe6, 0xf3, 0x20, 0x9f, 0x35, 0x69, 0x0a, 0xeb, 0xca, 0x15, 0xe6, + 0x9e, 0xa7, 0x5d, 0x37, 0xaa, 0xfb, 0x3d, 0xf8, 0x70, 0xb8, 0xa5, 0xc2, + 0xd5, 0xdb, 0x19, 0x77, 0xb1, 0x93, 0xca, 0x89, 0x76, 0xc7, 0x5d, 0x48, + 0x2b, 0x8d, 0x21, 0xb5, 0x6d, 0x05, 0x40, 0xef, 0xaa, 0x16, 0x0a, 0x14, + 0x3d, 0x1c, 0xc2, 0xab, 0xfc, 0x52, 0x20, 0xc9, 0x2c, 0x4f, 0x35, 0x32, + 0xce, 0xd6, 0xa4, 0x87, 0x21, 0x5e, 0x22, 0x38, 0xb0, 0xdb, 0x4c, 0x3a, + 0x93, 0xc8, 0xf3, 0x2a, 0x00, 0xa9, 0xd7, 0x14, 0x0f, 0xc1, 0x2a, 0x3a, + 0xd1, 0x4a, 0x93, 0xaa, 0xe9, 0x8d, 0x3d, 0x59, 0x83, 0x39, 0xe5, 0x53, + 0x18, 0x99, 0xf1, 0x7f, 0x07, 0xb3, 0x17, 0x70, 0x4e, 0x21, 0x5b, 0x32, + 0x24, 0x85, 0x29, 0x86, 0x97, 0xc9, 0x29, 0xb1, 0xde, 0xe3, 0x2b, 0x1a, + 0x58, 0x1e, 0xbd, 0x1d, 0x8f, 0x58, 0x15, 0xf7, 0x8b, 0x96, 0xbb, 0x45, + 0xeb, 0x29, 0xb3, 0x5f, 0x6d, 0xf2, 0x58, 0x5d, 0xce, 0x74, 0x51, 0x16, + 0x04, 0xc5, 0xa9, 0x2a, 0x4c, 0x78, 0xaa, 0xdb, 0x8b, 0x75, 0xb4, 0x2b, + 0xa7, 0x31, 0x1a, 0x1b, 0xf1, 0xe6, 0x48, 0x3b, 0x1b, 0x07, 0xe5, 0xfb, + 0xcf, 0xe0, 0xd7, 0x73, 0x57, 0x11, 0xe6, 0xe3, 0xf6, 0x9b, 0xf4, 0x36, + 0x20, 0xf9, 0x11, 0xb8, 0x42, 0x7a, 0x5f, 0x31, 0x51, 0x6f, 0xb4, 0x08, + 0x2d, 0xab, 0x94, 0x7c, 0x24, 0x92, 0x36, 0x7c, 0x41, 0x07, 0x43, 0x7a, + 0xab, 0x9f, 0xf0, 0x79, 0xb6, 0x5c, 0x2d, 0xf8, 0x7d, 0xc7, 0x0d, 0xc8, + 0x63, 0x2a, 0x25, 0xfa, 0xc3, 0x2c, 0x32, 0x64, 0x20, 0xed, 0x4a, 0x68, + 0xfb, 0xe4, 0x77, 0x1b, 0x59, 0xef, 0x48, 0x3b, 0x09, 0xf6, 0x68, 0x8e, + 0xfa, 0xbd, 0x18, 0xcd, 0x66, 0x18, 0xe7, 0xea, 0x56, 0xb4, 0xa9, 0xc9, + 0xa9, 0xa7, 0xc7, 0xd0, 0xb5, 0xaf, 0x37, 0x0b, 0xc6, 0x2f, 0x7d, 0xb6, + 0x62, 0x98, 0x56, 0x2a, 0xf3, 0x30, 0xe6, 0x48, 0x4b, 0x53, 0xaf, 0xcf, + 0xc3, 0x5c, 0xb0, 0x1d, 0x52, 0x79, 0xb9, 0x96, 0x94, 0xa9, 0x2a, 0x59, + 0xf3, 0x81, 0x2e, 0x2d, 0x40, 0x0e, 0xa0, 0x6f, 0x5a, 0x12, 0x7c, 0x40, + 0x61, 0x4c, 0x5d, 0x31, 0x89, 0x4f, 0xbc, 0xd3, 0xb7, 0x25, 0x97, 0x61, + 0xc8, 0x5b, 0x4d, 0x72, 0x25, 0xe6, 0xcb, 0x45, 0xc5, 0x2b, 0x94, 0x92, + 0x40, 0x0b, 0x6d, 0x1a, 0x1b, 0x3a, 0xe7, 0xef, 0xf1, 0xa1, 0x8c, 0xb3, + 0x29, 0x62, 0x38, 0x61, 0xfb, 0x6d, 0xa6, 0x73, 0xc0, 0x69, 0x32, 0x44, + 0xa5, 0xb0, 0x17, 0xe8, 0x25, 0xae, 0x45, 0x6b, 0xea, 0x57, 0xf7, 0x77, + 0x54, 0x59, 0x4c, 0xe9, 0xb7, 0x33, 0x75, 0xbd, 0xcb, 0x6e, 0x54, 0xd0, + 0xd9, 0x69, 0xb4, 0xb4, 0x8e, 0x46, 0x63, 0x20, 0x9d, 0xa9, 0x2d, 0x8d, + 0x93, 0xb2, 0x40, 0xda, 0x89, 0x24, 0xe8, 0x77, 0x0e, 0x95, 0x4a, 0x56, + 0xf5, 0x35, 0xa6, 0xcb, 0x56, 0xaf, 0x4f, 0x46, 0x16, 0xe6, 0xf8, 0x51, + 0xe7, 0x37, 0x70, 0x5c, 0xcb, 0x2d, 0xc4, 0xc0, 0xb8, 0x3a, 0xda, 0x52, + 0xb0, 0xa6, 0x43, 0xad, 0x48, 0x09, 0xde, 0x92, 0xa4, 0x6c, 0x1e, 0x61, + 0xcc, 0x74, 0xa0, 0x47, 0x4e, 0x87, 0x63, 0x40, 0x72, 0x4b, 0x89, 0x2e, + 0xe6, 0xf3, 0x6f, 0x5f, 0xee, 0x92, 0x6e, 0xaa, 0x69, 0x41, 0x6d, 0xb2, + 0xb0, 0x1b, 0x8e, 0x85, 0x0e, 0xe2, 0x1a, 0x4f, 0x42, 0x47, 0xeb, 0xf3, + 0x11, 0x5d, 0x85, 0xc4, 0xb4, 0x0b, 0xcb, 0x58, 0x42, 0x5b, 0x1c, 0xe5, + 0x65, 0x5d, 0x12, 0x07, 0x5d, 0x93, 0xe0, 0x3a, 0x75, 0xde, 0xbb, 0xaa, + 0x2f, 0x1a, 0xbb, 0xcb, 0xbf, 0xc6, 0x95, 0x76, 0x90, 0x91, 0xe4, 0xf2, + 0x26, 0x3a, 0x60, 0xac, 0x34, 0x51, 0xda, 0xc6, 0x07, 0x48, 0x73, 0x5d, + 0xfe, 0x76, 0x89, 0x07, 0x5d, 0x46, 0x8e, 0xba, 0xd7, 0x67, 0xbb, 0x8f, + 0xbc, 0xce, 0x39, 0x38, 0x95, 0x49, 0x7b, 0xbc, 0x67, 0x82, 0x03, 0x8d, + 0xf2, 0x53, 0x12, 0xd0, 0x89, 0xcb, 0x4b, 0x8b, 0x4d, 0xde, 0xc3, 0x3a, + 0xc8, 0xe6, 0xfa, 0x8e, 0xdd, 0x1a, 0x76, 0x3f, 0xd6, 0x7d, 0xf3, 0x54, + 0xe9, 0xa2, 0x3a, 0x13, 0xb2, 0x3b, 0xcf, 0xa6, 0x97, 0xf8, 0x98, 0x5a, + 0x46, 0x04, 0xbb, 0x83, 0xa3, 0x9f, 0xdc, 0x4b, 0xb4, 0x0b, 0x98, 0x04, + 0x6f, 0xcd, 0x0f, 0x25, 0xb7, 0x3a, 0x1f, 0xd4, 0x5a, 0xa9, 0x85, 0x40, + 0x85, 0x28, 0x1e, 0xfd, 0x9a, 0xad, 0x18, 0xe9, 0x9c, 0x91, 0x6a, 0xd2, + 0xd5, 0x18, 0xb3, 0x15, 0x09, 0x9a, 0x59, 0x5d, 0xbd, 0xd8, 0xd4, 0xc4, + 0x29, 0x02, 0x2d, 0xce, 0x33, 0xc8, 0x97, 0x6d, 0x92, 0x7f, 0xa0, 0x94, + 0xd1, 0xe6, 0x6d, 0x7e, 0xb1, 0xbe, 0x84, 0x78, 0xa4, 0xa8, 0x78, 0xd4, + 0xdd, 0x1a, 0xad, 0xe4, 0x93, 0x4d, 0x33, 0x14, 0xda, 0x69, 0xa2, 0x4e, + 0xda, 0xed, 0xa3, 0x8b, 0xfc, 0x23, 0x91, 0x0e, 0xe9, 0x14, 0xb2, 0xd5, + 0xce, 0x33, 0x90, 0xae, 0x51, 0x4f, 0x55, 0xc4, 0x92, 0x92, 0x52, 0xe2, + 0x0f, 0xa1, 0x48, 0x70, 0x6c, 0x1f, 0x52, 0x4d, 0x72, 0x70, 0x13, 0x24, + 0xb9, 0x5c, 0xb1, 0x99, 0x58, 0xbe, 0x48, 0xe7, 0x3e, 0x51, 0x8b, 0x48, + 0xf7, 0x32, 0xe6, 0x4f, 0x7b, 0xe1, 0x23, 0xde, 0x64, 0xf5, 0xea, 0x52, + 0xeb, 0x7c, 0xaa, 0xdf, 0xa7, 0x9b, 0xd1, 0x4b, 0x76, 0x19, 0xa9, 0xc2, + 0xf8, 0x9e, 0xdc, 0x92, 0xae, 0x4b, 0x1e, 0x58, 0xe2, 0x22, 0xcc, 0x04, + 0xf9, 0x91, 0xee, 0x21, 0x3a, 0x65, 0xdf, 0x50, 0x75, 0x29, 0xec, 0xcf, + 0xeb, 0x25, 0xbf, 0x4d, 0x76, 0x71, 0x83, 0x9f, 0x02, 0xcd, 0xac, 0xfc, + 0x5b, 0x88, 0x85, 0x26, 0xde, 0x90, 0x8b, 0x4e, 0x50, 0x84, 0x0e, 0x8a, + 0x86, 0xb5, 0x69, 0xa9, 0x07, 0xd2, 0x59, 0x71, 0x43, 0x67, 0xbf, 0x95, + 0x44, 0x77, 0x0a, 0xf1, 0x6a, 0x41, 0xd3, 0x93, 0x89, 0xec, 0x52, 0x9e, + 0xb8, 0xa6, 0x5b, 0xe9, 0x24, 0x80, 0x4d, 0x66, 0xbc, 0xb4, 0xb4, 0x38, + 0xd2, 0x5c, 0x6d, 0x49, 0x5a, 0x16, 0x02, 0x92, 0xa4, 0x9d, 0x82, 0x0f, + 0x71, 0x15, 0xea, 0xa8, 0x68, 0x14, 0x51, 0x45, 0x00, 0x51, 0x45, 0x14, + 0x02, 0x27, 0x19, 0x7e, 0x25, 0x8b, 0xfe, 0xd5, 0x5b, 0x3e, 0xfc, 0x53, + 0xdd, 0x22, 0x71, 0x97, 0xe2, 0x58, 0xbf, 0xed, 0x55, 0xb3, 0xef, 0xc5, + 0x3d, 0xd0, 0x05, 0x14, 0x51, 0x50, 0x02, 0x93, 0x38, 0xe3, 0xf2, 0x3d, + 0x97, 0x7d, 0x11, 0x23, 0xf8, 0x0d, 0x39, 0xd2, 0x67, 0x1c, 0x7e, 0x47, + 0xb2, 0xef, 0xa2, 0x24, 0x7f, 0x01, 0xa9, 0x03, 0x7c, 0x6f, 0x8b, 0xb7, + 0xf3, 0x07, 0xd9, 0x4a, 0x9c, 0x5f, 0xc2, 0x21, 0x71, 0x0b, 0x01, 0xb9, + 0xe3, 0x12, 0xc8, 0x69, 0xd7, 0xdb, 0xe7, 0x87, 0x23, 0xf3, 0xa3, 0xc8, + 0x4f, 0x56, 0xdc, 0x49, 0xef, 0x04, 0x2b, 0xbf, 0x5e, 0x04, 0x8f, 0x1a, + 0x6b, 0x8d, 0xf1, 0x76, 0xfe, 0x60, 0xfb, 0x2b, 0x66, 0xba, 0xd0, 0x1f, + 0x30, 0x70, 0x4f, 0x2e, 0xb8, 0x31, 0x3d, 0xbf, 0x75, 0x12, 0x63, 0xce, + 0x7a, 0x5b, 0xb0, 0xae, 0xb1, 0xd6, 0x75, 0xe4, 0xf7, 0x86, 0x47, 0xbf, + 0x7b, 0x13, 0x25, 0xb4, 0xf6, 0xc3, 0xd2, 0xb4, 0x3d, 0xaf, 0x0a, 0x70, + 0xe2, 0x45, 0xa6, 0x35, 0x83, 0x38, 0x87, 0x96, 0xc6, 0x49, 0x66, 0xcd, + 0x92, 0xad, 0xb8, 0xb3, 0xdc, 0x46, 0x93, 0xe4, 0xb3, 0xb5, 0xca, 0xc4, + 0x8e, 0xbb, 0xe5, 0xed, 0x13, 0xef, 0x2a, 0x3e, 0x9e, 0xcf, 0xd3, 0x4a, + 0xff, 0x00, 0x84, 0x36, 0x3a, 0xac, 0x53, 0x89, 0x51, 0xf2, 0xc8, 0x6f, + 0x26, 0x0d, 0xa3, 0x2c, 0x2c, 0xdb, 0xee, 0x2f, 0x9e, 0x8d, 0xc2, 0xba, + 0x34, 0x79, 0xa0, 0xcc, 0x57, 0x4e, 0x83, 0x98, 0x04, 0x28, 0xfe, 0x81, + 0x5f, 0xe9, 0x55, 0x8b, 0x89, 0x49, 0xb6, 0xf1, 0x13, 0x02, 0xb8, 0x63, + 0x77, 0x98, 0xab, 0x65, 0x32, 0xdb, 0x76, 0x24, 0xc8, 0x8a, 0x50, 0xed, + 0x21, 0xc8, 0x41, 0xd3, 0x8d, 0xfa, 0x94, 0x85, 0x8e, 0x64, 0x9f, 0x50, + 0x23, 0xc2, 0xba, 0x21, 0x37, 0xa7, 0x57, 0x58, 0xfd, 0x0e, 0x7a, 0x90, + 0x5a, 0xb1, 0xd2, 0x5f, 0x51, 0x53, 0x88, 0x96, 0xb9, 0x8a, 0x11, 0x72, + 0x2b, 0x4c, 0x11, 0x3e, 0x75, 0xb4, 0x38, 0x87, 0xa0, 0x68, 0x1f, 0x2f, + 0x86, 0xe6, 0xbb, 0x66, 0x3a, 0xf4, 0x2a, 0xf3, 0x52, 0xb4, 0x6f, 0xf3, + 0x90, 0x07, 0x8d, 0x6b, 0xc1, 0xee, 0xd6, 0xe7, 0x25, 0x88, 0xd6, 0xd9, + 0xce, 0x4b, 0xb4, 0xce, 0x8a, 0x89, 0x36, 0x97, 0x16, 0x95, 0x29, 0x48, + 0x4e, 0xd6, 0x1c, 0x60, 0xac, 0xf5, 0xf7, 0xb2, 0x9d, 0xf2, 0x28, 0xf3, + 0x27, 0x98, 0x8f, 0x01, 0x52, 0x38, 0x3c, 0xeb, 0x8b, 0x90, 0xe4, 0xd9, + 0x6f, 0xaa, 0xdd, 0xfa, 0xc8, 0xf1, 0x83, 0x71, 0x3d, 0xdd, 0xa9, 0x00, + 0x16, 0xdf, 0x1f, 0xaa, 0xeb, 0x7c, 0xab, 0x07, 0xd3, 0xcc, 0x2b, 0x92, + 0xe3, 0x88, 0x3c, 0x8b, 0x9c, 0x9b, 0xae, 0x2d, 0x7e, 0x91, 0x8f, 0x4b, + 0x98, 0xae, 0x79, 0xad, 0xb7, 0x1d, 0x12, 0x22, 0xc9, 0x5f, 0xf5, 0x8a, + 0x61, 0x5d, 0x03, 0x9f, 0xac, 0x92, 0x92, 0x7c, 0x77, 0x5e, 0x8a, 0xef, + 0x62, 0x71, 0x3c, 0xe6, 0xf1, 0xdd, 0x90, 0xd5, 0xcc, 0x90, 0x4a, 0x7d, + 0x03, 0x7a, 0xf4, 0xd6, 0x36, 0x08, 0x1d, 0x41, 0x1d, 0xfb, 0xdf, 0x4a, + 0x53, 0xb8, 0x31, 0x79, 0xc7, 0x6d, 0x89, 0xba, 0x19, 0xf3, 0xb2, 0x0e, + 0xc5, 0x3c, 0xd7, 0x36, 0x94, 0x84, 0x07, 0x1e, 0x46, 0xce, 0xdd, 0x65, + 0x09, 0x00, 0x25, 0x69, 0xd8, 0xf3, 0x07, 0x45, 0x25, 0x24, 0x7c, 0x2e, + 0xa6, 0x4e, 0xcd, 0x78, 0xb7, 0x5f, 0x2d, 0x2d, 0xdd, 0xad, 0x13, 0xd9, + 0x9b, 0x09, 0xf4, 0x85, 0x21, 0xe6, 0x55, 0xb4, 0xe8, 0xf5, 0x3b, 0xf1, + 0x49, 0xee, 0x1a, 0x20, 0x1a, 0xbe, 0xbd, 0xf0, 0xca, 0x69, 0xd8, 0xd5, + 0x9b, 0x65, 0x10, 0x31, 0x9b, 0x58, 0x97, 0x35, 0x89, 0x92, 0xbb, 0x55, + 0x16, 0xd9, 0x62, 0x2c, 0x72, 0xea, 0xdd, 0x57, 0xa3, 0x5d, 0xc9, 0x1e, + 0x92, 0xa2, 0x07, 0x77, 0xb2, 0xa0, 0x31, 0x5b, 0x86, 0x7d, 0x7e, 0xbe, + 0xc7, 0x9f, 0x73, 0x85, 0x17, 0x1d, 0xb0, 0x20, 0x29, 0x42, 0x1b, 0x88, + 0x0b, 0x95, 0x2b, 0xa1, 0x09, 0x04, 0xab, 0xaa, 0x52, 0x0e, 0x89, 0xf3, + 0x52, 0x7d, 0x1b, 0xef, 0xa7, 0x1e, 0x77, 0x5b, 0x49, 0xf3, 0x94, 0x95, + 0x12, 0x41, 0x00, 0xf8, 0xfe, 0x77, 0xd5, 0xbe, 0x83, 0x7e, 0x83, 0x5c, + 0x71, 0x6d, 0xb1, 0x63, 0x4c, 0x7e, 0x4c, 0x66, 0xb9, 0x64, 0x48, 0x57, + 0xbe, 0xbc, 0x56, 0x56, 0xa5, 0x1d, 0x6c, 0x9d, 0x92, 0x74, 0x94, 0x8e, + 0xe0, 0x08, 0x03, 0xaf, 0xa2, 0xab, 0x24, 0xdb, 0xce, 0x4b, 0x45, 0xa4, + 0xb1, 0x83, 0x4f, 0x10, 0xdc, 0x67, 0xf1, 0x6b, 0x9a, 0xa5, 0xc2, 0x39, + 0x3d, 0xc2, 0x92, 0x4a, 0x89, 0x1a, 0x4a, 0xb5, 0xe6, 0x7f, 0xd4, 0x05, + 0x78, 0x91, 0x9b, 0x62, 0x50, 0x18, 0x40, 0x9d, 0x93, 0xda, 0x1b, 0x75, + 0x28, 0x1d, 0xa2, 0x04, 0xa4, 0x29, 0x49, 0x3a, 0xea, 0x08, 0x49, 0x24, + 0x52, 0xd5, 0xd6, 0xee, 0xde, 0x4f, 0x2a, 0x0d, 0xa2, 0x03, 0x25, 0xdc, + 0x57, 0xdd, 0x46, 0x99, 0xba, 0xdc, 0x92, 0x90, 0x59, 0x79, 0xf4, 0x92, + 0xe3, 0x51, 0x1b, 0x27, 0xa2, 0xd0, 0x5c, 0x6d, 0x01, 0x6a, 0x1d, 0x36, + 0x94, 0x24, 0x1d, 0xa8, 0xd4, 0x9e, 0x4b, 0x17, 0x12, 0xc7, 0x67, 0x22, + 0xe6, 0xed, 0xe7, 0xf9, 0x3d, 0x71, 0x90, 0xb1, 0xd8, 0xf9, 0x1a, 0x00, + 0x90, 0xfe, 0xcf, 0xc1, 0x42, 0x5a, 0x48, 0x75, 0x67, 0xd0, 0x3c, 0xf4, + 0xfa, 0x50, 0x77, 0x58, 0x4a, 0x72, 0x52, 0x94, 0xe2, 0xb2, 0x74, 0x46, + 0x11, 0x71, 0x8c, 0x24, 0xf0, 0x6d, 0x1c, 0x4e, 0xc0, 0xd5, 0xae, 0x4c, + 0x8d, 0x85, 0xef, 0xbb, 0x91, 0x87, 0x95, 0xf6, 0x20, 0xd7, 0xa3, 0xc4, + 0xdc, 0x05, 0x09, 0x2a, 0x7b, 0x27, 0x87, 0x1d, 0x20, 0xeb, 0x9a, 0x42, + 0x56, 0xc8, 0x27, 0xd0, 0x0a, 0xd2, 0x36, 0x7d, 0x5d, 0xf5, 0x2d, 0x64, + 0xfc, 0x70, 0x64, 0x6c, 0xa1, 0xa8, 0x52, 0x17, 0x8f, 0xc0, 0x0a, 0x20, + 0x5d, 0x6f, 0x51, 0x80, 0x9a, 0xea, 0x37, 0xd1, 0x49, 0x86, 0x83, 0xca, + 0x85, 0x6b, 0xbc, 0xb8, 0x47, 0xa7, 0x90, 0x77, 0x54, 0xef, 0xf2, 0x67, + 0x06, 0xc3, 0xe5, 0xc6, 0xbc, 0xe5, 0x97, 0x69, 0x37, 0xcb, 0xe6, 0xf5, + 0x1a, 0x65, 0xe5, 0xf3, 0x2e, 0x49, 0x56, 0xc1, 0xe5, 0x8e, 0xc0, 0x1a, + 0x49, 0xdf, 0x83, 0x48, 0x07, 0xdb, 0x59, 0x4a, 0xee, 0x4b, 0xc8, 0xd2, + 0x36, 0x91, 0x7e, 0x68, 0x4c, 0x75, 0xbb, 0x97, 0x12, 0x63, 0x37, 0x67, + 0xb3, 0x63, 0xb7, 0x01, 0x61, 0x90, 0xfb, 0x2a, 0x97, 0x79, 0x9c, 0x83, + 0x15, 0xae, 0xc9, 0x2e, 0x25, 0x64, 0xc7, 0x42, 0xbd, 0xf1, 0xc5, 0x1e, + 0x5d, 0x05, 0x04, 0x84, 0x83, 0xa3, 0xba, 0xbb, 0xef, 0x76, 0xd8, 0x37, + 0x9b, 0x34, 0xbb, 0x4d, 0xd2, 0x23, 0x72, 0xe1, 0x4c, 0x61, 0x4c, 0x48, + 0x65, 0xc1, 0xb4, 0xb8, 0x85, 0x0e, 0x55, 0x24, 0x8f, 0x58, 0x26, 0x93, + 0x66, 0x64, 0x99, 0x35, 0xe1, 0x61, 0x9b, 0x4c, 0x36, 0xb1, 0xf8, 0xca, + 0xde, 0xa4, 0x4e, 0x6f, 0xb7, 0x98, 0xb1, 0xaf, 0xe8, 0xe3, 0x24, 0xe9, + 0x1d, 0x47, 0x7b, 0xaa, 0x1e, 0xb4, 0x50, 0x71, 0x3b, 0x84, 0xc0, 0xa7, + 0x1f, 0xbe, 0xe5, 0x6e, 0x38, 0x76, 0x4b, 0xcb, 0xbb, 0x79, 0x31, 0x3e, + 0xc6, 0xda, 0x48, 0x40, 0x1e, 0xa2, 0x2b, 0x9e, 0xa3, 0x9d, 0x47, 0xaa, + 0x47, 0x45, 0x35, 0x0a, 0x6b, 0x4a, 0x22, 0xb8, 0x0b, 0x73, 0x99, 0x67, + 0x55, 0xdb, 0x85, 0x37, 0xd9, 0x2a, 0x7a, 0xe7, 0x8a, 0xa9, 0x29, 0x84, + 0xfb, 0xa7, 0xce, 0x99, 0x6d, 0x5e, 0xcc, 0x77, 0x7d, 0x65, 0x23, 0x6d, + 0xab, 0x5d, 0xc5, 0x03, 0xd3, 0x56, 0xb0, 0xee, 0xaf, 0x9c, 0x38, 0xca, + 0xbc, 0x8f, 0x87, 0x79, 0x26, 0x3b, 0xc4, 0x19, 0x6e, 0xc9, 0xbb, 0x35, + 0x69, 0x77, 0xb0, 0x33, 0x4b, 0x68, 0x0f, 0xbd, 0x05, 0xdf, 0xcb, 0x43, + 0x91, 0xc8, 0x02, 0x54, 0xb4, 0xe9, 0x2e, 0x36, 0xb0, 0x00, 0x25, 0x2a, + 0x07, 0xa9, 0x05, 0x5f, 0x42, 0xdb, 0x27, 0x45, 0xb9, 0x5b, 0x63, 0x5c, + 0x60, 0xbe, 0xdc, 0x88, 0x92, 0x99, 0x43, 0xcc, 0x3a, 0x83, 0xb4, 0xad, + 0x0b, 0x00, 0xa5, 0x43, 0xd4, 0x41, 0x06, 0xb1, 0x36, 0x3a, 0xa8, 0xa2, + 0x8a, 0x00, 0xa2, 0x8a, 0x28, 0x04, 0x4e, 0x32, 0xfc, 0x4b, 0x17, 0xfd, + 0xaa, 0xb6, 0x7d, 0xf8, 0xa7, 0xba, 0x44, 0xe3, 0x2f, 0xc4, 0xb1, 0x7f, + 0xda, 0xab, 0x67, 0xdf, 0x8a, 0x7b, 0xa0, 0x0a, 0x28, 0xa2, 0xa0, 0x05, + 0x26, 0x71, 0xc7, 0xe4, 0x7b, 0x2e, 0xfa, 0x22, 0x47, 0xf0, 0x1a, 0x73, + 0xa4, 0xce, 0x38, 0xfc, 0x8f, 0x65, 0xdf, 0x44, 0x48, 0xfe, 0x03, 0x52, + 0x06, 0xf8, 0xdf, 0x17, 0x6f, 0xe6, 0x0f, 0xb2, 0xb6, 0x56, 0xb8, 0xdf, + 0x17, 0x6f, 0xe6, 0x0f, 0xb2, 0xb6, 0x50, 0x0b, 0xbc, 0x47, 0xc5, 0x2d, + 0xd9, 0xc6, 0x15, 0x77, 0xc5, 0xae, 0xa8, 0xdc, 0x5b, 0x8c, 0x72, 0xd7, + 0x37, 0x79, 0x6d, 0x7d, 0xe8, 0x58, 0xf5, 0xa5, 0x40, 0x28, 0x7b, 0x2b, + 0xe7, 0x0e, 0x13, 0xe4, 0x57, 0x3b, 0x3e, 0x4a, 0x18, 0xbd, 0x2b, 0xb0, + 0xbd, 0x46, 0x98, 0x8c, 0x7f, 0x22, 0x04, 0xf4, 0x33, 0x9b, 0x49, 0xf2, + 0x29, 0x87, 0xd2, 0x99, 0x0d, 0x20, 0xb4, 0x4f, 0x8a, 0x9b, 0x41, 0x3f, + 0x0b, 0xaf, 0xd6, 0x04, 0x6c, 0xee, 0xbe, 0x75, 0xfc, 0x28, 0x71, 0x88, + 0x96, 0x4c, 0x92, 0x27, 0x10, 0x9c, 0x4b, 0x8d, 0xd8, 0xee, 0x8c, 0x26, + 0xc5, 0x95, 0xa9, 0xad, 0xed, 0xa6, 0x56, 0xa0, 0x63, 0xcd, 0x1a, 0xfc, + 0xe6, 0x5d, 0x08, 0x3b, 0xef, 0xd2, 0x52, 0x3c, 0x4d, 0x5a, 0x12, 0x70, + 0x92, 0x68, 0xac, 0xe2, 0xa7, 0x1c, 0x31, 0xa3, 0x8b, 0x0d, 0xb5, 0x69, + 0x97, 0x6e, 0xe2, 0x9c, 0x24, 0x94, 0xc4, 0x6d, 0xa4, 0xc1, 0xc8, 0x50, + 0x91, 0xbf, 0xe6, 0x85, 0x5e, 0x63, 0xe7, 0xd2, 0x59, 0x70, 0x92, 0x7f, + 0x51, 0x6b, 0xf4, 0x0a, 0x91, 0xd8, 0x52, 0x76, 0x0a, 0x54, 0x9d, 0x7c, + 0x20, 0x76, 0x35, 0xe9, 0xad, 0x1c, 0x1d, 0xbf, 0x2e, 0xfd, 0x65, 0x9d, + 0x8e, 0xe4, 0xad, 0x32, 0xed, 0xc1, 0x85, 0xb9, 0x6e, 0xbb, 0x33, 0xa0, + 0x5b, 0x71, 0xd0, 0x81, 0xb5, 0x8f, 0x02, 0xdb, 0xcd, 0xa9, 0x2e, 0x27, + 0xc0, 0x85, 0x74, 0xa4, 0x76, 0xe7, 0x5f, 0x31, 0x0b, 0x83, 0xdc, 0x32, + 0x85, 0x01, 0xcb, 0xa5, 0xee, 0x17, 0x5b, 0x43, 0xf2, 0x02, 0xbb, 0x03, + 0x6e, 0x27, 0x4d, 0x3e, 0xfb, 0x80, 0x75, 0x0d, 0x7e, 0x49, 0x40, 0x0e, + 0x65, 0x29, 0x03, 0x5d, 0xfb, 0x1d, 0xf4, 0x2a, 0x28, 0x37, 0x1e, 0x8f, + 0x74, 0x70, 0x56, 0xa6, 0xe6, 0xb5, 0x75, 0x5b, 0x32, 0x53, 0x89, 0x79, + 0x14, 0xab, 0x3c, 0x38, 0xb6, 0xdb, 0x53, 0xed, 0xb1, 0x78, 0xba, 0x2d, + 0xc4, 0xc7, 0x75, 0x63, 0x98, 0x46, 0x69, 0x09, 0x2a, 0x7a, 0x41, 0x4f, + 0xe7, 0x72, 0xa0, 0x69, 0x23, 0xc5, 0x45, 0x20, 0xf4, 0xde, 0x97, 0xb1, + 0xcc, 0x42, 0xd5, 0x6f, 0xc9, 0x67, 0x58, 0x12, 0xbb, 0x94, 0x09, 0x26, + 0x3a, 0x67, 0xdb, 0xa7, 0xc7, 0x94, 0x5a, 0x94, 0xa6, 0xd4, 0x40, 0x79, + 0xb7, 0x14, 0x9f, 0x35, 0xc2, 0x97, 0x7a, 0xe9, 0x41, 0x40, 0x07, 0x07, + 0x80, 0x15, 0x1d, 0x27, 0x14, 0x5c, 0x4e, 0x28, 0xb7, 0x2a, 0x7c, 0xb7, + 0xae, 0x72, 0x9e, 0xb4, 0x36, 0xb9, 0x97, 0x17, 0xbc, 0xd0, 0x49, 0x9c, + 0xc8, 0x52, 0x52, 0x37, 0xca, 0xdb, 0x69, 0x6d, 0x04, 0x04, 0xfa, 0x09, + 0x24, 0x92, 0x49, 0xa6, 0xb7, 0xef, 0x71, 0x32, 0x1b, 0xfc, 0x37, 0x70, + 0xfb, 0x4c, 0xec, 0xaa, 0xe7, 0x6e, 0x53, 0xa8, 0x4b, 0x90, 0x48, 0x44, + 0x36, 0xfb, 0x44, 0x72, 0xad, 0x0e, 0xc9, 0x5f, 0xbd, 0x81, 0xd0, 0x1d, + 0x27, 0x99, 0x5b, 0x48, 0xe9, 0xd2, 0xb5, 0x72, 0x59, 0x72, 0x9e, 0xd8, + 0x32, 0x51, 0x78, 0x4a, 0x3b, 0xe4, 0xda, 0xac, 0x77, 0x32, 0x6d, 0x7c, + 0x90, 0xb3, 0xc4, 0xad, 0xa1, 0xa0, 0x8f, 0x2a, 0xb2, 0x32, 0xeb, 0x80, + 0x0e, 0xef, 0x39, 0x2a, 0x40, 0x3f, 0xf2, 0xee, 0x96, 0xee, 0xb1, 0x6d, + 0x2f, 0x5c, 0x7d, 0xc8, 0xbe, 0xe4, 0xb7, 0xfc, 0xe6, 0xe6, 0x01, 0xff, + 0x00, 0x41, 0x5a, 0x99, 0x4a, 0x50, 0xaf, 0xf7, 0xad, 0xb0, 0x12, 0x00, + 0xf5, 0xba, 0xb0, 0x9f, 0x4d, 0x58, 0x8d, 0xf0, 0xf2, 0xf7, 0x75, 0x8a, + 0xe4, 0xbe, 0x21, 0xe5, 0x48, 0x83, 0x6e, 0x09, 0xe6, 0x76, 0xd7, 0x65, + 0x71, 0x51, 0x98, 0x09, 0x1e, 0x0f, 0x49, 0x56, 0x9c, 0x58, 0xd7, 0x7f, + 0x2f, 0x66, 0x2a, 0x52, 0xd7, 0x7d, 0xb0, 0x58, 0x2d, 0xf1, 0xac, 0x9c, + 0x35, 0xc5, 0x11, 0x2d, 0x97, 0x5d, 0x0c, 0xb2, 0xb8, 0xe9, 0x4c, 0x68, + 0x6a, 0x57, 0x5e, 0x65, 0x17, 0x48, 0xdb, 0xba, 0x00, 0x92, 0xa4, 0x25, + 0x7e, 0x3b, 0x3b, 0xae, 0x6a, 0x97, 0x31, 0xe2, 0x08, 0xea, 0xa7, 0x6b, + 0x27, 0xbc, 0xc8, 0x3b, 0x4e, 0x0b, 0x9b, 0x5f, 0xed, 0xe9, 0x83, 0x74, + 0x7e, 0x1e, 0x13, 0x61, 0xd2, 0x12, 0x2d, 0xd6, 0xc0, 0x87, 0xe6, 0x14, + 0x21, 0x5c, 0xc9, 0x05, 0xd2, 0x3b, 0x26, 0x74, 0x40, 0x23, 0xb3, 0x4a, + 0x88, 0xf0, 0x50, 0xa9, 0x9b, 0x24, 0x6e, 0x1d, 0x60, 0xb2, 0xe4, 0xb7, + 0x8d, 0xda, 0x9f, 0xbc, 0x5f, 0xca, 0x7f, 0x9c, 0xae, 0x1a, 0x55, 0x3e, + 0x7b, 0x87, 0xb8, 0xf6, 0xaf, 0x28, 0x9e, 0x4d, 0x9f, 0x05, 0xa9, 0x23, + 0xd5, 0x5b, 0x9d, 0xb1, 0x5f, 0x72, 0x85, 0x2c, 0x5e, 0xef, 0x0b, 0x93, + 0x10, 0x9d, 0x2a, 0x34, 0x5e, 0x68, 0xf0, 0xfd, 0x69, 0xd0, 0x3d, 0xa3, + 0xa0, 0x1f, 0x15, 0x2b, 0x94, 0xfe, 0x88, 0xae, 0xe6, 0xda, 0xc5, 0x6c, + 0xcd, 0x1b, 0x70, 0x9a, 0x48, 0x6b, 0x6a, 0x54, 0x6b, 0x73, 0x2a, 0xe4, + 0x6b, 0x43, 0x64, 0xa9, 0x0c, 0xa4, 0xf2, 0xf4, 0xf1, 0x3a, 0xac, 0x24, + 0x9b, 0x79, 0x9b, 0x36, 0x86, 0x23, 0xb4, 0x11, 0xc8, 0xc5, 0xd3, 0x2b, + 0xc9, 0xdd, 0x72, 0x3b, 0x6f, 0x0c, 0x70, 0xa0, 0x02, 0xe4, 0x06, 0xd2, + 0x0c, 0xe4, 0xa0, 0xec, 0x05, 0x29, 0xc7, 0x13, 0xca, 0x90, 0x7a, 0xe8, + 0xb6, 0x95, 0xf5, 0xfc, 0xfa, 0x85, 0x98, 0xc4, 0x1c, 0x5a, 0xfb, 0x3d, + 0xc5, 0xa1, 0x8f, 0x29, 0x66, 0x10, 0x93, 0x21, 0xd9, 0x4b, 0x70, 0x3f, + 0x3c, 0x12, 0x41, 0x6d, 0x12, 0x16, 0x76, 0xa2, 0x34, 0x8f, 0x37, 0x67, + 0xaa, 0xc0, 0x09, 0x14, 0xe9, 0x76, 0xb6, 0x48, 0x7a, 0xd6, 0xd5, 0xd2, + 0xd1, 0x2b, 0xca, 0x67, 0x43, 0x49, 0x91, 0x6d, 0x73, 0x63, 0xdf, 0x50, + 0x40, 0x25, 0x95, 0x1e, 0xe5, 0x21, 0x60, 0x01, 0xea, 0x3c, 0xa7, 0xc0, + 0x54, 0xcc, 0x05, 0x5b, 0x2f, 0x50, 0xe0, 0x5d, 0x9a, 0x65, 0xa7, 0xd1, + 0xc8, 0x1d, 0x8c, 0xb7, 0x10, 0x0a, 0x9a, 0xe6, 0x1d, 0x75, 0xe2, 0x95, + 0x78, 0x1f, 0x66, 0xaa, 0x9a, 0xb4, 0xec, 0x8b, 0xe9, 0xd5, 0xbb, 0x23, + 0xaf, 0xb2, 0x11, 0x69, 0xc3, 0xe4, 0xdc, 0xed, 0x4c, 0xa1, 0x9e, 0x46, + 0x12, 0xe9, 0x51, 0x68, 0x92, 0x86, 0xfa, 0x73, 0x2c, 0xa4, 0xe8, 0x9e, + 0x54, 0x95, 0x2b, 0x47, 0xd1, 0x48, 0x37, 0x34, 0x4c, 0x91, 0x2a, 0x6b, + 0xa8, 0x8f, 0xe5, 0x51, 0xa2, 0x2f, 0x4e, 0x4f, 0xba, 0xca, 0x71, 0x29, + 0x3e, 0x71, 0x1c, 0xfc, 0xfc, 0xe8, 0x42, 0x46, 0x87, 0x37, 0xbd, 0x27, + 0xa2, 0x4a, 0x0f, 0x52, 0x48, 0x16, 0xeb, 0x8d, 0xa1, 0xd6, 0xd6, 0xdb, + 0x89, 0x0a, 0x42, 0x81, 0x0a, 0x4a, 0xba, 0x82, 0x0f, 0x81, 0x07, 0xec, + 0xa5, 0x17, 0x31, 0x1c, 0x26, 0xca, 0x91, 0x71, 0x9e, 0xdb, 0x48, 0x8d, + 0x0c, 0x73, 0x36, 0x67, 0xcb, 0x52, 0xd8, 0x8c, 0x3d, 0x21, 0x2b, 0x57, + 0x22, 0x40, 0xf0, 0x3e, 0x15, 0x55, 0xbf, 0x24, 0xf8, 0x78, 0x34, 0x5d, + 0x2c, 0xf2, 0x32, 0x7e, 0x14, 0x5c, 0xec, 0x93, 0x16, 0xa9, 0x2e, 0xbf, + 0x19, 0xe6, 0xa2, 0xc8, 0x74, 0xef, 0xb6, 0xe5, 0x24, 0xc7, 0x78, 0xfa, + 0x49, 0xd3, 0x6a, 0x27, 0xc4, 0xee, 0x90, 0x7f, 0x06, 0x7b, 0xfa, 0xac, + 0xee, 0x1e, 0x1c, 0x5c, 0x14, 0xa4, 0xb0, 0xa8, 0x2d, 0xde, 0xb1, 0xb5, + 0xac, 0xec, 0x39, 0x01, 0xf1, 0xcc, 0xa6, 0x01, 0x3d, 0xea, 0x65, 0xc2, + 0xa4, 0x7c, 0xde, 0x5f, 0x0a, 0x73, 0xb9, 0xe6, 0x17, 0x3c, 0x9a, 0x03, + 0xf0, 0x78, 0x7d, 0x67, 0x93, 0x30, 0x3c, 0x92, 0xd2, 0x6f, 0x32, 0x47, + 0x61, 0x09, 0xad, 0x82, 0x39, 0xd0, 0x55, 0xe7, 0xbb, 0xaf, 0xd4, 0x49, + 0x1e, 0xba, 0x82, 0xe2, 0xa6, 0x0f, 0x2a, 0xd1, 0xc3, 0xfc, 0x76, 0xf3, + 0x88, 0x23, 0xb7, 0xc8, 0x70, 0x26, 0xd1, 0x22, 0xdf, 0xe6, 0xf2, 0xaa, + 0x63, 0x0d, 0xa0, 0x26, 0x43, 0x0a, 0xd7, 0xf5, 0x8d, 0x83, 0xd3, 0xf4, + 0x80, 0xf4, 0xd1, 0xac, 0x12, 0x9e, 0x4b, 0x84, 0x51, 0x50, 0xf8, 0x6e, + 0x45, 0x6c, 0xca, 0xf1, 0x6b, 0x6e, 0x47, 0x67, 0x7b, 0xb6, 0x81, 0x71, + 0x8e, 0x99, 0x0c, 0xab, 0x5d, 0x74, 0xa1, 0xdc, 0x47, 0x82, 0x81, 0xe8, + 0x47, 0x81, 0x06, 0xa6, 0x07, 0x75, 0x41, 0x21, 0x45, 0x14, 0x50, 0x08, + 0x9c, 0x65, 0xf8, 0x96, 0x2f, 0xfb, 0x55, 0x6c, 0xfb, 0xf1, 0x4f, 0x74, + 0x89, 0xc6, 0x5f, 0x89, 0x62, 0xff, 0x00, 0xb5, 0x56, 0xcf, 0xbf, 0x14, + 0xf7, 0x40, 0x1e, 0x26, 0x8a, 0xf2, 0xa4, 0x82, 0x7a, 0xd1, 0x4c, 0x11, + 0x94, 0x7a, 0xa4, 0xce, 0x38, 0xfc, 0x8f, 0x65, 0xdf, 0x44, 0x48, 0xfe, + 0x03, 0x4e, 0x74, 0x99, 0xc7, 0x1f, 0x91, 0xec, 0xbb, 0xe8, 0x89, 0x1f, + 0xc0, 0x68, 0x48, 0xdf, 0x1b, 0xe2, 0xed, 0xfc, 0xc1, 0xf6, 0x56, 0xca, + 0xd7, 0x1b, 0xe2, 0xed, 0xfc, 0xc1, 0xf6, 0x56, 0xca, 0x00, 0xa8, 0xdc, + 0xa2, 0xc9, 0x6e, 0xc8, 0xf1, 0xe9, 0xf6, 0x1b, 0xb3, 0x01, 0xf8, 0x37, + 0x06, 0x17, 0x1d, 0xf6, 0xcf, 0x8a, 0x54, 0x34, 0x75, 0xe8, 0x23, 0xbc, + 0x1f, 0x02, 0x05, 0x49, 0x50, 0x46, 0xe8, 0x0f, 0x98, 0x78, 0x35, 0x12, + 0xe5, 0x6a, 0xbf, 0x5c, 0x71, 0xa9, 0x31, 0x5e, 0x93, 0x96, 0xe3, 0x4b, + 0x6a, 0xd3, 0x3e, 0x40, 0x92, 0x1b, 0x4c, 0x8b, 0x5a, 0x52, 0xb7, 0x21, + 0x4d, 0xe4, 0x57, 0xe5, 0x54, 0x01, 0x4b, 0x5b, 0x1d, 0x42, 0x4a, 0x7b, + 0xf5, 0xd2, 0xea, 0xbe, 0x63, 0x16, 0x6c, 0xe6, 0x14, 0x2b, 0x84, 0x97, + 0x26, 0x41, 0xb9, 0x45, 0x4a, 0x90, 0xc4, 0xe8, 0x2f, 0x76, 0x6f, 0xb4, + 0x15, 0xae, 0x64, 0x83, 0xd5, 0x2a, 0x42, 0xb4, 0x0f, 0x2a, 0x82, 0x87, + 0xab, 0x75, 0x5b, 0xfe, 0x14, 0x56, 0x24, 0x58, 0xe6, 0xc0, 0xe2, 0x9c, + 0x78, 0x4b, 0x95, 0x12, 0x2b, 0x0a, 0xb4, 0xe5, 0x31, 0x5a, 0xe8, 0xa9, + 0x76, 0xb7, 0xcf, 0x2a, 0x95, 0xd3, 0xbd, 0x4d, 0x28, 0xf3, 0x27, 0xd0, + 0x48, 0x3e, 0x15, 0xd7, 0xc3, 0xec, 0xe2, 0x4d, 0x9b, 0x1a, 0xbd, 0x43, + 0x9e, 0xfa, 0x27, 0xdc, 0xed, 0x2c, 0x37, 0xd9, 0xbe, 0x0e, 0xd1, 0x3d, + 0x0e, 0x24, 0x2a, 0x24, 0x90, 0x7f, 0x45, 0xd4, 0xa9, 0x3c, 0xc7, 0xc1, + 0x41, 0x60, 0xf7, 0x56, 0xf1, 0x7a, 0xa1, 0x8e, 0xab, 0x74, 0x63, 0x35, + 0x89, 0xe7, 0xa3, 0xd9, 0x93, 0x33, 0xb8, 0x67, 0x8a, 0x42, 0x3e, 0xeb, + 0xf1, 0x13, 0x25, 0x95, 0x7e, 0x65, 0xae, 0x8d, 0xb5, 0x73, 0x75, 0xb6, + 0x22, 0x0d, 0x1d, 0x8d, 0xb0, 0xd8, 0x4a, 0x5d, 0x57, 0x41, 0xf0, 0xf9, + 0xb6, 0x7b, 0x85, 0x4b, 0xff, 0x00, 0x29, 0x6e, 0x0e, 0x46, 0x6a, 0xdf, + 0x88, 0x63, 0xed, 0x5b, 0xa1, 0x25, 0x01, 0x2c, 0x3b, 0x35, 0x82, 0xde, + 0x93, 0xe1, 0xd9, 0xc5, 0x4e, 0x95, 0xad, 0x77, 0x73, 0x96, 0xfd, 0x84, + 0x57, 0xa4, 0x5a, 0x62, 0xc1, 0x71, 0xe9, 0xf7, 0x29, 0x4d, 0xbd, 0x36, + 0x1b, 0x7c, 0xf3, 0xaf, 0x52, 0x92, 0x14, 0xe3, 0x67, 0x5b, 0x28, 0x64, + 0x11, 0xef, 0x69, 0xeb, 0xa0, 0x94, 0xf5, 0xf4, 0xec, 0x9d, 0x9e, 0xaf, + 0x76, 0xcd, 0xa2, 0x3a, 0x1e, 0x8d, 0x8f, 0xad, 0xa6, 0x1d, 0x58, 0x4b, + 0x66, 0x64, 0xa4, 0xb3, 0x26, 0x5a, 0xbf, 0x45, 0xa6, 0xc8, 0x25, 0x4a, + 0xd0, 0xe8, 0x95, 0x14, 0x13, 0xd0, 0x55, 0x5a, 0x4b, 0x79, 0xee, 0xc9, + 0x59, 0x7b, 0x43, 0x64, 0x29, 0xdd, 0xe2, 0x39, 0xee, 0xaa, 0xff, 0x00, + 0x94, 0x88, 0xb8, 0xde, 0xa5, 0x44, 0x60, 0xcc, 0x71, 0x52, 0x92, 0x84, + 0xb0, 0xc3, 0x69, 0x03, 0x65, 0xa6, 0xce, 0x9b, 0x2a, 0xea, 0x35, 0xa0, + 0xa5, 0x6c, 0xe8, 0xa8, 0x1a, 0x67, 0xca, 0xd9, 0x66, 0xd2, 0x6c, 0x19, + 0x33, 0x2b, 0x75, 0xd8, 0xd0, 0xa4, 0x28, 0x48, 0x52, 0x94, 0x4e, 0xda, + 0x7d, 0x01, 0x1c, 0xe7, 0x7d, 0xc0, 0x1e, 0xcf, 0xd8, 0x37, 0x4c, 0xa1, + 0x9b, 0x2e, 0x45, 0x0a, 0x15, 0xc1, 0x71, 0xa3, 0x4f, 0x8e, 0x42, 0x5f, + 0x8c, 0xb7, 0x1b, 0xe6, 0xd6, 0xc7, 0x42, 0x37, 0xdd, 0xe1, 0xf5, 0x8f, + 0x55, 0x73, 0xb1, 0x75, 0xc7, 0xb2, 0x36, 0xa4, 0xda, 0xdb, 0x9b, 0x1a, + 0x62, 0x56, 0x1c, 0x61, 0xf8, 0xea, 0x24, 0x29, 0x40, 0x12, 0x85, 0x8e, + 0x53, 0xa3, 0xae, 0xf1, 0xb1, 0xd3, 0xae, 0xf7, 0xdd, 0x51, 0xef, 0x1f, + 0x1d, 0x0b, 0x7b, 0xb5, 0xf3, 0x35, 0x65, 0xad, 0x16, 0xf1, 0x07, 0xd3, + 0x05, 0xa7, 0x54, 0xcb, 0x61, 0x0b, 0x5b, 0x4c, 0x13, 0xce, 0xe3, 0x01, + 0x69, 0x53, 0x89, 0x4e, 0x8e, 0xf6, 0x50, 0x14, 0x06, 0xbb, 0xf7, 0xaa, + 0x8f, 0x5e, 0x61, 0x8b, 0x5b, 0x61, 0x35, 0x16, 0xcd, 0xd9, 0xca, 0x5b, + 0x9f, 0x90, 0x87, 0x01, 0xae, 0x65, 0xaf, 0xd8, 0x84, 0x8d, 0x8f, 0x6e, + 0xb5, 0xe9, 0xae, 0x4c, 0x5a, 0xf6, 0xab, 0x03, 0xea, 0xc5, 0x6f, 0xcf, + 0xab, 0xb6, 0x8a, 0x79, 0x22, 0xbe, 0xbe, 0x8a, 0x90, 0xcf, 0xe6, 0x2c, + 0x0f, 0x1e, 0x9d, 0x0e, 0xbb, 0x94, 0x15, 0xe1, 0x5e, 0xae, 0x39, 0xde, + 0x23, 0x64, 0x9c, 0xb8, 0x16, 0xe6, 0x0c, 0xfb, 0xb3, 0xbe, 0x71, 0x85, + 0x6c, 0x8d, 0xda, 0x48, 0x70, 0xfa, 0x54, 0x94, 0x8d, 0x8f, 0x6a, 0xb4, + 0x3d, 0x75, 0x3a, 0x1e, 0x73, 0x8c, 0x94, 0xd6, 0xb1, 0x8c, 0xe0, 0x9b, + 0xc1, 0x21, 0x4f, 0x83, 0x8f, 0xa1, 0x8b, 0x83, 0x6d, 0xb0, 0xb2, 0xf3, + 0xae, 0x37, 0x19, 0x27, 0x7d, 0x83, 0x6a, 0x59, 0x52, 0x5b, 0x27, 0xc7, + 0x40, 0xeb, 0xd5, 0xdd, 0xd7, 0x5b, 0xa5, 0xb7, 0x33, 0x2c, 0x7b, 0x0f, + 0xba, 0xdf, 0x20, 0x5c, 0x27, 0xb4, 0xdb, 0x1e, 0x54, 0x89, 0x11, 0x9a, + 0x49, 0x2b, 0x5a, 0x94, 0xf2, 0x76, 0xb4, 0x21, 0x23, 0x65, 0x47, 0x9c, + 0x29, 0x5a, 0x48, 0x27, 0xcf, 0xee, 0xa0, 0xc3, 0xe2, 0x0e, 0x57, 0xb3, + 0x3a, 0x43, 0x78, 0x7d, 0xad, 0x47, 0x5d, 0x83, 0x1c, 0x92, 0x27, 0x38, + 0x9f, 0x5a, 0xba, 0xb6, 0xd7, 0x4d, 0x78, 0x38, 0x7e, 0x6d, 0x6e, 0xb5, + 0xc1, 0xc1, 0xb0, 0xc7, 0x1f, 0x55, 0xad, 0xa6, 0x27, 0x5f, 0x56, 0xb4, + 0xb4, 0xe1, 0xf2, 0x84, 0xc8, 0xb8, 0x48, 0x5a, 0xb4, 0x90, 0x95, 0x29, + 0x67, 0x98, 0x0e, 0x83, 0xa6, 0xc2, 0x40, 0xf0, 0x00, 0x55, 0x70, 0x97, + 0x25, 0xd3, 0x6c, 0xd6, 0x9b, 0xd6, 0x7f, 0x92, 0x91, 0xee, 0x25, 0x8d, + 0xbc, 0x76, 0x0a, 0xbb, 0xa6, 0x5e, 0x01, 0xed, 0x88, 0xf1, 0xe5, 0x8e, + 0x93, 0xcd, 0xbf, 0x9e, 0x51, 0xec, 0xaf, 0x5f, 0xc8, 0x9b, 0x0d, 0xb9, + 0xa5, 0xdf, 0xf3, 0x4b, 0x8b, 0xd9, 0x0c, 0x98, 0xa9, 0xed, 0x94, 0xfd, + 0xcf, 0xce, 0x61, 0x92, 0x3c, 0x5b, 0x60, 0x0e, 0x40, 0x7c, 0x07, 0x45, + 0x2b, 0x7d, 0xc4, 0x9a, 0xdd, 0x94, 0x5c, 0xf2, 0xf8, 0x56, 0xa5, 0x4b, + 0x92, 0xc5, 0xba, 0x0c, 0x27, 0x56, 0xdb, 0x72, 0x1d, 0x8e, 0xea, 0x96, + 0xf4, 0x06, 0x94, 0xb4, 0x85, 0x3a, 0x49, 0x01, 0x2b, 0xe5, 0x49, 0x3d, + 0xc0, 0x68, 0xf5, 0xea, 0x07, 0x5d, 0x13, 0x2c, 0x38, 0xbd, 0xc9, 0x33, + 0xa0, 0x59, 0xdc, 0x8c, 0x32, 0x0b, 0x7b, 0xe8, 0x70, 0x49, 0x92, 0xb5, + 0x38, 0xf7, 0x6e, 0x8e, 0x57, 0x00, 0x75, 0x44, 0xf3, 0x29, 0xb3, 0xe6, + 0xec, 0x77, 0x68, 0xf4, 0xd7, 0x4a, 0x8d, 0x4d, 0xf0, 0x4a, 0x8a, 0x5c, + 0x9d, 0x97, 0xfc, 0x96, 0xfd, 0x0e, 0xc1, 0x26, 0xe5, 0x0f, 0x18, 0x71, + 0x88, 0xa8, 0x48, 0x4a, 0x1c, 0x92, 0xf0, 0x0e, 0xb4, 0x0e, 0x87, 0x6a, + 0xa6, 0x40, 0x3e, 0x62, 0x77, 0xcc, 0x41, 0x50, 0x56, 0x81, 0xe8, 0x3b, + 0xab, 0x51, 0xb5, 0xc0, 0x98, 0x99, 0x5e, 0x45, 0x92, 0x4c, 0x7a, 0xfb, + 0x15, 0x40, 0x26, 0x73, 0xb3, 0x09, 0x4a, 0x1e, 0xd0, 0x50, 0x4f, 0x66, + 0x92, 0x1b, 0xe4, 0x3b, 0x00, 0xa4, 0x24, 0x74, 0x57, 0xac, 0x1a, 0x8f, + 0xb4, 0x66, 0x37, 0x79, 0xee, 0xbe, 0xb5, 0xda, 0x44, 0xfb, 0x7c, 0xc8, + 0xe8, 0x7d, 0x88, 0xe1, 0x87, 0x52, 0xe2, 0x02, 0xdb, 0x4e, 0xda, 0x59, + 0x5a, 0x03, 0x3d, 0x9e, 0xf9, 0x8f, 0x69, 0xcf, 0xd4, 0x1f, 0x83, 0x50, + 0x36, 0x9b, 0x6d, 0xbe, 0x3a, 0xa2, 0xa6, 0xdf, 0x7f, 0x85, 0x72, 0xbd, + 0xba, 0xc3, 0x70, 0xd4, 0xd5, 0xa8, 0x36, 0x99, 0x0e, 0x04, 0xa3, 0xb8, + 0xbe, 0x49, 0xf7, 0xb4, 0x84, 0xeb, 0xb5, 0xe5, 0xe7, 0x00, 0x00, 0x15, + 0xbd, 0x26, 0xa3, 0x65, 0xc9, 0x3b, 0xbe, 0x0e, 0x1e, 0x0a, 0xdc, 0xc6, + 0x23, 0x9f, 0xc9, 0xc2, 0xd6, 0xd7, 0x93, 0x58, 0xf2, 0x64, 0xbd, 0x79, + 0xc7, 0xdb, 0xdf, 0x9b, 0x19, 0xf0, 0xa2, 0x26, 0xc2, 0x1e, 0x1a, 0x4b, + 0x9b, 0x71, 0x3a, 0xd7, 0x9a, 0xaf, 0xee, 0xbd, 0x53, 0xdc, 0x2a, 0x92, + 0xe3, 0x1e, 0x31, 0x90, 0xbb, 0x86, 0x5b, 0x1a, 0x81, 0x12, 0xd5, 0x0a, + 0xe3, 0x68, 0x94, 0x89, 0x58, 0xf3, 0xf0, 0xb9, 0xcf, 0x92, 0x4c, 0x47, + 0xe4, 0xda, 0x71, 0x4b, 0xea, 0xb4, 0x3c, 0x36, 0xd9, 0x3e, 0x6e, 0xd4, + 0xb4, 0xec, 0x1d, 0xee, 0xac, 0x6e, 0x16, 0xe6, 0x50, 0x73, 0xcc, 0x12, + 0xdb, 0x93, 0xc0, 0x41, 0x64, 0x49, 0x6f, 0x4f, 0xc7, 0x51, 0xf3, 0xe3, + 0xbe, 0x93, 0xca, 0xe3, 0x4a, 0x1e, 0x05, 0x2a, 0x04, 0x75, 0xf0, 0xd1, + 0xf1, 0xa8, 0x24, 0x68, 0xa2, 0x81, 0xdd, 0x45, 0x00, 0x89, 0xc6, 0x5f, + 0x89, 0x62, 0xff, 0x00, 0xb5, 0x56, 0xcf, 0xbf, 0x14, 0xf7, 0x48, 0x9c, + 0x65, 0xf8, 0x96, 0x2f, 0xfb, 0x55, 0x6c, 0xfb, 0xf1, 0x4f, 0x74, 0x07, + 0x95, 0x10, 0x0f, 0x52, 0x07, 0xb6, 0x8a, 0xc9, 0x1d, 0x68, 0xa8, 0x18, + 0x46, 0x69, 0x33, 0x8e, 0x3f, 0x23, 0xd9, 0x77, 0xd1, 0x12, 0x3f, 0x80, + 0xd3, 0x9d, 0x26, 0x71, 0xc7, 0xe4, 0x7b, 0x2e, 0xfa, 0x22, 0x47, 0xf0, + 0x1a, 0x90, 0x37, 0xc6, 0xf8, 0xbb, 0x7f, 0x30, 0x7d, 0x95, 0xb2, 0xb5, + 0xc6, 0xf8, 0xbb, 0x7f, 0x30, 0x7d, 0x95, 0xb2, 0x80, 0x28, 0xa2, 0x8a, + 0x03, 0x96, 0xe9, 0x0a, 0x2d, 0xca, 0xdf, 0x26, 0xdf, 0x39, 0x86, 0xe4, + 0x45, 0x94, 0xd2, 0x99, 0x7d, 0xa5, 0x8d, 0xa5, 0xc4, 0x28, 0x10, 0xa4, + 0x91, 0xe8, 0x20, 0x9a, 0xf8, 0xda, 0xe3, 0x06, 0xe1, 0x84, 0xca, 0xbd, + 0xe2, 0x73, 0x24, 0x2f, 0xb4, 0xc5, 0xd8, 0x54, 0x54, 0xba, 0xb5, 0x12, + 0x5e, 0xb1, 0x49, 0x73, 0x9e, 0x24, 0x8d, 0xf8, 0xf9, 0x2c, 0x92, 0x02, + 0xbf, 0x55, 0xc5, 0x78, 0x26, 0xbe, 0xd3, 0xd5, 0x52, 0xff, 0x00, 0x84, + 0xee, 0x3e, 0xfc, 0x7b, 0x7c, 0x0e, 0x26, 0x5a, 0x20, 0x26, 0x74, 0xfc, + 0x68, 0x38, 0x9b, 0x8c, 0x32, 0x9e, 0x61, 0x70, 0xb5, 0x3a, 0x39, 0x65, + 0x30, 0xa1, 0xe3, 0xe6, 0x92, 0xa1, 0xbe, 0xef, 0x38, 0xd4, 0xa7, 0x87, + 0x92, 0x1a, 0xca, 0xc0, 0xd9, 0x8f, 0x4c, 0x63, 0x39, 0xc0, 0x24, 0xbd, + 0x09, 0xc1, 0x1d, 0xeb, 0x83, 0x45, 0x44, 0xf7, 0x86, 0x24, 0x00, 0x02, + 0x90, 0xaf, 0x5a, 0x1c, 0x49, 0x04, 0x7a, 0x3d, 0xbb, 0xa8, 0x35, 0xe3, + 0xf7, 0x97, 0xef, 0x82, 0x5a, 0xec, 0x97, 0x17, 0xee, 0x4b, 0x5f, 0x94, + 0x11, 0x2e, 0x7e, 0xed, 0xd1, 0x5d, 0x56, 0x8a, 0x96, 0x00, 0xf3, 0x97, + 0xa2, 0x94, 0xe9, 0x1d, 0x7a, 0x01, 0xbd, 0x68, 0x6a, 0xbd, 0xe0, 0x62, + 0xe7, 0x40, 0x99, 0x2e, 0xd9, 0x88, 0xdf, 0x18, 0x2f, 0xc6, 0xec, 0x96, + 0xc3, 0x53, 0xb9, 0x95, 0x16, 0xef, 0x6f, 0x75, 0x3c, 0xd0, 0xe4, 0x12, + 0x9f, 0x39, 0xb7, 0x83, 0x7e, 0xf4, 0xa7, 0x12, 0x0f, 0x9c, 0xce, 0x94, + 0x09, 0xd6, 0xad, 0xe7, 0xa4, 0x71, 0x56, 0x6a, 0xfb, 0x36, 0xad, 0x18, + 0xa5, 0xa9, 0x24, 0xf5, 0x7d, 0x77, 0x17, 0xa4, 0x94, 0x8f, 0x48, 0x40, + 0x65, 0x1c, 0xc7, 0xda, 0xa4, 0xee, 0xb4, 0x97, 0x39, 0x7d, 0x4c, 0xa3, + 0xc6, 0x17, 0x41, 0xaa, 0xdb, 0x1e, 0x2d, 0xa2, 0xd4, 0xdc, 0x65, 0xba, + 0xda, 0x52, 0x9e, 0x75, 0x2d, 0x4a, 0x3c, 0xa9, 0x2a, 0x51, 0x2a, 0x5a, + 0xb4, 0x4f, 0x40, 0x4a, 0x94, 0x75, 0xe1, 0xba, 0xae, 0x63, 0xdd, 0xf1, + 0x96, 0xaf, 0x32, 0x15, 0x83, 0xd8, 0xe6, 0xe5, 0x77, 0x30, 0xf1, 0x2d, + 0xba, 0xcf, 0x9d, 0x16, 0x0a, 0xb9, 0x74, 0xa4, 0x26, 0x42, 0xf4, 0x96, + 0xd2, 0x76, 0x49, 0x4a, 0x49, 0x3b, 0x27, 0xa7, 0x85, 0x48, 0xdc, 0xb0, + 0xfb, 0x5b, 0x4d, 0x0b, 0x8f, 0x10, 0xf2, 0x49, 0x57, 0xd4, 0x95, 0x84, + 0x88, 0xee, 0x0e, 0xc2, 0x22, 0xd5, 0xde, 0x12, 0x23, 0xb7, 0xb2, 0xe9, + 0xef, 0xf3, 0x54, 0x57, 0xbf, 0x01, 0x52, 0x3f, 0xca, 0x44, 0xf6, 0xf0, + 0xf1, 0xbb, 0x05, 0xad, 0xcb, 0x5c, 0x97, 0x50, 0xa5, 0x23, 0xcb, 0x60, + 0xa9, 0xa6, 0x63, 0xc6, 0x40, 0xf3, 0x9d, 0x08, 0x1a, 0x0a, 0x1b, 0x29, + 0x48, 0x4e, 0xd2, 0x76, 0xa1, 0xbd, 0x78, 0xd3, 0xba, 0xbd, 0x4d, 0x31, + 0x26, 0x45, 0x4e, 0xc3, 0x2e, 0xd9, 0x34, 0x65, 0x4a, 0xe2, 0x4d, 0xf6, + 0x3b, 0x76, 0xe4, 0x12, 0xb3, 0x6c, 0xb7, 0x9e, 0xcd, 0x84, 0x27, 0xd0, + 0xe4, 0x85, 0x0e, 0xd1, 0x5d, 0x3b, 0xca, 0x7b, 0x31, 0xed, 0xef, 0x3d, + 0xb6, 0x6b, 0xbe, 0x19, 0x8f, 0x43, 0x62, 0xcb, 0x85, 0xdb, 0x61, 0xf6, + 0xd2, 0x57, 0xd9, 0xc5, 0x89, 0x15, 0xae, 0xc1, 0x32, 0x08, 0x07, 0x6b, + 0xed, 0x0a, 0x74, 0xb4, 0xa4, 0x02, 0x54, 0xb1, 0xcc, 0x40, 0x1e, 0x24, + 0xea, 0x8c, 0x8e, 0xdb, 0x2e, 0x0c, 0xbb, 0x44, 0xeb, 0xcd, 0xdd, 0xfb, + 0xbd, 0x99, 0x12, 0xd2, 0x27, 0x35, 0x21, 0xa6, 0x92, 0x84, 0x2d, 0x63, + 0x91, 0xa7, 0x00, 0x4a, 0x53, 0xef, 0x69, 0x5a, 0x86, 0xc1, 0x27, 0x5d, + 0x0f, 0x81, 0xdf, 0x1e, 0x53, 0x95, 0x44, 0xb8, 0x40, 0x76, 0x33, 0x31, + 0xa6, 0xb1, 0x90, 0xdb, 0x66, 0x03, 0x09, 0xa5, 0xc7, 0x5b, 0x6e, 0x29, + 0xe0, 0xa2, 0x11, 0xca, 0x08, 0x1c, 0xe8, 0x71, 0x3c, 0xc0, 0x94, 0xed, + 0x3c, 0xaa, 0x3d, 0x7c, 0x43, 0x79, 0x0d, 0xa2, 0x75, 0xe5, 0x09, 0xc9, + 0x9a, 0x89, 0x0d, 0xdb, 0xe4, 0xf8, 0x66, 0xce, 0xf4, 0x94, 0x22, 0xe8, + 0x88, 0x0d, 0xad, 0xb5, 0xb0, 0xc9, 0xd8, 0x1a, 0x70, 0xaf, 0x6a, 0x6c, + 0xa8, 0xa4, 0x2c, 0xe9, 0x27, 0x97, 0x64, 0x68, 0x6e, 0xb9, 0xae, 0x93, + 0x71, 0x69, 0xf8, 0x9c, 0x89, 0xd6, 0x01, 0x12, 0x14, 0x8b, 0x13, 0x8e, + 0x48, 0x8e, 0x52, 0xc0, 0x40, 0x8a, 0xfb, 0x64, 0x82, 0x85, 0x80, 0x07, + 0x2f, 0x3e, 0x8a, 0x0a, 0x4f, 0x52, 0x15, 0xd0, 0x6f, 0xbb, 0x86, 0xea, + 0xac, 0x95, 0x84, 0x5c, 0x2d, 0xd7, 0x69, 0x66, 0x34, 0x2b, 0xa2, 0xdd, + 0x69, 0x84, 0x3e, 0xda, 0x1d, 0x7d, 0x28, 0x73, 0x7c, 0xe8, 0x69, 0xb6, + 0x4a, 0x94, 0xf9, 0x01, 0x5e, 0x6a, 0x94, 0x11, 0xcb, 0xd3, 0x98, 0x2a, + 0xb3, 0x1e, 0x1d, 0xae, 0x45, 0xfb, 0xc8, 0xa0, 0x44, 0x99, 0x6d, 0x7d, + 0x96, 0xd3, 0x31, 0xf7, 0xef, 0x21, 0x6e, 0x96, 0x93, 0xbe, 0x46, 0xd4, + 0xd2, 0x16, 0xa2, 0x85, 0x2c, 0xe8, 0x80, 0xb3, 0xcd, 0xc9, 0xa0, 0x35, + 0xd4, 0x0a, 0x61, 0x2f, 0x52, 0x77, 0x61, 0x75, 0xbc, 0x65, 0x2b, 0x5d, + 0xd3, 0xca, 0x9b, 0x44, 0x3b, 0x2c, 0xb6, 0x88, 0x6f, 0xdd, 0x56, 0x92, + 0xc8, 0x8c, 0xb5, 0xa3, 0x45, 0x2a, 0x5f, 0x31, 0xed, 0x53, 0xde, 0x52, + 0x84, 0x27, 0x98, 0x95, 0x00, 0x48, 0xd1, 0xad, 0x46, 0xd9, 0x69, 0x97, + 0x39, 0x8b, 0x55, 0xad, 0xe9, 0x97, 0x5b, 0x9b, 0xd1, 0x42, 0x5c, 0x6e, + 0xe0, 0xc9, 0x88, 0x83, 0x1d, 0xb4, 0x80, 0x16, 0xf2, 0x83, 0x49, 0x79, + 0xe6, 0xb9, 0x88, 0xd2, 0x0a, 0x94, 0x92, 0x49, 0x04, 0xe8, 0x13, 0x5d, + 0xf9, 0x63, 0xb3, 0x31, 0x0b, 0x94, 0x4c, 0x8e, 0x52, 0xee, 0x39, 0x2c, + 0x34, 0x32, 0xb6, 0x63, 0xb6, 0xe2, 0x5a, 0x2e, 0xb0, 0xfa, 0x88, 0x25, + 0x69, 0xe5, 0x4a, 0x76, 0x14, 0x84, 0xab, 0x43, 0x5b, 0xd8, 0xe5, 0x1f, + 0x0c, 0x01, 0x9c, 0x9e, 0xe9, 0x6e, 0xca, 0xd8, 0xb6, 0x2b, 0x1e, 0x91, + 0x2d, 0x37, 0x49, 0x0a, 0x53, 0x29, 0xec, 0x42, 0x98, 0x7d, 0x31, 0x5c, + 0x1c, 0xae, 0xa9, 0x5b, 0x4e, 0xdb, 0x48, 0xe8, 0xa0, 0xa5, 0x27, 0xe1, + 0xa1, 0x3a, 0x07, 0xb8, 0xc6, 0xfd, 0x42, 0xc6, 0x76, 0x3d, 0x64, 0xb1, + 0xe7, 0x5a, 0x97, 0x6b, 0x39, 0x1c, 0x86, 0x6e, 0x78, 0xf2, 0x0a, 0xca, + 0xe3, 0x46, 0x83, 0xc8, 0x90, 0xf2, 0x53, 0xef, 0x2d, 0xf2, 0x02, 0x79, + 0x9b, 0x24, 0x10, 0x90, 0x7f, 0x3f, 0x90, 0x75, 0xde, 0x86, 0xcc, 0xc2, + 0xe3, 0x68, 0xbb, 0xe1, 0xd0, 0x32, 0x4b, 0x1c, 0x94, 0xa2, 0x53, 0x0b, + 0x47, 0xb9, 0x6e, 0xb2, 0x00, 0x52, 0xcb, 0xba, 0x4a, 0x99, 0x6f, 0xa6, + 0x94, 0x54, 0x9d, 0x8d, 0x0e, 0xe2, 0x90, 0x7f, 0x36, 0xb8, 0x15, 0xee, + 0xd0, 0xb6, 0x44, 0xc7, 0xf2, 0x89, 0x6e, 0x2c, 0x95, 0x36, 0xeb, 0xad, + 0x36, 0x80, 0xf4, 0xd9, 0x49, 0x6d, 0x61, 0x49, 0x09, 0x6d, 0x91, 0xca, + 0xda, 0x76, 0x9d, 0x15, 0x92, 0x77, 0xa3, 0xd1, 0x35, 0xd7, 0x60, 0xb7, + 0xc4, 0xb9, 0xde, 0x64, 0x2e, 0xc0, 0xc3, 0xb6, 0x56, 0xe0, 0x14, 0xc7, + 0x7a, 0x4c, 0xb6, 0xd4, 0xec, 0xd4, 0xb8, 0xa4, 0xf3, 0x29, 0xa6, 0xbb, + 0x62, 0xa0, 0xd2, 0x42, 0x54, 0x36, 0x40, 0x21, 0x5b, 0xe9, 0xd0, 0x6c, + 0xb6, 0x5c, 0x05, 0x97, 0xc9, 0xcf, 0x70, 0x99, 0x7b, 0x7b, 0x11, 0x36, + 0x5c, 0x91, 0xf4, 0xc6, 0x0f, 0x28, 0x33, 0x26, 0xe5, 0x35, 0x86, 0xe3, + 0x02, 0x90, 0xa1, 0xd1, 0x96, 0x50, 0xe2, 0xd4, 0xb7, 0x48, 0x1b, 0x04, + 0x10, 0x90, 0x41, 0x23, 0xc0, 0x52, 0xde, 0x39, 0x78, 0xb7, 0xe1, 0x7c, + 0x74, 0x90, 0xc4, 0x09, 0x04, 0xe2, 0xd9, 0xd3, 0xca, 0x5a, 0x02, 0x90, + 0xa4, 0x08, 0x77, 0x94, 0x24, 0x17, 0x10, 0x52, 0xad, 0x29, 0x3d, 0xb3, + 0x7a, 0x5f, 0x51, 0xd5, 0x43, 0xa7, 0x4a, 0x6d, 0x9c, 0x6e, 0x18, 0xbe, + 0x47, 0x32, 0x53, 0xd1, 0x65, 0xe4, 0xd2, 0x9c, 0x8a, 0x85, 0xda, 0x82, + 0x8b, 0x61, 0xfe, 0xc5, 0xb5, 0x7f, 0x38, 0x69, 0x04, 0x04, 0x82, 0xe0, + 0x0b, 0xe7, 0x1d, 0x01, 0x58, 0xd2, 0x76, 0x4a, 0x77, 0x4b, 0xfc, 0x51, + 0xb1, 0x46, 0xe2, 0x4c, 0x66, 0xad, 0xb6, 0xc4, 0x49, 0xb7, 0xde, 0x25, + 0xc5, 0x53, 0xea, 0xed, 0x11, 0xca, 0xec, 0x07, 0x99, 0xdb, 0x91, 0x24, + 0x38, 0x01, 0xf3, 0x16, 0x1c, 0xf3, 0x40, 0xef, 0x52, 0x1d, 0x5f, 0x78, + 0x1d, 0x0f, 0x7d, 0xc2, 0xc7, 0x05, 0xcc, 0x93, 0xb1, 0xba, 0xcd, 0x23, + 0xf0, 0x57, 0x34, 0x73, 0x36, 0xc1, 0x63, 0xcf, 0x9e, 0xcf, 0x92, 0xde, + 0xa1, 0x2d, 0x50, 0x2f, 0x31, 0x08, 0xd2, 0x98, 0x9a, 0xd1, 0xe5, 0x71, + 0x24, 0x78, 0x02, 0x7c, 0xe1, 0xea, 0x50, 0xf4, 0x53, 0xc0, 0xa8, 0x24, + 0x44, 0xe3, 0x2f, 0xc4, 0xb1, 0x7f, 0xda, 0xab, 0x67, 0xdf, 0x8a, 0x7b, + 0xa4, 0x4e, 0x32, 0xfc, 0x4b, 0x17, 0xfd, 0xaa, 0xb6, 0x7d, 0xf8, 0xa7, + 0xba, 0x00, 0xa2, 0x8a, 0x28, 0x02, 0x93, 0x38, 0xe3, 0xf2, 0x3d, 0x97, + 0x7d, 0x11, 0x23, 0xf8, 0x0d, 0x39, 0xd2, 0x67, 0x1c, 0x7e, 0x47, 0xb2, + 0xef, 0xa2, 0x24, 0x7f, 0x01, 0xa0, 0x1b, 0xe3, 0x7c, 0x5d, 0xbf, 0x98, + 0x3e, 0xca, 0xd9, 0x5a, 0xe3, 0x7c, 0x5d, 0xbf, 0x98, 0x3e, 0xca, 0xd9, + 0x40, 0x14, 0x51, 0x45, 0x00, 0x56, 0xb9, 0x0d, 0xb4, 0xfb, 0x4e, 0x32, + 0xf3, 0x68, 0x75, 0xb7, 0x12, 0x52, 0xb4, 0x28, 0x6c, 0x28, 0x11, 0xd4, + 0x11, 0xe8, 0x22, 0xb6, 0x50, 0x46, 0xe8, 0x0f, 0x8d, 0xe5, 0x43, 0x93, + 0xc2, 0x6e, 0x21, 0xcb, 0xb4, 0x27, 0xb7, 0xec, 0xf1, 0xc4, 0xae, 0x75, + 0xb1, 0x5d, 0x49, 0x99, 0x8f, 0xbe, 0xef, 0x33, 0xec, 0x8e, 0xed, 0xae, + 0x2b, 0xa7, 0xb6, 0x1f, 0xaa, 0x1d, 0xf0, 0xd5, 0x7d, 0x29, 0x22, 0xf4, + 0xed, 0xd8, 0x5a, 0x62, 0xc4, 0xbb, 0xa2, 0xdf, 0x1e, 0x63, 0x2b, 0x71, + 0xf9, 0x6d, 0xf2, 0xa9, 0x6a, 0xd1, 0x6d, 0x29, 0x43, 0x65, 0x5b, 0x00, + 0xab, 0xb4, 0x07, 0x98, 0x82, 0x75, 0xad, 0x75, 0x3b, 0x0b, 0x5f, 0x84, + 0xc6, 0x29, 0x2e, 0xed, 0x89, 0xc7, 0xcb, 0x2c, 0x31, 0x9b, 0x7b, 0x23, + 0xc5, 0x5c, 0x55, 0xc2, 0x23, 0x6b, 0x40, 0x50, 0x94, 0xc7, 0x2e, 0xa4, + 0x46, 0x50, 0xf1, 0x43, 0x8d, 0xf3, 0x02, 0x9f, 0x1d, 0x6b, 0xc6, 0xab, + 0xbe, 0x0f, 0x3f, 0x65, 0xc8, 0xac, 0xab, 0xc1, 0x9e, 0x90, 0xe2, 0xed, + 0x6a, 0x8e, 0xdd, 0xc7, 0x1e, 0x92, 0xb5, 0xed, 0xc1, 0x01, 0xd5, 0x1e, + 0xcd, 0x25, 0x43, 0x47, 0x99, 0x87, 0x02, 0x98, 0x5e, 0x88, 0x20, 0xa1, + 0x27, 0x7d, 0xc6, 0xb5, 0x87, 0x7a, 0x3a, 0x4c, 0xa7, 0xdd, 0x96, 0xaf, + 0xcc, 0xb5, 0xf3, 0x08, 0xa7, 0x0b, 0x76, 0x26, 0x59, 0x1d, 0x37, 0x0b, + 0xac, 0x78, 0xad, 0xad, 0x87, 0xe3, 0x48, 0x96, 0xb7, 0x8a, 0x14, 0xe1, + 0x48, 0x4b, 0xc8, 0x2e, 0x12, 0x42, 0xb6, 0x02, 0x08, 0x04, 0x0d, 0x2f, + 0x7a, 0x1a, 0x3b, 0xf5, 0x93, 0xdd, 0x6d, 0x19, 0x44, 0x68, 0x11, 0xec, + 0xaa, 0x71, 0xfb, 0xa0, 0x79, 0xb5, 0x32, 0x1a, 0x0a, 0x4a, 0x98, 0x69, + 0x7a, 0x4b, 0xa5, 0xc2, 0x9e, 0xad, 0xa0, 0xb4, 0xa5, 0x8e, 0xba, 0x24, + 0x81, 0xad, 0x90, 0x35, 0xc9, 0x0a, 0xc9, 0x72, 0x6a, 0x5a, 0x2c, 0xad, + 0xca, 0x7a, 0xfc, 0x6d, 0xa5, 0x0b, 0x59, 0x94, 0xe2, 0x9a, 0x8a, 0xd3, + 0x9a, 0xe6, 0x41, 0x74, 0xa8, 0xad, 0xc7, 0xdc, 0xd6, 0x94, 0x13, 0xbe, + 0x51, 0xb0, 0x4e, 0x8e, 0xb5, 0xee, 0x0a, 0x2d, 0xe6, 0xf5, 0x22, 0xd3, + 0x96, 0x08, 0x76, 0xce, 0x49, 0x0d, 0x31, 0x0a, 0xd9, 0x11, 0xc2, 0xd4, + 0x59, 0x5c, 0xe8, 0x2a, 0x0e, 0xe8, 0x04, 0x95, 0x95, 0x14, 0x38, 0x9e, + 0x53, 0xb0, 0x9e, 0xcc, 0x8e, 0xbb, 0xd9, 0xcf, 0x2b, 0x84, 0x69, 0xbf, + 0x2c, 0xe7, 0x72, 0xdf, 0x77, 0x65, 0x11, 0xb1, 0xdb, 0xc2, 0x9c, 0xba, + 0x3e, 0x5b, 0x42, 0x9f, 0x62, 0x22, 0xbb, 0x67, 0xe5, 0x34, 0x95, 0x12, + 0x90, 0xeb, 0xab, 0x4b, 0x6d, 0xb2, 0xd9, 0x29, 0x00, 0xe8, 0x6d, 0x5a, + 0x3a, 0x3d, 0xf5, 0xd1, 0x6f, 0x66, 0x12, 0xf2, 0x09, 0x76, 0xf5, 0x5b, + 0x93, 0x88, 0x88, 0xec, 0xb6, 0xb9, 0x09, 0x8d, 0xd9, 0x87, 0xe5, 0x17, + 0x56, 0xae, 0x50, 0xdb, 0xa9, 0xea, 0x1a, 0xf3, 0x34, 0x79, 0x79, 0x4f, + 0x31, 0xd6, 0xc6, 0xba, 0xe9, 0xbf, 0x2d, 0x78, 0x54, 0xe9, 0x0e, 0x63, + 0x22, 0x0a, 0x18, 0xba, 0x21, 0x12, 0x5a, 0x43, 0xc5, 0x7d, 0x92, 0x0b, + 0x2b, 0x42, 0x5d, 0x68, 0x29, 0x29, 0x57, 0x66, 0x85, 0x25, 0xcd, 0xa5, + 0x5a, 0x29, 0x42, 0x81, 0xd8, 0x00, 0xf4, 0x32, 0x26, 0x1a, 0xce, 0x13, + 0x01, 0xe9, 0x2d, 0xb5, 0x02, 0x23, 0x1c, 0xe1, 0xdb, 0x83, 0xe9, 0x53, + 0x4d, 0xad, 0x4a, 0xe5, 0x28, 0x6d, 0xad, 0xa9, 0x0a, 0x59, 0x0b, 0x4a, + 0x55, 0xce, 0x92, 0x91, 0xe6, 0x8e, 0x52, 0x77, 0x53, 0xcf, 0x21, 0x7a, + 0x1b, 0x72, 0x36, 0x64, 0xe1, 0x73, 0xdd, 0xba, 0x59, 0x48, 0x96, 0x99, + 0x90, 0xcc, 0x78, 0x88, 0x9f, 0x35, 0x4a, 0x4c, 0x77, 0x9b, 0x25, 0xd5, + 0x36, 0x97, 0x1c, 0x3d, 0x03, 0x88, 0x0b, 0xd6, 0xce, 0x82, 0xd0, 0x91, + 0xd0, 0x1e, 0x9a, 0x72, 0x67, 0xd9, 0xcf, 0x1a, 0x83, 0x12, 0x04, 0x1e, + 0x69, 0x6b, 0x49, 0x4c, 0xa1, 0xdb, 0x10, 0x18, 0x8e, 0xb4, 0x12, 0xa4, + 0x3c, 0xb4, 0x6c, 0x21, 0x5c, 0xe9, 0x6d, 0x49, 0x48, 0x25, 0x41, 0x48, + 0x0a, 0xd6, 0x87, 0x5e, 0xeb, 0x5c, 0x2b, 0x8c, 0x9b, 0x82, 0x9e, 0x87, + 0x1d, 0xab, 0xc1, 0x82, 0xa7, 0x18, 0x44, 0x89, 0x4a, 0xf2, 0x58, 0x2d, + 0x2f, 0x5c, 0xae, 0x06, 0x9a, 0x48, 0x52, 0x9c, 0x57, 0x52, 0x92, 0xb5, + 0x6f, 0xf3, 0x82, 0x4f, 0x78, 0xad, 0x56, 0x85, 0xda, 0x2e, 0x4b, 0x2c, + 0xe4, 0xea, 0x4c, 0x17, 0x55, 0x31, 0xf8, 0x91, 0xec, 0xa8, 0x7f, 0xb3, + 0x8e, 0xc1, 0x6b, 0x44, 0xec, 0x23, 0x41, 0xc2, 0x52, 0x52, 0xbe, 0x65, + 0x7e, 0x6a, 0xc6, 0x80, 0xa8, 0xf4, 0x43, 0x18, 0xdd, 0x9e, 0x11, 0x1e, + 0x43, 0x92, 0x23, 0x59, 0x27, 0x34, 0xe5, 0xe0, 0x5b, 0x56, 0x97, 0x15, + 0x02, 0x17, 0xbe, 0x6d, 0xc0, 0x9f, 0x35, 0x72, 0xa4, 0x3a, 0x10, 0x92, + 0xad, 0x1e, 0x60, 0x80, 0x12, 0x49, 0xd1, 0xea, 0x3a, 0x57, 0x45, 0x85, + 0x16, 0xfb, 0x83, 0xaf, 0xb6, 0xe1, 0x56, 0x35, 0x08, 0x4f, 0x30, 0xda, + 0xb5, 0x45, 0xe4, 0x88, 0xe3, 0xaf, 0x21, 0x1c, 0xca, 0xed, 0x16, 0xd9, + 0xd9, 0x24, 0x12, 0x52, 0x10, 0xa0, 0x39, 0x00, 0x3e, 0x76, 0xfa, 0x46, + 0x48, 0xbb, 0xcb, 0xc2, 0x2e, 0x09, 0xb5, 0xdb, 0x5c, 0x81, 0x22, 0x02, + 0xe5, 0x19, 0xc9, 0x12, 0xa5, 0x76, 0x6b, 0x7a, 0x3b, 0xdc, 0xc1, 0x49, + 0xed, 0x57, 0xb1, 0xce, 0xda, 0xd3, 0xd0, 0x28, 0xf9, 0xe8, 0x20, 0x03, + 0xb4, 0x9a, 0xd3, 0x92, 0xa6, 0xdd, 0x7e, 0x9a, 0xf5, 0xc6, 0xe8, 0x20, + 0x43, 0xb7, 0x4a, 0x69, 0xa4, 0xa5, 0xb9, 0xb0, 0x9c, 0x71, 0x73, 0x9e, + 0x6d, 0x4b, 0xe5, 0x75, 0x96, 0x92, 0xa4, 0xbd, 0xb4, 0xa5, 0x5a, 0x0b, + 0x1a, 0x2a, 0xde, 0x80, 0x29, 0xd1, 0xa9, 0xf5, 0x61, 0x3e, 0x88, 0x90, + 0x7e, 0x65, 0xc3, 0x0d, 0xbb, 0xdc, 0x59, 0x86, 0xca, 0x2f, 0x0e, 0xca, + 0x71, 0xb9, 0x6d, 0x2a, 0x54, 0xb0, 0x97, 0xde, 0x8a, 0x90, 0x1b, 0x53, + 0x08, 0x5a, 0xf4, 0x14, 0xe3, 0x6a, 0x01, 0x40, 0x28, 0xf9, 0xc9, 0x5f, + 0x53, 0xbe, 0xfe, 0x6b, 0xec, 0x37, 0x33, 0x5b, 0xda, 0x24, 0x58, 0x5b, + 0x7e, 0x32, 0xd2, 0xca, 0x57, 0x2e, 0x43, 0x72, 0x43, 0x69, 0x6d, 0xf6, + 0xd6, 0x92, 0xc8, 0x0e, 0xa5, 0x2b, 0x47, 0x6c, 0x02, 0x9d, 0x49, 0xd0, + 0x58, 0x01, 0x5a, 0x3f, 0x9b, 0xae, 0xeb, 0x65, 0xb6, 0xec, 0x51, 0x22, + 0x6f, 0xf2, 0x69, 0x37, 0x2e, 0x66, 0x12, 0x82, 0x6e, 0xce, 0xa1, 0x82, + 0xe3, 0x60, 0xf3, 0x76, 0x4c, 0xb0, 0x94, 0xa8, 0x36, 0x36, 0x01, 0xf3, + 0xc8, 0x24, 0xeb, 0x98, 0x9d, 0x74, 0xf3, 0x61, 0x56, 0x35, 0x77, 0x66, + 0xdc, 0x9b, 0xf4, 0xe5, 0xbd, 0x71, 0x9b, 0x0d, 0x52, 0xd9, 0x88, 0x1d, + 0x5b, 0x4c, 0xc4, 0x6c, 0x1e, 0x55, 0x21, 0xb4, 0x20, 0x84, 0xa4, 0xb6, + 0x4f, 0x29, 0x3d, 0x55, 0xb4, 0xf7, 0xd1, 0x79, 0x24, 0x1e, 0xdb, 0xb3, + 0xc3, 0x71, 0xdd, 0xb9, 0xcd, 0xd4, 0xfb, 0x5b, 0x99, 0x2f, 0x90, 0xa5, + 0x6c, 0x25, 0x86, 0x02, 0x53, 0x15, 0x85, 0x13, 0xa7, 0x09, 0x75, 0xd2, + 0x0b, 0xce, 0xf4, 0xd7, 0x9a, 0x00, 0x4f, 0x51, 0xa4, 0x9a, 0xdf, 0x89, + 0x37, 0x6f, 0xbb, 0xc7, 0x42, 0x3c, 0xb1, 0xfb, 0x2b, 0x4f, 0x17, 0x9c, + 0x62, 0xcf, 0x0b, 0xf9, 0xa2, 0xda, 0x4a, 0x16, 0x5b, 0x2a, 0x78, 0xa7, + 0xcf, 0x53, 0x9b, 0x1e, 0x77, 0x50, 0x90, 0x7a, 0x10, 0x7b, 0xcc, 0x6d, + 0xbf, 0x2c, 0x95, 0x88, 0xcf, 0x67, 0x19, 0x7a, 0x33, 0x52, 0x60, 0xc2, + 0x0b, 0x60, 0xcb, 0x0b, 0xf3, 0xc9, 0x3c, 0xae, 0x34, 0xe2, 0x81, 0xea, + 0xbd, 0xa1, 0x7a, 0x50, 0x47, 0x32, 0xf9, 0x82, 0x8f, 0x2e, 0x88, 0xae, + 0x94, 0x61, 0xf2, 0xb2, 0x0b, 0xf3, 0xd9, 0x2a, 0xa1, 0xb1, 0x6c, 0x25, + 0xd4, 0xb9, 0x19, 0x32, 0x59, 0x74, 0x39, 0xbe, 0x44, 0x87, 0x1c, 0x28, + 0x6d, 0xd4, 0x68, 0x28, 0xa1, 0x3e, 0xf6, 0xe7, 0x36, 0xf9, 0x41, 0x20, + 0x12, 0x45, 0x31, 0xd5, 0x8c, 0xf4, 0x42, 0x54, 0x59, 0xb2, 0x78, 0x6b, + 0xc7, 0x74, 0xcc, 0x9a, 0xe9, 0x5d, 0x9b, 0x27, 0x71, 0x9b, 0x6d, 0xd5, + 0xfe, 0x81, 0x02, 0x61, 0x49, 0xf2, 0x39, 0x67, 0xb8, 0x02, 0xea, 0x12, + 0xa6, 0x96, 0x75, 0xd5, 0x6d, 0x95, 0x78, 0x81, 0x5f, 0x42, 0x27, 0xaa, + 0x77, 0x55, 0xf6, 0x4d, 0x82, 0xda, 0x72, 0x0b, 0x15, 0xdf, 0x11, 0xb9, + 0xba, 0xec, 0xd7, 0x2f, 0x11, 0xb9, 0xe5, 0xce, 0x70, 0x8e, 0xd5, 0xb7, + 0x11, 0xae, 0xc9, 0x68, 0x4a, 0x40, 0x08, 0x08, 0x57, 0x54, 0x81, 0xad, + 0x7a, 0xce, 0xc9, 0xcf, 0x00, 0xb2, 0x4b, 0xae, 0x43, 0xc3, 0xe4, 0x35, + 0x90, 0x68, 0xdf, 0x6c, 0xb2, 0xdf, 0xb3, 0xdd, 0x17, 0xbd, 0x87, 0x24, + 0x47, 0x57, 0x21, 0x5e, 0xff, 0x00, 0x58, 0x72, 0xa8, 0xfa, 0xc9, 0xa8, + 0x69, 0xa0, 0x9a, 0x66, 0xee, 0x32, 0xfc, 0x4b, 0x17, 0xfd, 0xaa, 0xb6, + 0x7d, 0xf8, 0xa7, 0xba, 0x44, 0xe3, 0x27, 0xc4, 0x71, 0x7f, 0xda, 0xab, + 0x67, 0xdf, 0x8a, 0x7b, 0xa8, 0x24, 0x28, 0xac, 0x6f, 0x47, 0xa9, 0x14, + 0x50, 0x19, 0xa4, 0xce, 0x38, 0xfc, 0x8f, 0x65, 0xdf, 0x44, 0x48, 0xfe, + 0x03, 0x4e, 0x74, 0x99, 0xc7, 0x1f, 0x91, 0xec, 0xbb, 0xe8, 0x89, 0x1f, + 0xc0, 0x68, 0x06, 0xf8, 0xdf, 0x17, 0x6f, 0xe6, 0x0f, 0xb2, 0xb6, 0x56, + 0xb8, 0xdf, 0x17, 0x6f, 0xe6, 0x0f, 0xb2, 0xb6, 0x50, 0x05, 0x14, 0x51, + 0x40, 0x14, 0x51, 0x45, 0x01, 0xe5, 0x40, 0x6f, 0x64, 0x6f, 0xd1, 0x5f, + 0x22, 0x66, 0x56, 0x29, 0x9c, 0x37, 0xe2, 0x5c, 0xbb, 0x55, 0xa9, 0x82, + 0x5b, 0x8c, 0xb7, 0xf2, 0x4c, 0x5d, 0xa1, 0xd0, 0x48, 0x8c, 0xa1, 0xfe, + 0x93, 0xb6, 0xa7, 0xfe, 0x11, 0xdb, 0x20, 0x78, 0x14, 0x0e, 0xf2, 0xaa, + 0xfa, 0xf7, 0x5d, 0x77, 0x55, 0xa7, 0xe1, 0x11, 0x88, 0x4f, 0xc9, 0xb0, + 0x74, 0xdc, 0x71, 0xef, 0x33, 0x28, 0xc7, 0xa4, 0x26, 0xeb, 0x65, 0x74, + 0x0d, 0xab, 0xb6, 0x6f, 0xaa, 0x9b, 0xf5, 0x85, 0xa4, 0x14, 0xeb, 0xb8, + 0x9d, 0x6e, 0xa5, 0x3c, 0x3c, 0xa2, 0x1a, 0xca, 0xc3, 0x39, 0x58, 0xbc, + 0x5c, 0x6e, 0xb6, 0x06, 0xb2, 0x1c, 0x4e, 0x63, 0xef, 0xa2, 0x77, 0x93, + 0xcb, 0x0e, 0x34, 0xc7, 0x6c, 0x95, 0x72, 0xa4, 0x75, 0x5a, 0x01, 0xe6, + 0x28, 0x71, 0xbd, 0x24, 0x94, 0xed, 0x49, 0x52, 0x47, 0x4e, 0xfa, 0xd3, + 0x96, 0x47, 0xb7, 0x64, 0x13, 0xe3, 0x9c, 0xae, 0xe2, 0xdd, 0x99, 0xb8, + 0xcc, 0xa8, 0x30, 0xdb, 0xaa, 0x6f, 0xca, 0x9c, 0x25, 0x6d, 0xac, 0x38, + 0xe2, 0x13, 0xcc, 0x1a, 0x6d, 0x1d, 0x98, 0xd1, 0xde, 0xf6, 0x49, 0x25, + 0x3d, 0xc5, 0x03, 0xf0, 0x78, 0xcc, 0xa0, 0xc7, 0xb8, 0xb7, 0x12, 0x22, + 0x15, 0x12, 0xcd, 0x7a, 0x4b, 0x97, 0x2b, 0x43, 0x24, 0x79, 0xb1, 0xce, + 0xff, 0x00, 0x9e, 0xc2, 0xf5, 0x16, 0x5e, 0x25, 0x49, 0x4f, 0xe8, 0x2d, + 0x26, 0xad, 0x7c, 0x66, 0xfd, 0x6a, 0xb2, 0x3b, 0x02, 0xc3, 0x36, 0xdd, + 0x26, 0x3c, 0xc9, 0xcd, 0x6d, 0xd9, 0x4b, 0x68, 0xac, 0x4b, 0x94, 0x14, + 0xa0, 0xe2, 0x54, 0xb1, 0xf0, 0x96, 0x48, 0xe6, 0xd1, 0xef, 0x4a, 0x86, + 0xba, 0x77, 0x6b, 0x35, 0x9c, 0x38, 0xf5, 0x32, 0x83, 0x4b, 0x2a, 0x5d, + 0x0f, 0x78, 0xcc, 0x5b, 0xbc, 0x80, 0x6f, 0x96, 0x64, 0x44, 0x58, 0x7d, + 0xae, 0x56, 0x27, 0x5d, 0x54, 0xa7, 0x1e, 0x92, 0xd7, 0x7a, 0x42, 0x10, + 0xdf, 0x2a, 0x18, 0x69, 0x47, 0x44, 0x72, 0xef, 0x7d, 0x09, 0x4e, 0xc5, + 0x73, 0xe3, 0x32, 0xb1, 0xb9, 0x8e, 0xc6, 0x56, 0x4d, 0xe4, 0xf2, 0x32, + 0x19, 0xa2, 0x40, 0x75, 0xb9, 0xa9, 0x0b, 0x0c, 0xa9, 0xa5, 0x00, 0xeb, + 0x0d, 0x85, 0x0d, 0x24, 0x24, 0x29, 0x3d, 0xc3, 0x6a, 0x1a, 0x51, 0xde, + 0xf7, 0x51, 0x96, 0xd5, 0x5e, 0xe3, 0xde, 0x55, 0xfc, 0x9a, 0x7e, 0x74, + 0xcb, 0x04, 0x27, 0x9d, 0x8b, 0x1d, 0x2d, 0x21, 0xa5, 0x32, 0xa6, 0xb9, + 0x42, 0xf4, 0x87, 0x16, 0xe2, 0x79, 0x3b, 0x35, 0xa9, 0x48, 0xe7, 0xd2, + 0xd2, 0x52, 0x90, 0x06, 0x94, 0x9e, 0xba, 0xe7, 0x2b, 0x1d, 0x94, 0xd4, + 0xab, 0xcd, 0xf6, 0x6b, 0xd2, 0xa6, 0x2e, 0x68, 0x7d, 0x84, 0x5b, 0x3b, + 0x43, 0x19, 0x2f, 0x04, 0x25, 0xb6, 0x9a, 0x43, 0xfc, 0xbc, 0x85, 0xd2, + 0x12, 0x91, 0xce, 0x0a, 0x4e, 0xce, 0x86, 0x87, 0x4a, 0xc9, 0x63, 0x93, + 0x5d, 0xfa, 0x1b, 0x99, 0x99, 0x79, 0xc7, 0xee, 0xc6, 0xcf, 0x8c, 0x29, + 0x32, 0x6d, 0x96, 0xd7, 0xc8, 0xf2, 0x61, 0x15, 0xd7, 0x90, 0x58, 0x7c, + 0x07, 0x12, 0x9e, 0x66, 0xc2, 0x8a, 0x5d, 0x41, 0xe6, 0xd7, 0x30, 0xe5, + 0x52, 0x14, 0x01, 0x20, 0xf5, 0x39, 0xbd, 0xc6, 0x6a, 0xeb, 0x75, 0x91, + 0x26, 0xe6, 0x86, 0xd8, 0x9a, 0xe9, 0x8e, 0xa6, 0x2d, 0x6d, 0xc2, 0x6a, + 0x74, 0x82, 0xa6, 0x82, 0xf9, 0x5e, 0x71, 0x07, 0x61, 0xa5, 0xf9, 0xe3, + 0x4a, 0xe6, 0x00, 0x04, 0xf5, 0x27, 0xc2, 0x55, 0xfb, 0x06, 0x4f, 0x1b, + 0x17, 0x96, 0xcd, 0x9a, 0xe1, 0x02, 0xdf, 0x24, 0x36, 0xa5, 0x26, 0xdb, + 0x0d, 0x80, 0x5b, 0x20, 0xef, 0x69, 0x53, 0xaa, 0x3c, 0xea, 0x75, 0x49, + 0xfe, 0x93, 0xcd, 0xf3, 0xba, 0x90, 0x6b, 0x7c, 0x0c, 0xab, 0x1e, 0xb2, + 0xdb, 0x10, 0xa8, 0x16, 0x0b, 0xb3, 0x76, 0xf7, 0x58, 0x4c, 0x96, 0x5c, + 0x85, 0x6c, 0x75, 0xf4, 0x39, 0xcc, 0x3c, 0xee, 0x65, 0x36, 0x93, 0xca, + 0xe0, 0x20, 0x85, 0x73, 0x90, 0x7a, 0x6f, 0x7a, 0xeb, 0x4c, 0x92, 0x92, + 0x36, 0x58, 0xf1, 0xfb, 0xe8, 0x71, 0xe7, 0xd7, 0x2c, 0x5a, 0xd7, 0x2b, + 0x97, 0xb7, 0x79, 0x4b, 0x12, 0x67, 0x3a, 0x90, 0x4e, 0x92, 0x56, 0x40, + 0x69, 0xa0, 0x37, 0xf0, 0x50, 0x82, 0x91, 0xb3, 0xae, 0xbb, 0x35, 0x3f, + 0x6c, 0xb4, 0xda, 0x2c, 0x85, 0xd7, 0xd9, 0x6c, 0x99, 0x2b, 0x47, 0xbf, + 0x49, 0x79, 0x6a, 0x75, 0xf7, 0x40, 0xfd, 0x25, 0x2b, 0x6a, 0x23, 0xd5, + 0xdd, 0xe8, 0x14, 0x82, 0xac, 0xc3, 0x3a, 0xcb, 0xd6, 0x23, 0xe1, 0xf6, + 0x2f, 0x72, 0x20, 0x13, 0xa5, 0x5c, 0x66, 0x29, 0x0e, 0xb8, 0x47, 0x4f, + 0x82, 0x06, 0xda, 0x49, 0xf5, 0xed, 0xd2, 0x3c, 0x51, 0xba, 0x9b, 0xc6, + 0xb8, 0x7e, 0x98, 0xf1, 0x94, 0x6f, 0xf7, 0x17, 0xee, 0x2e, 0x3e, 0x42, + 0xa4, 0x36, 0x1d, 0x51, 0x4b, 0xe7, 0xfd, 0xaa, 0xcf, 0x9e, 0xe8, 0xfd, + 0x52, 0x43, 0x7e, 0x84, 0x01, 0xa1, 0x50, 0x49, 0x2e, 0xac, 0xba, 0xd9, + 0x21, 0x45, 0x8b, 0x33, 0x32, 0xaf, 0x32, 0x47, 0x9a, 0x5a, 0x88, 0xdf, + 0x44, 0x1f, 0xd7, 0x5a, 0xb4, 0x94, 0x7b, 0x14, 0x77, 0xea, 0xa8, 0x36, + 0x30, 0x69, 0x97, 0x15, 0x22, 0x7d, 0xd6, 0x44, 0x68, 0x72, 0x7c, 0xbd, + 0xc9, 0xcd, 0xc7, 0x66, 0x3b, 0x72, 0x04, 0x45, 0x2f, 0x5f, 0x93, 0x71, + 0xc4, 0xed, 0x2b, 0x3a, 0xda, 0xba, 0x14, 0xec, 0x9e, 0x9e, 0x25, 0xbd, + 0xd9, 0x16, 0xbb, 0x34, 0x34, 0x30, 0x90, 0xcc, 0x66, 0x90, 0x34, 0xdb, + 0x2d, 0x20, 0x0d, 0x7a, 0x82, 0x45, 0x25, 0x66, 0xbc, 0x47, 0xb7, 0x59, + 0x5b, 0x6c, 0x4a, 0x96, 0x88, 0x21, 0xf3, 0xca, 0xc3, 0x41, 0x25, 0xd9, + 0x2f, 0x9f, 0x43, 0x6d, 0xa7, 0x6a, 0x51, 0xf5, 0x24, 0x13, 0xec, 0xad, + 0x69, 0xd1, 0x94, 0xf7, 0x5c, 0x79, 0x98, 0xce, 0xb4, 0x61, 0xb7, 0x51, + 0xb6, 0x0c, 0x6b, 0x46, 0x37, 0x01, 0x4d, 0xa5, 0xd5, 0x24, 0xb8, 0xe1, + 0x75, 0xd7, 0x1e, 0x70, 0xb8, 0xf3, 0xee, 0x10, 0x01, 0x5a, 0x89, 0xea, + 0xa5, 0x74, 0x03, 0xd0, 0x00, 0x00, 0x68, 0x00, 0x2a, 0x26, 0xe5, 0x92, + 0x3c, 0xf7, 0xbd, 0x42, 0x41, 0x65, 0x04, 0xeb, 0x9c, 0x80, 0x54, 0x7d, + 0x9e, 0x03, 0xed, 0xaa, 0xba, 0xff, 0x00, 0x94, 0x5c, 0xa3, 0x34, 0xdd, + 0xeb, 0x2c, 0x9f, 0x17, 0x03, 0xc7, 0xcf, 0x9c, 0xa9, 0x17, 0x87, 0x02, + 0xee, 0x72, 0x87, 0xa1, 0x98, 0xe9, 0x27, 0x93, 0x7d, 0xdb, 0x57, 0x31, + 0xd7, 0xe6, 0x8a, 0x81, 0xfc, 0x6e, 0x66, 0x19, 0xb3, 0x6e, 0xc3, 0xe0, + 0x16, 0x07, 0x22, 0x5b, 0x68, 0xf3, 0x1e, 0xc8, 0xaf, 0x49, 0x4b, 0x4c, + 0xa3, 0xae, 0xbd, 0xed, 0x2a, 0x20, 0x28, 0xf8, 0xf5, 0x3b, 0xfd, 0x4a, + 0xd5, 0x7b, 0xaa, 0x5b, 0xbe, 0xf3, 0xfd, 0x0c, 0xa4, 0xab, 0x55, 0xf4, + 0x45, 0xb9, 0x75, 0xc9, 0x2c, 0x7c, 0x3c, 0xc5, 0xa7, 0x65, 0x59, 0x7d, + 0xc1, 0xb8, 0x2c, 0x84, 0x79, 0x89, 0x71, 0x5e, 0xfa, 0xf1, 0xd7, 0x44, + 0x21, 0x3d, 0xea, 0x52, 0x8e, 0xb4, 0x3c, 0x3b, 0xfb, 0xb6, 0x6b, 0x9b, + 0xf0, 0x6f, 0xb3, 0x5c, 0xed, 0xbc, 0x37, 0x17, 0x5b, 0xdb, 0x45, 0x9b, + 0xae, 0x47, 0x3a, 0x45, 0xf2, 0x63, 0x47, 0xbd, 0xb5, 0x49, 0x5f, 0x3a, + 0x52, 0x7d, 0x61, 0x1c, 0x80, 0x8f, 0x03, 0xb1, 0x4b, 0xdc, 0x3b, 0xe0, + 0x44, 0x66, 0xae, 0x4d, 0x65, 0x9c, 0x54, 0xba, 0x39, 0x9c, 0x65, 0x9b, + 0xe6, 0x4b, 0x93, 0x09, 0x5c, 0x48, 0x7d, 0x76, 0x12, 0xcb, 0x47, 0xcd, + 0xe9, 0xd3, 0xa9, 0x1a, 0xd8, 0xd8, 0x02, 0xae, 0xc0, 0x3a, 0x77, 0x56, + 0x15, 0x26, 0xe7, 0x27, 0x26, 0x6f, 0x4e, 0x0a, 0x11, 0xd2, 0x84, 0x5e, + 0x32, 0x7c, 0x47, 0x17, 0xfd, 0xaa, 0xb6, 0x7d, 0xf8, 0xa7, 0xba, 0x44, + 0xe3, 0x20, 0xd4, 0x1c, 0x5c, 0x7f, 0xf7, 0x55, 0xb3, 0xef, 0xc5, 0x3d, + 0xd5, 0x0b, 0x9e, 0x57, 0xdf, 0xd0, 0x91, 0x45, 0x64, 0x8d, 0x9a, 0x28, + 0x43, 0xc9, 0x9a, 0x4c, 0xe3, 0x8f, 0xc8, 0xf6, 0x5d, 0xf4, 0x44, 0x8f, + 0xe0, 0x34, 0xe7, 0x50, 0x1c, 0x45, 0xb2, 0xc8, 0xc9, 0x30, 0x5b, 0xe6, + 0x3f, 0x11, 0xd6, 0xd9, 0x91, 0x70, 0x82, 0xec, 0x66, 0x9c, 0x73, 0x7c, + 0x89, 0x52, 0xd2, 0x40, 0x2a, 0xd7, 0x5d, 0x6e, 0x84, 0x93, 0x71, 0xbe, + 0x2e, 0xdf, 0xcc, 0x1f, 0x65, 0x6c, 0xaa, 0xfd, 0xbf, 0xc7, 0x2a, 0x1b, + 0x4a, 0x03, 0x58, 0x11, 0x09, 0x1a, 0xdf, 0x69, 0x2f, 0xaf, 0xfd, 0x35, + 0xeb, 0x9b, 0x8c, 0xbf, 0xd4, 0xe0, 0x3f, 0xbd, 0x97, 0xfe, 0x5a, 0x01, + 0xfa, 0x8a, 0x41, 0xe7, 0xe3, 0x2f, 0xf5, 0x58, 0x0f, 0xef, 0x65, 0xff, + 0x00, 0x96, 0x8e, 0x6e, 0x32, 0xff, 0x00, 0x53, 0x80, 0xfe, 0xf6, 0x5f, + 0xf9, 0x68, 0x07, 0xea, 0x29, 0x07, 0x9b, 0x8c, 0xbf, 0xd4, 0xe0, 0x3f, + 0xbd, 0x97, 0xfe, 0x5a, 0x39, 0xf8, 0xcb, 0xfd, 0x56, 0x03, 0xfb, 0xd9, + 0x7f, 0xe5, 0xa0, 0x1f, 0xa8, 0x23, 0x74, 0x83, 0xcf, 0xc6, 0x5f, 0xea, + 0xb0, 0x1f, 0xde, 0x4b, 0xff, 0x00, 0x2d, 0x1c, 0xfc, 0x65, 0xfe, 0xab, + 0x01, 0xfd, 0xe4, 0xbf, 0xf2, 0xd0, 0x14, 0x97, 0x15, 0x71, 0x49, 0x58, + 0x87, 0x12, 0xe5, 0x5a, 0xac, 0xc8, 0x4b, 0x31, 0xf2, 0x49, 0x26, 0xfd, + 0x8b, 0x2f, 0x5a, 0x44, 0x7b, 0xdb, 0x29, 0xf7, 0xf8, 0xbb, 0xee, 0x4a, + 0x24, 0xb5, 0xb4, 0xf5, 0xe8, 0x49, 0x00, 0x7c, 0x1a, 0xb3, 0x2c, 0x37, + 0x9b, 0x06, 0x55, 0x83, 0xc5, 0xc8, 0x5a, 0xb7, 0x31, 0x29, 0xf9, 0x4c, + 0x33, 0xe4, 0xe2, 0x43, 0x8a, 0x42, 0x3c, 0xe5, 0xa5, 0x1c, 0x8f, 0x80, + 0x47, 0x30, 0x6d, 0x44, 0xec, 0x1f, 0xd1, 0x23, 0xa5, 0x70, 0x71, 0x73, + 0x0c, 0xe2, 0xb6, 0x77, 0x88, 0xb9, 0x6b, 0x94, 0x30, 0xc8, 0xf2, 0xa3, + 0x3c, 0xdc, 0xdb, 0x7c, 0x98, 0xae, 0xc9, 0x0f, 0x47, 0x92, 0xd2, 0xb9, + 0x90, 0xb4, 0x15, 0x27, 0x5b, 0xef, 0x1d, 0x7a, 0x75, 0x35, 0x5f, 0xf0, + 0xb7, 0x26, 0x8e, 0xbb, 0x82, 0x15, 0x36, 0x30, 0x8d, 0x6b, 0xcb, 0x5f, + 0x75, 0x46, 0x3a, 0xba, 0x22, 0x05, 0xe9, 0x03, 0x53, 0xa1, 0x11, 0xde, + 0x8e, 0xd0, 0xe9, 0xe4, 0x0e, 0x9b, 0xe6, 0x20, 0x75, 0xad, 0xa9, 0x62, + 0x59, 0x83, 0xeb, 0xf5, 0x32, 0xab, 0x98, 0xe2, 0x6b, 0xa7, 0xd0, 0xbb, + 0xaf, 0xd8, 0xcc, 0xc7, 0xf1, 0xc7, 0x84, 0x6b, 0xf4, 0xab, 0x84, 0x86, + 0x39, 0x43, 0x90, 0x92, 0x50, 0xd4, 0x57, 0xd0, 0x85, 0x02, 0xa8, 0xdd, + 0x92, 0x46, 0x90, 0x14, 0x01, 0x47, 0x7f, 0x30, 0xd8, 0xd9, 0x22, 0xb5, + 0x8c, 0xaa, 0xd7, 0x7a, 0xb5, 0xcf, 0x85, 0x75, 0xb7, 0xaa, 0x1d, 0x95, + 0xc4, 0x21, 0x31, 0xe5, 0x1f, 0x35, 0x0f, 0x34, 0xb4, 0x05, 0x20, 0xb4, + 0x40, 0x3b, 0x75, 0x2a, 0xf3, 0x79, 0x13, 0xe7, 0x25, 0x43, 0xbb, 0xbb, + 0x7a, 0x2d, 0x38, 0xeb, 0x8e, 0x4f, 0x9d, 0x2e, 0x35, 0xf2, 0x34, 0x06, + 0x27, 0xbc, 0x1f, 0x7c, 0xb4, 0x55, 0xdb, 0x73, 0x84, 0xf2, 0xa9, 0x49, + 0x42, 0xd4, 0x5b, 0x6d, 0x64, 0x6b, 0x99, 0x41, 0x24, 0x12, 0x37, 0xa0, + 0x69, 0x86, 0x1c, 0x3c, 0x36, 0xc4, 0x96, 0x97, 0x0a, 0x0d, 0xbd, 0x0f, + 0x32, 0xd8, 0x6d, 0x0e, 0x34, 0xca, 0x54, 0xe9, 0x00, 0x6b, 0xaa, 0xb5, + 0xb2, 0x75, 0xe2, 0x4d, 0x46, 0x86, 0x9e, 0x31, 0xb8, 0xf7, 0x89, 0xac, + 0xb7, 0xb0, 0xaf, 0x8b, 0xd8, 0xf3, 0x56, 0xe7, 0xfb, 0xae, 0x97, 0x0c, + 0x65, 0xdc, 0x63, 0xb2, 0xf4, 0xc6, 0xe5, 0xc8, 0x05, 0xae, 0xdc, 0x34, + 0x84, 0x17, 0x0b, 0x49, 0x6c, 0x2c, 0x2f, 0x49, 0x1b, 0x6f, 0x9c, 0x27, + 0x7b, 0x3b, 0x1b, 0xd5, 0x34, 0xda, 0xb0, 0x7c, 0x7a, 0x24, 0x66, 0x53, + 0x2e, 0x13, 0x57, 0x09, 0x28, 0xda, 0xdd, 0x7d, 0xf4, 0x03, 0xdb, 0x38, + 0x54, 0x54, 0xa5, 0xa9, 0x3f, 0x07, 0x7b, 0x51, 0xd7, 0x4e, 0x9d, 0x00, + 0xee, 0xa2, 0x46, 0x54, 0x80, 0x9d, 0x45, 0x8a, 0x48, 0x00, 0xf9, 0xce, + 0x2b, 0xa0, 0xf6, 0xeb, 0xff, 0x00, 0x35, 0x5d, 0x5d, 0xb8, 0xb3, 0x06, + 0xe5, 0x70, 0x55, 0xaa, 0xc2, 0xec, 0xdc, 0xa2, 0xe2, 0x09, 0x06, 0x05, + 0x81, 0xae, 0xdf, 0x93, 0xc3, 0xdf, 0x1c, 0x04, 0x36, 0xd8, 0xdf, 0x8a, + 0xd6, 0x2b, 0x4f, 0xb3, 0xcf, 0x99, 0xec, 0x67, 0xf6, 0x88, 0x7e, 0x0d, + 0xcb, 0x66, 0x7d, 0xe2, 0xdd, 0x01, 0x1d, 0x99, 0x70, 0x2d, 0x69, 0xee, + 0x6d, 0xbe, 0xba, 0xff, 0x00, 0xb0, 0xa4, 0x1c, 0xd7, 0x89, 0x96, 0xfb, + 0x4b, 0xad, 0xc4, 0x93, 0x38, 0x31, 0x2a, 0x47, 0x48, 0xf0, 0x22, 0xb6, + 0xa7, 0xe6, 0x3e, 0x7c, 0x39, 0x5b, 0x40, 0x2b, 0x3f, 0xdc, 0x07, 0xaf, + 0xc6, 0xa9, 0x0e, 0x23, 0x71, 0x3d, 0x16, 0x82, 0xa8, 0xd9, 0x4e, 0x55, + 0x1e, 0xc8, 0xe9, 0x25, 0x3e, 0xe0, 0x62, 0xee, 0x22, 0x75, 0xcd, 0x5d, + 0xfe, 0x63, 0xd2, 0xcf, 0xbd, 0x47, 0x3b, 0xef, 0xe4, 0x05, 0x5d, 0x7a, + 0x1a, 0xe3, 0xe1, 0xe5, 0x87, 0x8c, 0xb9, 0x84, 0x59, 0x3f, 0x8b, 0xfc, + 0x5e, 0x17, 0x0a, 0xec, 0x32, 0x92, 0xa5, 0x2a, 0xeb, 0x24, 0x2d, 0xcb, + 0x8c, 0xde, 0xa3, 0x5d, 0xa3, 0xee, 0x6d, 0xe5, 0x6f, 0x5b, 0xda, 0x42, + 0x53, 0x4d, 0x54, 0xe9, 0xf8, 0x56, 0x5f, 0x9b, 0x1a, 0x2a, 0x54, 0xf1, + 0x3c, 0x21, 0xcb, 0x39, 0xce, 0xa5, 0x5b, 0x23, 0x2e, 0x5e, 0x53, 0x7d, + 0x87, 0xc3, 0xd8, 0x4b, 0x47, 0x3a, 0x1a, 0x92, 0x53, 0x32, 0xfb, 0x21, + 0x3d, 0x3a, 0xa2, 0x32, 0x49, 0x43, 0x3b, 0xde, 0xb9, 0x96, 0x54, 0x47, + 0x88, 0x14, 0x85, 0x88, 0x5d, 0xf8, 0x8b, 0x9a, 0xce, 0x77, 0xf1, 0x29, + 0x83, 0x39, 0x61, 0x8d, 0x23, 0x68, 0x7f, 0x31, 0xbf, 0xab, 0xb7, 0x9c, + 0xfa, 0x77, 0xa3, 0xa7, 0x9c, 0x05, 0x29, 0x1f, 0xa8, 0xd8, 0x56, 0xb5, + 0xd3, 0x54, 0xcf, 0x80, 0xfe, 0x0b, 0xd7, 0xcc, 0x76, 0xf0, 0x6f, 0x77, + 0xa4, 0xe2, 0xb9, 0x75, 0xd7, 0xb5, 0xed, 0x43, 0xd7, 0x79, 0x12, 0x94, + 0xdf, 0x30, 0x3b, 0x0a, 0x2d, 0x84, 0xe9, 0x67, 0xe7, 0x95, 0x0a, 0xbc, + 0x59, 0x6f, 0x8c, 0x0c, 0x34, 0x86, 0x99, 0x8d, 0xc3, 0xf6, 0xdb, 0x6d, + 0x21, 0x28, 0x4a, 0x57, 0x2d, 0x21, 0x29, 0x1d, 0xc0, 0x00, 0x9e, 0x83, + 0xa7, 0x75, 0x65, 0x3a, 0xb2, 0x9f, 0x2c, 0xda, 0x14, 0xa3, 0x0e, 0x0a, + 0xfb, 0x02, 0xfc, 0x18, 0xf1, 0xd8, 0xd7, 0x04, 0xe4, 0x1c, 0x4a, 0xbb, + 0x4e, 0xce, 0xef, 0xeb, 0x57, 0x3b, 0x8b, 0x9c, 0xea, 0x8c, 0x60, 0xae, + 0x9d, 0x39, 0x09, 0x25, 0x7a, 0x3d, 0x34, 0xa2, 0x53, 0xa1, 0xf0, 0x45, + 0x5f, 0x70, 0xe1, 0xc5, 0x87, 0x11, 0xa8, 0x90, 0xe3, 0xb5, 0x1a, 0x3b, + 0x29, 0x08, 0x6d, 0xa6, 0x90, 0x10, 0x94, 0x24, 0x78, 0x00, 0x3a, 0x01, + 0xec, 0xa4, 0x7d, 0xf1, 0x93, 0xfa, 0xac, 0x07, 0xf7, 0x92, 0xff, 0x00, + 0xcb, 0x59, 0x0a, 0xe3, 0x2f, 0xf5, 0x38, 0x0f, 0xef, 0x65, 0xff, 0x00, + 0x96, 0xb3, 0x2e, 0x3e, 0xe8, 0x56, 0x47, 0x4a, 0x41, 0xe7, 0xe3, 0x2f, + 0xf5, 0x58, 0x0f, 0xef, 0x65, 0xff, 0x00, 0x96, 0x8e, 0x7e, 0x32, 0xff, + 0x00, 0x55, 0x80, 0xfe, 0xf2, 0x5f, 0xf9, 0x68, 0x0f, 0x7c, 0x65, 0xf8, + 0x96, 0x2f, 0xfb, 0x55, 0x6c, 0xfb, 0xf1, 0x4f, 0x75, 0x58, 0xdd, 0xac, + 0x5c, 0x4d, 0xc8, 0x66, 0x59, 0x5a, 0xbe, 0xaf, 0x10, 0x62, 0x04, 0x1b, + 0xb4, 0x6b, 0x83, 0xa6, 0x12, 0xa4, 0x17, 0x54, 0x19, 0x58, 0x57, 0x2a, + 0x42, 0xc6, 0xba, 0xea, 0xac, 0xea, 0x00, 0xa2, 0xb0, 0x49, 0xf0, 0xa2, + 0x80, 0xcd, 0x1a, 0xeb, 0x45, 0x14, 0x01, 0x45, 0x14, 0x50, 0x05, 0x14, + 0x51, 0x40, 0x14, 0x51, 0x45, 0x00, 0x51, 0x45, 0x14, 0x01, 0x5f, 0x2b, + 0x7e, 0x11, 0x38, 0x75, 0xcf, 0x0a, 0xc9, 0xef, 0x79, 0x7c, 0x1c, 0x7d, + 0xeb, 0xf6, 0x09, 0x91, 0x86, 0x9c, 0xc8, 0xe0, 0x43, 0x51, 0x4c, 0xa8, + 0x12, 0xdb, 0xdf, 0x24, 0xe6, 0x08, 0xf8, 0x0b, 0x1f, 0x0b, 0x98, 0x74, + 0xdf, 0x30, 0x57, 0x42, 0x0d, 0x7d, 0x53, 0x58, 0x52, 0x42, 0xba, 0x28, + 0x6c, 0x1e, 0x84, 0x50, 0x1f, 0x19, 0xe3, 0x3c, 0x46, 0xe2, 0x5c, 0x7b, + 0x12, 0x2e, 0x96, 0x9b, 0x64, 0x1e, 0x28, 0x63, 0xcd, 0x68, 0x79, 0x75, + 0xad, 0x65, 0xab, 0x93, 0x49, 0xf0, 0x12, 0x23, 0x8d, 0xa9, 0x0e, 0x01, + 0xde, 0x42, 0x08, 0xdf, 0xe7, 0x10, 0x76, 0x58, 0xec, 0xdc, 0x52, 0xcb, + 0xb2, 0x08, 0xcb, 0x7e, 0xd5, 0xc3, 0x29, 0x76, 0x88, 0x6d, 0x90, 0x97, + 0xae, 0x99, 0x1c, 0xe4, 0xdb, 0xe1, 0x32, 0x77, 0xd4, 0xa9, 0x6a, 0x4e, + 0xd5, 0xeb, 0x09, 0xd9, 0xf5, 0x55, 0x8d, 0xc4, 0x0f, 0xc1, 0xd7, 0x05, + 0xc8, 0xee, 0xc6, 0xfb, 0x65, 0x72, 0xe3, 0x87, 0xde, 0xf7, 0xbf, 0x2d, + 0xb1, 0xbd, 0xe4, 0xfc, 0xc7, 0xd2, 0xa4, 0x01, 0xad, 0xf8, 0xec, 0x68, + 0x93, 0xd4, 0x9a, 0xe6, 0xb4, 0x7e, 0x0d, 0xb8, 0x6a, 0xee, 0xcd, 0xdd, + 0xf3, 0x4b, 0xce, 0x49, 0x9c, 0xce, 0x6c, 0x0e, 0x53, 0x7c, 0xb8, 0x29, + 0xd6, 0xc6, 0xbf, 0x50, 0x6b, 0x63, 0xa0, 0xe8, 0x49, 0x1e, 0xaa, 0xdd, + 0x5c, 0xd4, 0x4b, 0x19, 0x30, 0x76, 0xd4, 0xdb, 0xcb, 0x45, 0x4d, 0x17, + 0x3f, 0x72, 0x6d, 0xc3, 0xdc, 0xeb, 0x42, 0x2e, 0x9c, 0x5e, 0xc9, 0x04, + 0x80, 0xf0, 0x62, 0xcc, 0xca, 0xe1, 0x59, 0xa0, 0x10, 0x85, 0x23, 0xb3, + 0x2b, 0xef, 0x79, 0x1e, 0x79, 0x27, 0x9f, 0xcd, 0x3a, 0x1d, 0xd4, 0xe9, + 0x03, 0x83, 0xfc, 0x55, 0xcc, 0xed, 0xcd, 0xc3, 0xcd, 0xf2, 0xe8, 0x18, + 0x56, 0x3c, 0x47, 0xfa, 0xb7, 0x8a, 0x47, 0x0d, 0x23, 0x93, 0xa7, 0x9a, + 0xb7, 0x3b, 0xbc, 0x07, 0x7f, 0x38, 0xaf, 0xa1, 0x6c, 0xf6, 0x9b, 0x5d, + 0x9a, 0x0a, 0x60, 0x5a, 0x2d, 0xd1, 0x2d, 0xf1, 0x11, 0xf0, 0x59, 0x8a, + 0xca, 0x5a, 0x40, 0xf6, 0x25, 0x20, 0x0a, 0xec, 0x00, 0x0a, 0xca, 0x53, + 0x94, 0xb9, 0x35, 0x8c, 0x23, 0x1e, 0x11, 0x5f, 0x70, 0xe3, 0x82, 0xfc, + 0x36, 0xc0, 0x43, 0x4e, 0xe3, 0xd8, 0xcc, 0x51, 0x35, 0xb1, 0xf1, 0xe9, + 0x43, 0xb7, 0x92, 0x4f, 0xa7, 0x9d, 0x7b, 0xe5, 0x3f, 0x37, 0x43, 0xd5, + 0x56, 0x0f, 0x28, 0xac, 0x81, 0xae, 0xea, 0x2a, 0xa5, 0x8c, 0x68, 0x56, + 0x68, 0xa2, 0x80, 0x28, 0xa2, 0x8a, 0x00, 0xa2, 0x8a, 0x28, 0x03, 0x42, + 0x8a, 0x28, 0xa0, 0x30, 0x52, 0x0f, 0x7d, 0x15, 0x9a, 0x28, 0x02, 0x8a, + 0x28, 0xa0, 0x0a, 0x28, 0xa2, 0x80, 0x28, 0xa2, 0x8a, 0x00, 0xa2, 0x8a, + 0x28, 0x02, 0x8a, 0x28, 0xa0, 0x0a, 0x28, 0xa2, 0x80, 0x35, 0x45, 0x14, + 0x50, 0x05, 0x14, 0x51, 0x40, 0x14, 0x51, 0x45, 0x00, 0x51, 0x45, 0x14, + 0x01, 0x45, 0x14, 0x50, 0x05, 0x14, 0x51, 0x40, 0x14, 0x51, 0x45, 0x00, + 0x51, 0x45, 0x14, 0x07, 0xff, 0xd9 +}; diff --git a/camera/libcameraservice/FakeCamera.cpp b/camera/libcameraservice/FakeCamera.cpp new file mode 100644 index 000000000..3592eabcb --- /dev/null +++ b/camera/libcameraservice/FakeCamera.cpp @@ -0,0 +1,404 @@ +#define LOG_TAG "FakeCamera" +#include + +#include +#include +#include "FakeCamera.h" + +namespace android { + +static int tables_initialized = 0; +uint8_t *gYTable, *gCbTable, *gCrTable; + +static int +clamp(int x) +{ + if (x > 255) return 255; + if (x < 0) return 0; + return x; +} + +/* the equation used by the video code to translate YUV to RGB looks like this + * + * Y = (Y0 - 16)*k0 + * Cb = Cb0 - 128 + * Cr = Cr0 - 128 + * + * G = ( Y - k1*Cr - k2*Cb ) + * R = ( Y + k3*Cr ) + * B = ( Y + k4*Cb ) + * + */ + +static const double k0 = 1.164; +static const double k1 = 0.813; +static const double k2 = 0.391; +static const double k3 = 1.596; +static const double k4 = 2.018; + +/* let's try to extract the value of Y + * + * G + k1/k3*R + k2/k4*B = Y*( 1 + k1/k3 + k2/k4 ) + * + * Y = ( G + k1/k3*R + k2/k4*B ) / (1 + k1/k3 + k2/k4) + * Y0 = ( G0 + k1/k3*R0 + k2/k4*B0 ) / ((1 + k1/k3 + k2/k4)*k0) + 16 + * + * let define: + * kYr = k1/k3 + * kYb = k2/k4 + * kYy = k0 * ( 1 + kYr + kYb ) + * + * we have: + * Y = ( G + kYr*R + kYb*B ) + * Y0 = clamp[ Y/kYy + 16 ] + */ + +static const double kYr = k1/k3; +static const double kYb = k2/k4; +static const double kYy = k0*( 1. + kYr + kYb ); + +static void +initYtab( void ) +{ + const int imax = (int)( (kYr + kYb)*(31 << 2) + (61 << 3) + 0.1 ); + int i; + + gYTable = (uint8_t *)malloc(imax); + + for(i=0; i 235) x = 235; + gYTable[i] = (uint8_t) x; + } +} + +/* + * the source is RGB565, so adjust for 8-bit range of input values: + * + * G = (pixels >> 3) & 0xFC; + * R = (pixels >> 8) & 0xF8; + * B = (pixels & 0x1f) << 3; + * + * R2 = (pixels >> 11) R = R2*8 + * B2 = (pixels & 0x1f) B = B2*8 + * + * kYr*R = kYr2*R2 => kYr2 = kYr*8 + * kYb*B = kYb2*B2 => kYb2 = kYb*8 + * + * we want to use integer multiplications: + * + * SHIFT1 = 9 + * + * (ALPHA*R2) >> SHIFT1 == R*kYr => ALPHA = kYr*8*(1 << SHIFT1) + * + * ALPHA = kYr*(1 << (SHIFT1+3)) + * BETA = kYb*(1 << (SHIFT1+3)) + */ + +static const int SHIFT1 = 9; +static const int ALPHA = (int)( kYr*(1 << (SHIFT1+3)) + 0.5 ); +static const int BETA = (int)( kYb*(1 << (SHIFT1+3)) + 0.5 ); + +/* + * now let's try to get the values of Cb and Cr + * + * R-B = (k3*Cr - k4*Cb) + * + * k3*Cr = k4*Cb + (R-B) + * k4*Cb = k3*Cr - (R-B) + * + * R-G = (k1+k3)*Cr + k2*Cb + * = (k1+k3)*Cr + k2/k4*(k3*Cr - (R-B)/k0) + * = (k1 + k3 + k2*k3/k4)*Cr - k2/k4*(R-B) + * + * kRr*Cr = (R-G) + kYb*(R-B) + * + * Cr = ((R-G) + kYb*(R-B))/kRr + * Cr0 = clamp(Cr + 128) + */ + +static const double kRr = (k1 + k3 + k2*k3/k4); + +static void +initCrtab( void ) +{ + uint8_t *pTable; + int i; + + gCrTable = (uint8_t *)malloc(768*2); + + pTable = gCrTable + 384; + for(i=-384; i<384; i++) + pTable[i] = (uint8_t) clamp( i/kRr + 128.5 ); +} + +/* + * B-G = (k2 + k4)*Cb + k1*Cr + * = (k2 + k4)*Cb + k1/k3*(k4*Cb + (R-B)) + * = (k2 + k4 + k1*k4/k3)*Cb + k1/k3*(R-B) + * + * kBb*Cb = (B-G) - kYr*(R-B) + * + * Cb = ((B-G) - kYr*(R-B))/kBb + * Cb0 = clamp(Cb + 128) + * + */ + +static const double kBb = (k2 + k4 + k1*k4/k3); + +static void +initCbtab( void ) +{ + uint8_t *pTable; + int i; + + gCbTable = (uint8_t *)malloc(768*2); + + pTable = gCbTable + 384; + for(i=-384; i<384; i++) + pTable[i] = (uint8_t) clamp( i/kBb + 128.5 ); +} + +/* + * SHIFT2 = 16 + * + * DELTA = kYb*(1 << SHIFT2) + * GAMMA = kYr*(1 << SHIFT2) + */ + +static const int SHIFT2 = 16; +static const int DELTA = kYb*(1 << SHIFT2); +static const int GAMMA = kYr*(1 << SHIFT2); + +int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16,uint8_t *yuv422,uint32_t *param,uint8_t *table[]) +{ + uint16_t *inputRGB = (uint16_t*)rgb16; + uint8_t *outYUV = yuv422; + int32_t width_dst = param[0]; + int32_t height_dst = param[1]; + int32_t pitch_dst = param[2]; + int32_t mheight_dst = param[3]; + int32_t pitch_src = param[4]; + uint8_t *y_tab = table[0]; + uint8_t *cb_tab = table[1]; + uint8_t *cr_tab = table[2]; + + int32_t size16 = pitch_dst*mheight_dst; + int32_t i,j,count; + int32_t ilimit,jlimit; + uint8_t *tempY,*tempU,*tempV; + uint16_t pixels; + int tmp; +uint32_t temp; + + tempY = outYUV; + tempU = outYUV + (height_dst * pitch_dst); + tempV = tempU + 1; + + jlimit = height_dst; + ilimit = width_dst; + + for(j=0; j>11) ); + y0 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)]; + + G_ds += (pixels>>1) & 0x03E0; + B_ds += (pixels<<5) & 0x03E0; + R_ds += (pixels>>6) & 0x03E0; + + pixels = inputRGB[i+1]; + temp = (ALPHA*(pixels & 0x001F) + BETA*(pixels>>11) ); + y1 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)]; + + G_ds += (pixels>>1) & 0x03E0; + B_ds += (pixels<<5) & 0x03E0; + R_ds += (pixels>>6) & 0x03E0; + + R_ds >>= 1; + B_ds >>= 1; + G_ds >>= 1; + + tmp = R_ds - B_ds; + + u = cb_tab[(((R_ds-G_ds)<>(SHIFT2+2)]; + v = cr_tab[(((B_ds-G_ds)<>(SHIFT2+2)]; + + tempY[0] = y0; + tempY[1] = y1; + tempU[0] = u; + tempV[0] = v; + + tempY += 2; + tempU += 2; + tempV += 2; + } + + inputRGB += pitch_src; + } + + return 1; +} + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) + +static void convert_rgb16_to_yuv422(uint8_t *rgb, uint8_t *yuv, int width, int height) +{ + if (!tables_initialized) { + initYtab(); + initCrtab(); + initCbtab(); + tables_initialized = 1; + } + + uint32_t param[6]; + param[0] = (uint32_t) width; + param[1] = (uint32_t) height; + param[2] = (uint32_t) width; + param[3] = (uint32_t) height; + param[4] = (uint32_t) width; + param[5] = (uint32_t) 0; + + uint8_t *table[3]; + table[0] = gYTable; + table[1] = gCbTable + 384; + table[2] = gCrTable + 384; + + ccrgb16toyuv_wo_colorkey(rgb, yuv, param, table); +} + +const int FakeCamera::kRed; +const int FakeCamera::kGreen; +const int FakeCamera::kBlue; + +FakeCamera::FakeCamera(int width, int height) + : mTmpRgb16Buffer(0) +{ + setSize(width, height); +} + +FakeCamera::~FakeCamera() +{ + delete[] mTmpRgb16Buffer; +} + +void FakeCamera::setSize(int width, int height) +{ + mWidth = width; + mHeight = height; + mCounter = 0; + mCheckX = 0; + mCheckY = 0; + + // This will cause it to be reallocated on the next call + // to getNextFrameAsYuv422(). + delete[] mTmpRgb16Buffer; + mTmpRgb16Buffer = 0; +} + +void FakeCamera::getNextFrameAsRgb565(uint16_t *buffer) +{ + int size = mWidth / 10; + + drawCheckerboard(buffer, size); + + int x = ((mCounter*3)&255); + if(x>128) x = 255 - x; + int y = ((mCounter*5)&255); + if(y>128) y = 255 - y; + + drawSquare(buffer, x*size/32, y*size/32, (size*5)>>1, (mCounter&0x100)?kRed:kGreen, kBlue); + + mCounter++; +} + +void FakeCamera::getNextFrameAsYuv422(uint8_t *buffer) +{ + if (mTmpRgb16Buffer == 0) + mTmpRgb16Buffer = new uint16_t[mWidth * mHeight]; + + getNextFrameAsRgb565(mTmpRgb16Buffer); + convert_rgb16_to_yuv422((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight); +} + +void FakeCamera::drawSquare(uint16_t *dst, int x, int y, int size, int color, int shadow) +{ + int square_xstop, square_ystop, shadow_xstop, shadow_ystop; + + square_xstop = min(mWidth, x+size); + square_ystop = min(mHeight, y+size); + shadow_xstop = min(mWidth, x+size+(size/4)); + shadow_ystop = min(mHeight, y+size+(size/4)); + + // Do the shadow. + uint16_t *sh = &dst[(y+(size/4))*mWidth]; + for (int j = y + (size/4); j < shadow_ystop; j++) { + for (int i = x + (size/4); i < shadow_xstop; i++) { + sh[i] &= shadow; + } + sh += mWidth; + } + + // Draw the square. + uint16_t *sq = &dst[y*mWidth]; + for (int j = y; j < square_ystop; j++) { + for (int i = x; i < square_xstop; i++) { + sq[i] = color; + } + sq += mWidth; + } +} + +void FakeCamera::drawCheckerboard(uint16_t *dst, int size) +{ + bool black = true; + + if((mCheckX/size)&1) + black = false; + if((mCheckY/size)&1) + black = !black; + + int county = mCheckY%size; + int checkxremainder = mCheckX%size; + + for(int y=0;y= size) { + countx=0; + current = !current; + } + } + if(county++ >= size) { + county=0; + black = !black; + } + } + mCheckX += 3; + mCheckY++; +} + + +status_t FakeCamera::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, 255, " width x height (%d x %d), counter (%d), check x-y coordinate(%d, %d)\n", mWidth, mHeight, mCounter, mCheckX, mCheckY); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + + +}; // namespace android diff --git a/camera/libcameraservice/FakeCamera.h b/camera/libcameraservice/FakeCamera.h new file mode 100644 index 000000000..77c994c1a --- /dev/null +++ b/camera/libcameraservice/FakeCamera.h @@ -0,0 +1,51 @@ +/* +** +** Copyright 2008, 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. +*/ + +#ifndef ANDROID_HARDWARE_FAKECAMERA_H +#define ANDROID_HARDWARE_FAKECAMERA_H + +#include + +namespace android { + +class FakeCamera { +public: + FakeCamera(int width, int height); + ~FakeCamera(); + + void setSize(int width, int height); + void getNextFrameAsRgb565(uint16_t *buffer); + void getNextFrameAsYuv422(uint8_t *buffer); + status_t dump(int fd, const Vector& args); + +private: + void drawSquare(uint16_t *buffer, int x, int y, int size, int color, int shadow); + void drawCheckerboard(uint16_t *buffer, int size); + + static const int kRed = 0xf800; + static const int kGreen = 0x07c0; + static const int kBlue = 0x003e; + + int mWidth, mHeight; + int mCounter; + int mCheckX, mCheckY; + uint16_t *mTmpRgb16Buffer; +}; + +}; // namespace android + +#endif // ANDROID_HARDWARE_FAKECAMERA_H diff --git a/cmds/runtime/Android.mk b/cmds/runtime/Android.mk new file mode 100644 index 000000000..521eb2b28 --- /dev/null +++ b/cmds/runtime/Android.mk @@ -0,0 +1,29 @@ +ifeq ($(TARGET_SIMULATOR),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + ServiceManager.cpp \ + SignalHandler.cpp \ + main_runtime.cpp + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libandroid_runtime \ + libcutils \ + libui \ + libsystem_server \ + libhardware_legacy + +LOCAL_C_INCLUDES := \ + $(JNI_H_INCLUDE) + +ifeq ($(TARGET_OS),linux) + LOCAL_CFLAGS += -DXP_UNIX +endif + +LOCAL_MODULE:= runtime + +include $(BUILD_EXECUTABLE) +endif diff --git a/cmds/runtime/MODULE_LICENSE_APACHE2 b/cmds/runtime/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/cmds/runtime/NOTICE b/cmds/runtime/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/cmds/runtime/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/runtime/ServiceManager.cpp b/cmds/runtime/ServiceManager.cpp new file mode 100644 index 000000000..758a95c07 --- /dev/null +++ b/cmds/runtime/ServiceManager.cpp @@ -0,0 +1,74 @@ +// +// Copyright 2005 The Android Open Source Project +// + +#define LOG_TAG "ServiceManager" + +#include "ServiceManager.h" +#include "SignalHandler.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace android { + +BServiceManager::BServiceManager() +{ +} + +sp BServiceManager::getService(const String16& name) const +{ + AutoMutex _l(mLock); + ssize_t i = mServices.indexOfKey(name); + LOGV("ServiceManager: getService(%s) -> %d\n", String8(name).string(), i); + if (i >= 0) return mServices.valueAt(i); + return NULL; +} + +sp BServiceManager::checkService(const String16& name) const +{ + AutoMutex _l(mLock); + ssize_t i = mServices.indexOfKey(name); + LOGV("ServiceManager: getService(%s) -> %d\n", String8(name).string(), i); + if (i >= 0) return mServices.valueAt(i); + return NULL; +} + +status_t BServiceManager::addService(const String16& name, const sp& service) +{ + AutoMutex _l(mLock); + LOGI("ServiceManager: addService(%s, %p)\n", String8(name).string(), service.get()); + const ssize_t res = mServices.add(name, service); + if (res >= NO_ERROR) { + mChanged.broadcast(); + return NO_ERROR; + } + return res; +} + +Vector BServiceManager::listServices() +{ + Vector res; + + AutoMutex _l(mLock); + const size_t N = mServices.size(); + for (size_t i=0; i +#include +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class BServiceManager : public BnServiceManager +{ +public: + BServiceManager(); + + virtual sp getService( const String16& name) const; + virtual sp checkService( const String16& name) const; + virtual status_t addService( const String16& name, + const sp& service); + virtual Vector listServices(); + + +private: + mutable Mutex mLock; + mutable Condition mChanged; + sp mPermissionController; + KeyedVector > mServices; +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_SERVICE_MANAGER_H diff --git a/cmds/runtime/SignalHandler.cpp b/cmds/runtime/SignalHandler.cpp new file mode 100644 index 000000000..cccaabfc5 --- /dev/null +++ b/cmds/runtime/SignalHandler.cpp @@ -0,0 +1,249 @@ +// +// Copyright 2005 The Android Open Source Project +// + +#define LOG_TAG "SignalHandler" + +#include "SignalHandler.h" + +#include +#include +#include + +#include +#include +#include + +namespace android { + +class SignalHandler::ProcessThread : public Thread +{ +public: + ProcessThread(SignalHandler& sh) + : Thread(false) + , mOwner(sh) + { + } + + virtual bool threadLoop() + { + char buffer[32]; + read(mOwner.mAvailMsg[0], buffer, sizeof(buffer)); + + LOGV("Signal command processing thread woke up!"); + + if (mOwner.mLostCommands) { + LOGE("Lost %d signals!", mOwner.mLostCommands); + mOwner.mLostCommands = 0; + } + + int cur; + while ((cur=mOwner.mCommandBottom) != mOwner.mCommandTop) { + if (mOwner.mCommands[cur].filled == 0) { + LOGV("Command at %d is not yet filled", cur); + break; + } + + LOGV("Processing command at %d, top is %d", + cur, mOwner.mCommandTop); + processCommand(mOwner.mCommands[cur]); + mOwner.mCommands[cur].filled = 0; + + int next = mOwner.mCommandBottom+1; + if (next >= COMMAND_QUEUE_SIZE) { + next = 0; + } + + mOwner.mCommandBottom = next; + } + + return true; + } + + void processCommand(const CommandEntry& entry) + { + switch (entry.signum) { + case SIGCHLD: { + mOwner.mLock.lock(); + ssize_t i = mOwner.mChildHandlers.indexOfKey(entry.info.si_pid); + ChildHandler ch; + if (i >= 0) { + ch = mOwner.mChildHandlers.valueAt(i); + mOwner.mChildHandlers.removeItemsAt(i); + } + mOwner.mLock.unlock(); + + LOGD("SIGCHLD: pid=%d, handle index=%d", entry.info.si_pid, i); + + if (i >= 0) { + int res = waitpid(entry.info.si_pid, NULL, WNOHANG); + LOGW_IF(res == 0, + "Received SIGCHLD, but pid %d is not yet stopped", + entry.info.si_pid); + if (ch.handler) { + ch.handler(entry.info.si_pid, ch.userData); + } + } else { + LOGW("Unhandled SIGCHLD for pid %d", entry.info.si_pid); + } + } break; + } + } + + SignalHandler& mOwner; +}; + + +Mutex SignalHandler::mInstanceLock; +SignalHandler* SignalHandler::mInstance = NULL; + +status_t SignalHandler::setChildHandler(pid_t childPid, + int tag, + child_callback_t handler, + void* userData) +{ + SignalHandler* const self = getInstance(); + + self->mLock.lock(); + + // First make sure this child hasn't already exited. + pid_t res = waitpid(childPid, NULL, WNOHANG); + if (res != 0) { + if (res < 0) { + LOGW("setChildHandler waitpid of %d failed: %d (%s)", + childPid, res, strerror(errno)); + } else { + LOGW("setChildHandler waitpid of %d said %d already dead", + childPid, res); + } + + // Some kind of error... just handle the exit now. + self->mLock.unlock(); + + if (handler) { + handler(childPid, userData); + } + + // Return an error code -- 0 means it already exited. + return (status_t)res; + } + + ChildHandler entry; + entry.childPid = childPid; + entry.tag = tag; + entry.handler = handler; + entry.userData = userData; + + // Note: this replaces an existing entry for this pid, if there already + // is one. This is the required behavior. + LOGD("setChildHandler adding pid %d, tag %d, handler %p, data %p", + childPid, tag, handler, userData); + self->mChildHandlers.add(childPid, entry); + + self->mLock.unlock(); + + return NO_ERROR; +} + +void SignalHandler::killAllChildren(int tag) +{ + SignalHandler* const self = getInstance(); + + AutoMutex _l (self->mLock); + const size_t N = self->mChildHandlers.size(); + for (size_t i=0; imChildHandlers.valueAt(i)); + if (tag == 0 || ch.tag == tag) { + const pid_t pid = ch.childPid; + LOGI("Killing child %d (tag %d)\n", pid, ch.tag); + kill(pid, SIGKILL); + } + } +} + +SignalHandler::SignalHandler() + : mCommandTop(0) + , mCommandBottom(0) + , mLostCommands(0) +{ + memset(mCommands, 0, sizeof(mCommands)); + + int res = pipe(mAvailMsg); + LOGE_IF(res != 0, "Unable to create signal handler pipe: %s", strerror(errno)); + + mProcessThread = new ProcessThread(*this); + mProcessThread->run("SignalHandler", PRIORITY_HIGHEST); + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = sigAction; + sa.sa_flags = SA_NOCLDSTOP|SA_SIGINFO; + sigaction(SIGCHLD, &sa, NULL); +} + +SignalHandler::~SignalHandler() +{ +} + +SignalHandler* SignalHandler::getInstance() +{ + AutoMutex _l(mInstanceLock); + if (mInstance == NULL) { + mInstance = new SignalHandler(); + } + return mInstance; +} + +void SignalHandler::sigAction(int signum, siginfo_t* info, void*) +{ + static const char wakeupMsg[1] = { 0xff }; + + // If our signal handler is being called, then we know we have + // already initialized the SignalHandler class and thus mInstance + // is valid. + SignalHandler* const self = mInstance; + + // XXX This is not safe! + #if 0 + LOGV("Signal %d: signo=%d, errno=%d, code=%d, pid=%d\n", + signum, + info->si_signo, info->si_errno, info->si_code, + info->si_pid); + #endif + + int32_t oldTop, newTop; + + // Find the next command slot... + do { + oldTop = self->mCommandTop; + + newTop = oldTop + 1; + if (newTop >= COMMAND_QUEUE_SIZE) { + newTop = 0; + } + + if (newTop == self->mCommandBottom) { + // The buffer is filled up! Ouch! + // XXX This is not safe! + #if 0 + LOGE("Command buffer overflow! newTop=%d\n", newTop); + #endif + android_atomic_add(1, &self->mLostCommands); + write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg)); + return; + } + } while(android_atomic_cmpxchg(oldTop, newTop, &(self->mCommandTop))); + + // Fill in the command data... + self->mCommands[oldTop].signum = signum; + self->mCommands[oldTop].info = *info; + + // And now make this command available. + self->mCommands[oldTop].filled = 1; + + // Wake up the processing thread. + write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg)); +} + +}; // namespace android + diff --git a/cmds/runtime/SignalHandler.h b/cmds/runtime/SignalHandler.h new file mode 100644 index 000000000..7f4ef8e4e --- /dev/null +++ b/cmds/runtime/SignalHandler.h @@ -0,0 +1,137 @@ +// +// Copyright 2005 The Android Open Source Project +// +#ifndef ANDROID_SIGNAL_HANDLER_H +#define ANDROID_SIGNAL_HANDLER_H + +#include +#include + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +enum { + DEFAULT_PROCESS_TAG = 1 +}; + +class SignalHandler +{ +public: + typedef void (*child_callback_t)(pid_t child, void* userData); + + /** + * Set a handler for when a child process exits. By calling + * this, a waitpid() will be done when the child exits to remove + * it from the zombie state. You can also optionally specify a + * handler to be called when the child exits. + * + * If there is already a handler for this child process, it is + * replaced by this new handler. In this case the old handler's + * function is not called. + * + * @param childPid Process ID of child to watch. + * @param childTag User-defined tag for this child. Must be + * greater than zero. + * @param handler If non-NULL, this will be called when the + * child exits. It may be called in either a + * separate signal handling thread, or + * immediately if the child has already exited. + * @param userData Propageted as-is to handler. + * + * @return status_t NO_ERROR if all is well. + */ + static status_t setChildHandler(pid_t childPid, + int childTag = DEFAULT_PROCESS_TAG, + child_callback_t handler = NULL, + void* userData = NULL); + + /** + * Kill all of the child processes for which we have a waiting + * handler, whose tag is the given value. If tag is 0, all + * children are killed. + * + * @param tag + */ + static void killAllChildren(int tag = 0); + +private: + SignalHandler(); + ~SignalHandler(); + + static SignalHandler* getInstance(); + + static void sigAction(int, siginfo_t*, void*); + + // -------------------------------------------------- + // Shared state... all of this is protected by mLock. + // -------------------------------------------------- + + mutable Mutex mLock; + + struct ChildHandler + { + pid_t childPid; + int tag; + child_callback_t handler; + void* userData; + }; + KeyedVector mChildHandlers; + + // -------------------------------------------------- + // Commmand queue... data is inserted by the signal + // handler using atomic ops, and retrieved by the + // signal processing thread. Because these are touched + // by the signal handler, no lock is used. + // -------------------------------------------------- + + enum { + COMMAND_QUEUE_SIZE = 64 + }; + struct CommandEntry + { + int filled; + int signum; + siginfo_t info; + }; + + // The top of the queue. This is incremented atomically by the + // signal handler before placing a command in the queue. + volatile int32_t mCommandTop; + + // The bottom of the queue. Only modified by the processing + // thread; the signal handler reads it only to determine if the + // queue is full. + int32_t mCommandBottom; + + // Incremented each time we receive a signal and don't have room + // for it on the command queue. + volatile int32_t mLostCommands; + + // The command processing thread. + class ProcessThread; + sp mProcessThread; + + // Pipe used to tell command processing thread when new commands. + // are available. The thread blocks on the read end, the signal + // handler writes when it enqueues new commands. + int mAvailMsg[2]; + + // The commands. + CommandEntry mCommands[COMMAND_QUEUE_SIZE]; + + // -------------------------------------------------- + // Singleton. + // -------------------------------------------------- + + static Mutex mInstanceLock; + static SignalHandler* mInstance; +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_SIGNAL_HANDLER_H diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp new file mode 100644 index 000000000..1531a9efd --- /dev/null +++ b/cmds/runtime/main_runtime.cpp @@ -0,0 +1,514 @@ +// +// Copyright 2005 The Android Open Source Project +// +// Main entry point for runtime. +// + +#include "ServiceManager.h" +#include "SignalHandler.h" + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_ANDROID_OS +# include +#endif + +#undef LOG_TAG +#define LOG_TAG "runtime" + +static const char* ZYGOTE_ARGV[] = { + "--setuid=1000", + "--setgid=1000", + "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003", + /* CAP_SYS_TTY_CONFIG & CAP_SYS_RESOURCE & CAP_NET_BROADCAST & + * CAP_NET_ADMIN & CAP_NET_RAW & CAP_NET_BIND_SERVICE & CAP_KILL & + * CAP_SYS_BOOT + */ + "--capabilities=88161312,88161312", + "--runtime-init", + "--nice-name=system_server", + "com.android.server.SystemServer" +}; + +using namespace android; + +extern "C" status_t system_init(); + +enum { + SYSTEM_PROCESS_TAG = DEFAULT_PROCESS_TAG+1 +}; + +extern Mutex gEventQMutex; +extern Condition gEventQCondition; + +namespace android { + +extern status_t app_init(const char* className); +extern void set_finish_init_func(void (*func)()); + + +/** + * This class is used to kill this process (runtime) when the system_server dies. + */ +class GrimReaper : public IBinder::DeathRecipient { +public: + GrimReaper() { } + + virtual void binderDied(const wp& who) + { + LOGI("Grim Reaper killing runtime..."); + kill(getpid(), SIGKILL); + } +}; + +extern void QuickTests(); + +/* + * Print usage info. + */ +static void usage(const char* argv0) +{ + fprintf(stderr, + "Usage: runtime [-g gamma] [-l logfile] [-n] [-s]\n" + " [-j app-component] [-v app-verb] [-d app-data]\n" + "\n" + "-l: File to send log messages to\n" + "-n: Don't print to stdout/stderr\n" + "-s: Force single-process mode\n" + "-j: Custom home app component name\n" + "-v: Custom home app intent verb\n" + "-d: Custom home app intent data\n" + ); + exit(1); +} + +// Selected application to run. +static const char* gInitialApplication = NULL; +static const char* gInitialVerb = NULL; +static const char* gInitialData = NULL; + +static void writeStringToParcel(Parcel& parcel, const char* str) +{ + if (str) { + parcel.writeString16(String16(str)); + } else { + parcel.writeString16(NULL, 0); + } +} + +/* + * Starting point for program logic. + * + * Returns with an exit status code (0 on success, nonzero on error). + */ +static int run(sp& proc) +{ + // Temporary hack to call startRunning() on the activity manager. + sp sm = defaultServiceManager(); + sp am; + while ((am = sm->getService(String16("activity"))) == NULL) { + LOGI("Waiting for activity manager..."); + } + Parcel data, reply; + // XXX Need to also supply a package name for this to work again. + // IActivityManager::getInterfaceDescriptor() is the token for invoking on this interface; + // hardcoding it here avoids having to link with the full Activity Manager library + data.writeInterfaceToken(String16("android.app.IActivityManager")); + writeStringToParcel(data, NULL); + writeStringToParcel(data, gInitialApplication); + writeStringToParcel(data, gInitialVerb); + writeStringToParcel(data, gInitialData); +LOGI("run() sending FIRST_CALL_TRANSACTION to activity manager"); + am->transact(IBinder::FIRST_CALL_TRANSACTION, data, &reply); + + if (proc->supportsProcesses()) { + // Now we link to the Activity Manager waiting for it to die. If it does kill ourself. + // initd will restart this process and bring the system back up. + sp grim = new GrimReaper(); + am->linkToDeath(grim, grim.get(), 0); + + // Now join the thread pool. Note this is needed so that the message enqueued in the driver + // for the linkToDeath gets processed. + IPCThreadState::self()->joinThreadPool(); + } else { + // Keep this thread running forever... + while (1) { + usleep(100000); + } + } + return 1; +} + + +}; // namespace android + + +/* + * Post-system-process initialization. + * + * This function continues initialization after the system process + * has been initialized. It needs to be separate because the system + * initialization needs to care of starting the Android runtime if it is not + * running in its own process, which doesn't return until the runtime is + * being shut down. So it will call back to here from inside of Dalvik, + * to allow us to continue booting up. + */ +static void finish_system_init(sp& proc) +{ + // If we are running multiprocess, we now need to have the + // thread pool started here. We don't do this in boot_init() + // because when running single process we need to start the + // thread pool after the Android runtime has been started (so + // the pool uses Dalvik threads). + if (proc->supportsProcesses()) { + proc->startThreadPool(); + } +} + + +// This function can be used to enforce security to different +// root contexts. For now, we just give every access. +static bool contextChecker( + const String16& name, const sp& caller, void* userData) +{ + return true; +} + +/* + * Initialization of boot services. + * + * This is where we perform initialization of all of our low-level + * boot services. Most importantly, here we become the context + * manager and use that to publish the service manager that will provide + * access to all other services. + */ +static void boot_init() +{ + LOGI("Entered boot_init()!\n"); + + sp proc(ProcessState::self()); + LOGD("ProcessState: %p\n", proc.get()); + proc->becomeContextManager(contextChecker, NULL); + + if (proc->supportsProcesses()) { + LOGI("Binder driver opened. Multiprocess enabled.\n"); + } else { + LOGI("Binder driver not found. Processes not supported.\n"); + } + + sp sm = new BServiceManager; + proc->setContextObject(sm); +} + +/* + * Redirect stdin/stdout/stderr to /dev/null. + */ +static void redirectStdFds(void) +{ + int fd = open("/dev/null", O_RDWR, 0); + if (fd < 0) { + LOGW("Unable to open /dev/null: %s\n", strerror(errno)); + } else { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + } +} + +static int hasDir(const char* dir) +{ + struct stat s; + int res = stat(dir, &s); + if (res == 0) { + return S_ISDIR(s.st_mode); + } + return 0; +} + +static void validateTime() +{ +#if HAVE_ANDROID_OS + int fd; + int res; + time_t min_time = 1167652800; // jan 1 2007, type 'date -ud "1/1 12:00" +%s' to get value for current year + struct timespec ts; + + fd = open("/dev/alarm", O_RDWR); + if(fd < 0) { + LOGW("Unable to open alarm driver: %s\n", strerror(errno)); + return; + } + res = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_RTC_WAKEUP), &ts); + if(res < 0) { + LOGW("Unable to read rtc, %s\n", strerror(errno)); + } + else if(ts.tv_sec >= min_time) { + goto done; + } + LOGW("Invalid time detected, %ld set to %ld\n", ts.tv_sec, min_time); + ts.tv_sec = min_time; + ts.tv_nsec = 0; + res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); + if(res < 0) { + LOGW("Unable to set rtc to %ld: %s\n", ts.tv_sec, strerror(errno)); + } +done: + close(fd); +#endif +} + +#ifndef HAVE_ANDROID_OS +class QuickRuntime : public AndroidRuntime +{ +public: + QuickRuntime() {} + + virtual void onStarted() + { + printf("QuickRuntime: onStarted\n"); + } +}; +#endif + +static status_t start_process(const char* name); + +static void restart_me(pid_t child, void* userData) +{ + start_process((const char*)userData); +} + +static status_t start_process(const char* name) +{ + String8 path(name); + Vector args; + String8 leaf(path.getPathLeaf()); + String8 parentDir(path.getPathDir()); + args.insertAt(leaf.string(), 0); + args.add(parentDir.string()); + args.add(NULL); + pid_t child = fork(); + if (child < 0) { + status_t err = errno; + LOGE("*** fork of child %s failed: %s", leaf.string(), strerror(err)); + return -errno; + } else if (child == 0) { + LOGI("Executing: %s", path.string()); + execv(path.string(), const_cast(args.array())); + int err = errno; + LOGE("Exec failed: %s\n", strerror(err)); + _exit(err); + } else { + SignalHandler::setChildHandler(child, DEFAULT_PROCESS_TAG, + restart_me, (void*)name); + } + return -errno; +} + +/* + * Application entry point. + * + * Parse arguments, set some values, and pass control off to Run(). + * + * This is redefined to "SDL_main" on SDL simulator builds, and + * "runtime_main" on wxWidgets builds. + */ +extern "C" +int main(int argc, char* const argv[]) +{ + bool singleProcess = false; + const char* logFile = NULL; + int ic; + int result = 1; + pid_t systemPid; + + sp proc; + +#ifndef HAVE_ANDROID_OS + /* Set stdout/stderr to unbuffered for MinGW/MSYS. */ + //setvbuf(stdout, NULL, _IONBF, 0); + //setvbuf(stderr, NULL, _IONBF, 0); + + LOGI("commandline args:\n"); + for (int i = 0; i < argc; i++) + LOGI(" %2d: '%s'\n", i, argv[i]); +#endif + + while (1) { + ic = getopt(argc, argv, "g:j:v:d:l:ns"); + if (ic < 0) + break; + + switch (ic) { + case 'g': + break; + case 'j': + gInitialApplication = optarg; + break; + case 'v': + gInitialVerb = optarg; + break; + case 'd': + gInitialData = optarg; + break; + case 'l': + logFile = optarg; + break; + case 'n': + redirectStdFds(); + break; + case 's': + singleProcess = true; + break; + case '?': + default: + LOGE("runtime: unrecognized flag -%c\n", ic); + usage(argv[0]); + break; + } + } + if (optind < argc) { + LOGE("runtime: extra stuff: %s\n", argv[optind]); + usage(argv[0]); + } + + if (singleProcess) { + ProcessState::setSingleProcess(true); + } + + if (logFile != NULL) { + android_logToFile(NULL, logFile); + } + + /* + * Set up ANDROID_* environment variables. + * + * TODO: the use of $ANDROID_PRODUCT_OUT will go away soon. + */ + static const char* kSystemDir = "/system"; + static const char* kDataDir = "/data"; + static const char* kAppSubdir = "/app"; + const char* out = NULL; +#ifndef HAVE_ANDROID_OS + //out = getenv("ANDROID_PRODUCT_OUT"); +#endif + if (out == NULL) + out = ""; + + char* systemDir = (char*) malloc(strlen(out) + strlen(kSystemDir) +1); + char* dataDir = (char*) malloc(strlen(out) + strlen(kDataDir) +1); + + sprintf(systemDir, "%s%s", out, kSystemDir); + sprintf(dataDir, "%s%s", out, kDataDir); + setenv("ANDROID_ROOT", systemDir, 1); + setenv("ANDROID_DATA", dataDir, 1); + + char* assetDir = (char*) malloc(strlen(systemDir) + strlen(kAppSubdir) +1); + sprintf(assetDir, "%s%s", systemDir, kAppSubdir); + + LOGI("Startup: sys='%s' asset='%s' data='%s'\n", + systemDir, assetDir, dataDir); + free(systemDir); + free(dataDir); + +#ifdef HAVE_ANDROID_OS + /* set up a process group for easier killing on the device */ + setpgid(0, getpid()); +#endif + + // Change to asset dir. This is only necessary if we've changed to + // a different directory, but there's little harm in doing it regardless. + // + // Expecting assets to live in the current dir is not a great idea, + // because some of our code or one of our libraries could change the + // directory out from under us. Preserve the behavior for now. + if (chdir(assetDir) != 0) { + LOGW("WARNING: could not change dir to '%s': %s\n", + assetDir, strerror(errno)); + } + free(assetDir); + +#if 0 + // Hack to keep libc from beating the filesystem to death. It's + // hitting /etc/localtime frequently, + // + // This statement locks us into Pacific time. We could do better, + // but there's not much point until we're sure that the library + // can't be changed to do more along the lines of what we want. +#ifndef XP_WIN + setenv("TZ", "PST+8PDT,M4.1.0/2,M10.5.0/2", true); +#endif +#endif + + /* track our progress through the boot sequence */ + const int LOG_BOOT_PROGRESS_START = 3000; + LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, + ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); + + validateTime(); + + proc = ProcessState::self(); + + boot_init(); + + /* If we are in multiprocess mode, have zygote spawn the system + * server process and call system_init(). If we are running in + * single process mode just call system_init() directly. + */ + if (proc->supportsProcesses()) { + // If stdio logging is on, system_server should not inherit our stdio + // The dalvikvm instance will copy stdio to the log on its own + char propBuf[PROPERTY_VALUE_MAX]; + bool logStdio = false; + property_get("log.redirect-stdio", propBuf, ""); + logStdio = (strcmp(propBuf, "true") == 0); + + zygote_run_oneshot((int)(!logStdio), + sizeof(ZYGOTE_ARGV) / sizeof(ZYGOTE_ARGV[0]), + ZYGOTE_ARGV); + + //start_process("/system/bin/mediaserver"); + + } else { +#ifndef HAVE_ANDROID_OS + QuickRuntime* runt = new QuickRuntime(); + runt->start("com/android/server/SystemServer", + false /* spontaneously fork system server from zygote */); +#endif + } + + //printf("+++ post-zygote\n"); + + finish_system_init(proc); + run(proc); + +bail: + if (proc != NULL) { + proc->setContextObject(NULL); + } + + return 0; +} diff --git a/cmds/surfaceflinger/Android.mk b/cmds/surfaceflinger/Android.mk new file mode 100644 index 000000000..37c3d9424 --- /dev/null +++ b/cmds/surfaceflinger/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + main_surfaceflinger.cpp + +LOCAL_SHARED_LIBRARIES := \ + libsurfaceflinger \ + libutils + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../../libs/surfaceflinger + +LOCAL_MODULE:= surfaceflinger + +include $(BUILD_EXECUTABLE) diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp new file mode 100644 index 000000000..7c895783d --- /dev/null +++ b/cmds/surfaceflinger/main_surfaceflinger.cpp @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +#include + +using namespace android; + +int main(int argc, char** argv) +{ + sp proc(ProcessState::self()); + sp sm = defaultServiceManager(); + LOGI("ServiceManager: %p", sm.get()); + SurfaceFlinger::instantiate(); + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); +} diff --git a/im/java/android/im/BrandingResourceIDs.java b/im/java/android/im/BrandingResourceIDs.java new file mode 100644 index 000000000..996072222 --- /dev/null +++ b/im/java/android/im/BrandingResourceIDs.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 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 android.im; + +/** + * @hide + * Defines the IDs of branding resources. + */ +public interface BrandingResourceIDs { + /** + * The logo icon of the provider which is displayed in the landing page. + */ + public static final int DRAWABLE_LOGO = 100; + /** + * The icon of online presence status. + */ + public static final int DRAWABLE_PRESENCE_ONLINE = 102; + /** + * The icon of busy presence status. + */ + public static final int DRAWABLE_PRESENCE_BUSY = 103; + /** + * The icon of away presence status. + */ + public static final int DRAWABLE_PRESENCE_AWAY = 104; + /** + * The icon of invisible presence status. + */ + public static final int DRAWABLE_PRESENCE_INVISIBLE = 105; + /** + * The icon of offline presence status. + */ + public static final int DRAWABLE_PRESENCE_OFFLINE = 106; + /** + * The label of the menu to go to the contact list screen. + */ + public static final int STRING_MENU_CONTACT_LIST = 107; + +} diff --git a/im/java/android/im/IImPlugin.aidl b/im/java/android/im/IImPlugin.aidl new file mode 100644 index 000000000..229cd0eaa --- /dev/null +++ b/im/java/android/im/IImPlugin.aidl @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2008 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 android.im; + +/** + * @hide + */ +interface IImPlugin { + /** + * Notify the plugin the front door activity is created. This gives the plugin a chance to + * start its own servics, etc. + */ + void onStart(); + + /** + * Notify the plugin the front door activity is stopping. + */ + void onStop(); + + /** + * Sign in to the service for the account passed in. + * + * @param account the account id for the accont to be signed into. + */ + void signIn(long account); + + /** + * Sign out of the service for the account passed in. + * + * @param account the account id for the accont to be signed out of. + */ + void signOut(long account); + + /** + * Returns the package name used to load the resources for the given provider name. + * + * @return The package name to load the resourcs for the given provider. + */ + String getResourcePackageNameForProvider(String providerName); + + /** + * Returns a map of branding resources for the given provider. The keys are defined + * in {@link android.im.BrandingResourceIDs}. The values are the resource identifiers generated + * by the aapt tool. + * + * @return The map of branding resources for the given provider. + */ + Map getResourceMapForProvider(String providerName); + + /* + * Returns a list of supported IM providers. + * + * @return a List of supported providers. + */ + List getSupportedProviders(); +} diff --git a/im/java/android/im/ImPluginConsts.java b/im/java/android/im/ImPluginConsts.java new file mode 100644 index 000000000..416493ff7 --- /dev/null +++ b/im/java/android/im/ImPluginConsts.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2008 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 android.im; + +/** + * @hide + */ +public class ImPluginConsts { + /** + * The intent action name for the plugin service. + */ + public static final String PLUGIN_ACTION_NAME = "android.im.plugin"; +} \ No newline at end of file diff --git a/include/pim/EventRecurrence.h b/include/pim/EventRecurrence.h new file mode 100644 index 000000000..1ceda41e2 --- /dev/null +++ b/include/pim/EventRecurrence.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2006 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. + */ + +// +#ifndef _PIM_EVENT_RECURRENCE_H +#define _PIM_EVENT_RECURRENCE_H + +#include + +namespace android { + +struct EventRecurrence +{ +public: + EventRecurrence(); + ~EventRecurrence(); + + status_t parse(const String16&); + + + enum freq_t { + SECONDLY = 1, + MINUTELY = 2, + HOURLY = 3, + DAILY = 4, + WEEKLY = 5, + MONTHLY = 6, + YEARLY = 7 + }; + + enum { + SU = 0x00010000, + MO = 0x00020000, + TU = 0x00040000, + WE = 0x00080000, + TH = 0x00100000, + FR = 0x00200000, + SA = 0x00400000 + }; + + freq_t freq; + String16 until; + int count; + int interval; + int* bysecond; + int bysecondCount; + int* byminute; + int byminuteCount; + int* byhour; + int byhourCount; + int* byday; + int* bydayNum; + int bydayCount; + int* bymonthday; + int bymonthdayCount; + int* byyearday; + int byyeardayCount; + int* byweekno; + int byweeknoCount; + int* bymonth; + int bymonthCount; + int* bysetpos; + int bysetposCount; + int wkst; +}; + +}; // namespace android + +#endif // _PIM_EVENT_RECURRENCE_H diff --git a/include/private/opengles/gl_context.h b/include/private/opengles/gl_context.h new file mode 100644 index 000000000..0c7ad462f --- /dev/null +++ b/include/private/opengles/gl_context.h @@ -0,0 +1,632 @@ +/* + * Copyright (C) 2006 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. + */ + +#ifndef ANDROID_OPENGLES_CONTEXT_H +#define ANDROID_OPENGLES_CONTEXT_H + +#include +#include +#include +#include +#ifdef HAVE_ANDROID_OS +#include +#endif + +#include + +#include +#include + +namespace android { + +const unsigned int OGLES_NUM_COMPRESSED_TEXTURE_FORMATS = 10; + +class EGLTextureObject; +class EGLSurfaceManager; +class EGLBufferObjectManager; + +namespace gl { + +struct ogles_context_t; +struct matrixx_t; +struct transform_t; +struct buffer_t; + +ogles_context_t* getGlContext(); + +template +static inline void swap(T& a, T& b) { + T t(a); a = b; b = t; +} +template +inline T max(T a, T b) { + return a +inline T max(T a, T b, T c) { + return max(a, max(b, c)); +} +template +inline T min(T a, T b) { + return a +inline T min(T a, T b, T c) { + return min(a, min(b, c)); +} +template +inline T min(T a, T b, T c, T d) { + return min(min(a,b), min(c,d)); +} + +// ---------------------------------------------------------------------------- +// vertices +// ---------------------------------------------------------------------------- + +struct vec3_t { + union { + struct { GLfixed x, y, z; }; + struct { GLfixed r, g, b; }; + struct { GLfixed S, T, R; }; + GLfixed v[3]; + }; +}; + +struct vec4_t { + union { + struct { GLfixed x, y, z, w; }; + struct { GLfixed r, g, b, a; }; + struct { GLfixed S, T, R, Q; }; + GLfixed v[4]; + }; +}; + +struct vertex_t { + enum { + // these constant matter for our clipping + CLIP_L = 0x0001, // clipping flags + CLIP_R = 0x0002, + CLIP_B = 0x0004, + CLIP_T = 0x0008, + CLIP_N = 0x0010, + CLIP_F = 0x0020, + + EYE = 0x0040, + RESERVED = 0x0080, + + USER_CLIP_0 = 0x0100, // user clipping flags + USER_CLIP_1 = 0x0200, + USER_CLIP_2 = 0x0400, + USER_CLIP_3 = 0x0800, + USER_CLIP_4 = 0x1000, + USER_CLIP_5 = 0x2000, + + LIT = 0x4000, // lighting has been applied + TT = 0x8000, // texture coords transformed + + FRUSTUM_CLIP_ALL= 0x003F, + USER_CLIP_ALL = 0x3F00, + CLIP_ALL = 0x3F3F, + }; + + // the fields below are arranged to minimize d-cache usage + // we group together, by cache-line, the fields most likely to be used + + union { + vec4_t obj; + vec4_t eye; + }; + vec4_t clip; + + uint32_t flags; + size_t index; // cache tag, and vertex index + GLfixed fog; + uint8_t locked; + uint8_t mru; + uint8_t reserved[2]; + vec4_t window; + + vec4_t color; + vec4_t texture[GGL_TEXTURE_UNIT_COUNT]; + uint32_t reserved1[4]; + + inline void clear() { + flags = index = locked = mru = 0; + } +}; + +struct point_size_t { + GGLcoord size; + GLboolean smooth; +}; + +struct line_width_t { + GGLcoord width; + GLboolean smooth; +}; + +struct polygon_offset_t { + GLfixed factor; + GLfixed units; + GLboolean enable; +}; + +// ---------------------------------------------------------------------------- +// arrays +// ---------------------------------------------------------------------------- + +struct array_t { + typedef void (*fetcher_t)(ogles_context_t*, GLfixed*, const GLvoid*); + fetcher_t fetch; + GLvoid const* physical_pointer; + GLint size; + GLsizei stride; + GLvoid const* pointer; + buffer_t const* bo; + uint16_t type; + GLboolean enable; + GLboolean pad; + GLsizei bounds; + void init(GLint, GLenum, GLsizei, const GLvoid *, const buffer_t*, GLsizei); + inline void resolve(); + inline const GLubyte* element(GLint i) const { + return (const GLubyte*)physical_pointer + i * stride; + } +}; + +struct array_machine_t { + array_t vertex; + array_t normal; + array_t color; + array_t texture[GGL_TEXTURE_UNIT_COUNT]; + uint8_t activeTexture; + uint8_t tmu; + uint16_t cull; + uint32_t flags; + GLenum indicesType; + buffer_t const* array_buffer; + buffer_t const* element_array_buffer; + + void (*compileElements)(ogles_context_t*, vertex_t*, GLint, GLsizei); + void (*compileElement)(ogles_context_t*, vertex_t*, GLint); + + void (*mvp_transform)(transform_t const*, vec4_t*, vec4_t const*); + void (*mv_transform)(transform_t const*, vec4_t*, vec4_t const*); + void (*tex_transform[2])(transform_t const*, vec4_t*, vec4_t const*); + void (*perspective)(ogles_context_t*c, vertex_t* v); + void (*clipVertex)(ogles_context_t* c, vertex_t* nv, + GGLfixed t, const vertex_t* s, const vertex_t* p); + void (*clipEye)(ogles_context_t* c, vertex_t* nv, + GGLfixed t, const vertex_t* s, const vertex_t* p); +}; + +struct vertex_cache_t { + enum { + // must be at least 4 + // 3 vertice for triangles + // or 2 + 2 for indexed triangles w/ cache contention + VERTEX_BUFFER_SIZE = 8, + // must be a power of two and at least 3 + VERTEX_CACHE_SIZE = 64, // 8 KB + + INDEX_BITS = 16, + INDEX_MASK = ((1LU<(pthread_getspecific(gGLKey)); + } +#endif + + +struct prims_t { + typedef ogles_context_t* GL; + void (*renderPoint)(GL, vertex_t*); + void (*renderLine)(GL, vertex_t*, vertex_t*); + void (*renderTriangle)(GL, vertex_t*, vertex_t*, vertex_t*); +}; + +struct ogles_context_t { + context_t rasterizer; + array_machine_t arrays __attribute__((aligned(32))); + texture_state_t textures; + transform_state_t transforms; + vertex_cache_t vc; + prims_t prims; + culling_t cull; + lighting_t lighting; + user_clip_planes_t clipPlanes; + compute_iterators_t lerp; __attribute__((aligned(32))); + vertex_t current; + vec4_t currentColorClamped; + vec3_t currentNormal; + viewport_t viewport; + point_size_t point; + line_width_t line; + polygon_offset_t polygonOffset; + fog_t fog; + uint32_t perspective : 1; + uint32_t transformTextures : 1; + EGLSurfaceManager* surfaceManager; + EGLBufferObjectManager* bufferObjectManager; + GLenum error; + + static inline ogles_context_t* get() { + return getGlThreadSpecific(); + } + +}; + +}; // namespace gl +}; // namespace android + +#endif // ANDROID_OPENGLES_CONTEXT_H + diff --git a/include/private/ui/LayerState.h b/include/private/ui/LayerState.h new file mode 100644 index 000000000..b6fcd80c6 --- /dev/null +++ b/include/private/ui/LayerState.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_COMPOSER_LAYER_STATE_H +#define ANDROID_COMPOSER_LAYER_STATE_H + +#include +#include + +#include + +#include +#include + +#include + +namespace android { + +class Parcel; + +struct layer_state_t { + + layer_state_t() + : surface(0), what(0), + x(0), y(0), z(0), w(0), h(0), + alpha(0), tint(0), flags(0), mask(0), + reserved(0) + { + matrix.dsdx = matrix.dtdy = 1.0f; + matrix.dsdy = matrix.dtdx = 0.0f; + } + + status_t write(Parcel& output) const; + status_t read(const Parcel& input); + + struct matrix22_t { + float dsdx; + float dtdx; + float dsdy; + float dtdy; + }; + SurfaceID surface; + uint32_t what; + int32_t x; + int32_t y; + uint32_t z; + uint32_t w; + uint32_t h; + float alpha; + uint32_t tint; + uint8_t flags; + uint8_t mask; + uint8_t reserved; + matrix22_t matrix; + // non POD must be last. see write/read + Region transparentRegion; +}; + +}; // namespace android + +#endif // ANDROID_COMPOSER_LAYER_STATE_H + diff --git a/include/private/ui/SharedState.h b/include/private/ui/SharedState.h new file mode 100644 index 000000000..546d0adfd --- /dev/null +++ b/include/private/ui/SharedState.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_UI_SHARED_STATE_H +#define ANDROID_UI_SHARED_STATE_H + +#include +#include + +#include + +namespace android { + +/* + * These structures are shared between the composer process and its clients + */ + +// --------------------------------------------------------------------------- + +struct surface_info_t { // 4 longs, 16 bytes + enum { + eBufferDirty = 0x01 + }; + uint16_t w; + uint16_t h; + uint16_t stride; + uint16_t bpr; + uint16_t reserved; + uint8_t format; + uint8_t flags; + ssize_t bits_offset; +}; + +// --------------------------------------------------------------------------- + +const uint32_t NUM_LAYERS_MAX = 31; + +enum { // layer_cblk_t swapState + eIndex = 0x00000001, + eFlipRequested = 0x00000002, + + eResizeBuffer0 = 0x00000004, + eResizeBuffer1 = 0x00000008, + eResizeRequested = eResizeBuffer0 | eResizeBuffer1, + + eBusy = 0x00000010, + eLocked = 0x00000020, + eNextFlipPending = 0x00000040, + eInvalidSurface = 0x00000080 +}; + +enum { // layer_cblk_t flags + eLayerNotPosted = 0x00000001, + eNoCopyBack = 0x00000002, + eReserved = 0x0000007C, + eBufferIndexShift = 7, + eBufferIndex = 1<>1)); + } + static inline int frontBuffer(uint32_t state) { + return 1 - backBuffer(state); + } +}; + +// --------------------------------------------------------------------------- + +struct per_client_cblk_t // 4KB max +{ + Mutex lock; + Condition cv; + layer_cblk_t layers[NUM_LAYERS_MAX] __attribute__((aligned(32))); + + enum { + BLOCKING = 0x00000001, + INSPECT = 0x00000002 + }; + + per_client_cblk_t(); + + // these functions are used by the clients + status_t validate(size_t i) const; + int32_t lock_layer(size_t i, uint32_t flags); + uint32_t unlock_layer_and_post(size_t i); + void unlock_layer(size_t i); +}; +// --------------------------------------------------------------------------- + +const uint32_t NUM_DISPLAY_MAX = 4; + +struct display_cblk_t +{ + uint16_t w; + uint16_t h; + uint8_t format; + uint8_t orientation; + uint8_t reserved[2]; + float fps; + float density; + float xdpi; + float ydpi; + uint32_t pad[2]; +}; + +struct surface_flinger_cblk_t // 4KB max +{ + surface_flinger_cblk_t(); + + uint8_t connected; + uint8_t reserved[3]; + uint32_t pad[7]; + + display_cblk_t displays[NUM_DISPLAY_MAX]; +}; + +// --------------------------------------------------------------------------- + +template struct CTA; +template<> struct CTA { }; + +// compile-time assertions. just to avoid catastrophes. +inline void compile_time_asserts() { + CTA sizeof__layer_cblk_t__eq_128; + (void)sizeof__layer_cblk_t__eq_128; // we don't want a warning + CTA sizeof__per_client_cblk_t__le_4096; + (void)sizeof__per_client_cblk_t__le_4096; // we don't want a warning + CTA sizeof__surface_flinger_cblk_t__le_4096; + (void)sizeof__surface_flinger_cblk_t__le_4096; // we don't want a warning +} + +}; // namespace android + +#endif // ANDROID_UI_SHARED_STATE_H + diff --git a/include/private/ui/SurfaceFlingerSynchro.h b/include/private/ui/SurfaceFlingerSynchro.h new file mode 100644 index 000000000..ff91b61b1 --- /dev/null +++ b/include/private/ui/SurfaceFlingerSynchro.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 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. + */ + + +#ifndef ANDROID_SURFACE_FLINGER_SYNCHRO_H +#define ANDROID_SURFACE_FLINGER_SYNCHRO_H + +#include +#include +#include +#include +#include + +namespace android { + +class SurfaceFlinger; + +class SurfaceFlingerSynchro +{ +public: + + // client constructor + SurfaceFlingerSynchro(const sp& flinger); + ~SurfaceFlingerSynchro(); + + // signal surfaceflinger for some work + status_t signal(); + +private: + class Barrier { + public: + Barrier(); + ~Barrier(); + void open(); + void close(); + void waitAndClose(); + status_t waitAndClose(nsecs_t timeout); + private: + enum { OPENED, CLOSED }; + mutable Mutex lock; + mutable Condition cv; + volatile int state; + }; + + friend class SurfaceFlinger; + + // server constructor + SurfaceFlingerSynchro(); + + void open(); + + // wait until there is some work to do + status_t wait(); + status_t wait(nsecs_t timeout); + + sp mSurfaceComposer; + Barrier mBarrier; +}; + +}; // namespace android + +#endif // ANDROID_SURFACE_FLINGER_SYNCHRO_H + diff --git a/include/private/utils/Static.h b/include/private/utils/Static.h new file mode 100644 index 000000000..f1439b75d --- /dev/null +++ b/include/private/utils/Static.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2008 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. + */ + +// All static variables go here, to control initialization and +// destruction order in the library. + +#include +#include + +#ifndef LIBUTILS_NATIVE +#include +#include +#include +#include +#include +#endif + +namespace android { +// For TextStream.cpp +extern Vector gTextBuffers; + +// For String8.cpp +extern void initialize_string8(); +extern void terminate_string8(); + +// For String16.cpp +extern void initialize_string16(); +extern void terminate_string16(); + + + +#ifndef LIBUTILS_NATIVE + +// For ProcessState.cpp +extern Mutex gProcessMutex; +extern sp gProcess; + +// For ServiceManager.cpp +extern Mutex gDefaultServiceManagerLock; +extern sp gDefaultServiceManager; +extern sp gPermissionController; + +#endif + +} // namespace android diff --git a/include/private/utils/binder_module.h b/include/private/utils/binder_module.h new file mode 100644 index 000000000..fdf327e1c --- /dev/null +++ b/include/private/utils/binder_module.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef _BINDER_MODULE_H_ +#define _BINDER_MODULE_H_ + +#ifdef __cplusplus +namespace android { +#endif + +#if defined(HAVE_ANDROID_OS) + +/* obtain structures and constants from the kernel header */ + +#include +#include + +#else + +/* Some parts of the simulator need fake versions of this + * stuff in order to compile. Really this should go away + * entirely... + */ + +#define BINDER_CURRENT_PROTOCOL_VERSION 7 + +#define BINDER_TYPE_BINDER 1 +#define BINDER_TYPE_WEAK_BINDER 2 +#define BINDER_TYPE_HANDLE 3 +#define BINDER_TYPE_WEAK_HANDLE 4 +#define BINDER_TYPE_FD 5 + +struct flat_binder_object { + unsigned long type; + unsigned long flags; + union { + void *binder; + signed long handle; + }; + void *cookie; +}; + +struct binder_write_read { + signed long write_size; + signed long write_consumed; + unsigned long write_buffer; + signed long read_size; + signed long read_consumed; + unsigned long read_buffer; +}; + +struct binder_transaction_data { + union { + size_t handle; + void *ptr; + } target; + void *cookie; + unsigned int code; + + unsigned int flags; + pid_t sender_pid; + uid_t sender_euid; + size_t data_size; + size_t offsets_size; + + union { + struct { + const void *buffer; + const void *offsets; + } ptr; + uint8_t buf[8]; + } data; +}; + +enum transaction_flags { + TF_ONE_WAY = 0x01, + TF_ROOT_OBJECT = 0x04, + TF_STATUS_CODE = 0x08, + TF_ACCEPT_FDS = 0x10, +}; + + +enum { + FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff, + FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100, +}; + +enum BinderDriverReturnProtocol { + BR_ERROR, + BR_OK, + BR_TRANSACTION, + BR_REPLY, + BR_ACQUIRE_RESULT, + BR_DEAD_REPLY, + BR_TRANSACTION_COMPLETE, + BR_INCREFS, + BR_ACQUIRE, + BR_RELEASE, + BR_DECREFS, + BR_ATTEMPT_ACQUIRE, + BR_NOOP, + BR_SPAWN_LOOPER, + BR_FINISHED, + BR_DEAD_BINDER, + BR_CLEAR_DEATH_NOTIFICATION_DONE, + BR_FAILED_REPLY, +}; + +enum BinderDriverCommandProtocol { + BC_TRANSACTION, + BC_REPLY, + BC_ACQUIRE_RESULT, + BC_FREE_BUFFER, + BC_INCREFS, + BC_ACQUIRE, + BC_RELEASE, + BC_DECREFS, + BC_INCREFS_DONE, + BC_ACQUIRE_DONE, + BC_ATTEMPT_ACQUIRE, + BC_REGISTER_LOOPER, + BC_ENTER_LOOPER, + BC_EXIT_LOOPER, + BC_REQUEST_DEATH_NOTIFICATION, + BC_CLEAR_DEATH_NOTIFICATION, + BC_DEAD_BINDER_DONE, +}; + +#endif + +#ifdef __cplusplus +} // namespace android +#endif + +#endif // _BINDER_MODULE_H_ diff --git a/include/private/utils/futex_synchro.h b/include/private/utils/futex_synchro.h new file mode 100644 index 000000000..ac2ab1995 --- /dev/null +++ b/include/private/utils/futex_synchro.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef _FUTEX_SYNCHRO_H +#define _FUTEX_SYNCHRO_H + +#ifndef HAVE_FUTEX +#error "HAVE_FUTEX not defined" +#endif + +#define FUTEX_WAIT_INFINITE (0) + +typedef struct futex_mutex_t futex_mutex_t; + +struct futex_mutex_t +{ + volatile int value; +}; + +typedef struct futex_cond_t futex_cond_t; + +struct futex_cond_t +{ + volatile int value; +}; + + +#if __cplusplus +extern "C" { +#endif + +void futex_mutex_init(futex_mutex_t *m); +int futex_mutex_lock(futex_mutex_t *m, unsigned msec); +void futex_mutex_unlock(futex_mutex_t *m); +int futex_mutex_trylock(futex_mutex_t *m); + +void futex_cond_init(futex_cond_t *c); +int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec); +void futex_cond_signal(futex_cond_t *c); +void futex_cond_broadcast(futex_cond_t *c); + +#if __cplusplus +} // extern "C" +#endif + +#endif // _FUTEX_SYNCHRO_H + diff --git a/include/ui/Camera.h b/include/ui/Camera.h new file mode 100644 index 000000000..e593feab7 --- /dev/null +++ b/include/ui/Camera.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008 HTC Inc. + * + * 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. + */ + +#ifndef ANDROID_HARDWARE_CAMERA_H +#define ANDROID_HARDWARE_CAMERA_H + +#include + +namespace android { + +/* + * A set of bit masks for specifying how the received preview frames are + * handled before the previewCallback() call. + * + * The least significant 3 bits of an "int" value are used for this purpose: + * + * ..... 0 0 0 + * ^ ^ ^ + * | | |---------> determine whether the callback is enabled or not + * | |-----------> determine whether the callback is one-shot or not + * |-------------> determine whether the frame is copied out or not + * + * WARNING: + * When a frame is sent directly without copying, it is the frame receiver's + * responsiblity to make sure that the frame data won't get corrupted by + * subsequent preview frames filled by the camera. This flag is recommended + * only when copying out data brings significant performance price and the + * handling/processing of the received frame data is always faster than + * the preview frame rate so that data corruption won't occur. + * + * For instance, + * 1. 0x00 disables the callback. In this case, copy out and one shot bits + * are ignored. + * 2. 0x01 enables a callback without copying out the received frames. A + * typical use case is the Camcorder application to avoid making costly + * frame copies. + * 3. 0x05 is enabling a callback with frame copied out repeatedly. A typical + * use case is the Camera application. + * 4. 0x07 is enabling a callback with frame copied out only once. A typical use + * case is the Barcode scanner application. + */ +#define FRAME_CALLBACK_FLAG_ENABLE_MASK 0x01 +#define FRAME_CALLBACK_FLAG_ONE_SHOT_MASK 0x02 +#define FRAME_CALLBACK_FLAG_COPY_OUT_MASK 0x04 + +// Typical use cases +#define FRAME_CALLBACK_FLAG_NOOP 0x00 +#define FRAME_CALLBACK_FLAG_CAMCORDER 0x01 +#define FRAME_CALLBACK_FLAG_CAMERA 0x05 +#define FRAME_CALLBACK_FLAG_BARCODE_SCANNER 0x07 + +class ICameraService; +class ICamera; +class Surface; +class Mutex; +class String8; + +typedef void (*shutter_callback)(void *cookie); +typedef void (*frame_callback)(const sp& mem, void *cookie); +typedef void (*autofocus_callback)(bool focused, void *cookie); +typedef void (*error_callback)(status_t err, void *cookie); + +class Camera : public BnCameraClient, public IBinder::DeathRecipient +{ +public: + // construct a camera client from an existing remote + Camera(const sp& camera); + + static sp connect(); + ~Camera(); + void init(); + + status_t reconnect(); + void disconnect(); + status_t lock(); + status_t unlock(); + + status_t getStatus() { return mStatus; } + + // pass the buffered ISurface to the camera service + status_t setPreviewDisplay(const sp& surface); + status_t setPreviewDisplay(const sp& surface); + + // start preview mode, must call setPreviewDisplay first + status_t startPreview(); + + // stop preview mode + void stopPreview(); + + // get preview state + bool previewEnabled(); + + // start recording mode, must call setPreviewDisplay first + status_t startRecording(); + + // stop recording mode + void stopRecording(); + + // get recording state + bool recordingEnabled(); + + // release a recording frame + void releaseRecordingFrame(const sp& mem); + + // autoFocus - status returned from callback + status_t autoFocus(); + + // take a picture - picture returned from callback + status_t takePicture(); + + // set preview/capture parameters - key/value pairs + status_t setParameters(const String8& params); + + // get preview/capture parameters - key/value pairs + String8 getParameters() const; + + void setShutterCallback(shutter_callback cb, void *cookie); + void setRawCallback(frame_callback cb, void *cookie); + void setJpegCallback(frame_callback cb, void *cookie); + void setRecordingCallback(frame_callback cb, void *cookie); + void setPreviewCallback(frame_callback cb, void *cookie, int preview_callback_flag = FRAME_CALLBACK_FLAG_NOOP); + void setErrorCallback(error_callback cb, void *cookie); + void setAutoFocusCallback(autofocus_callback cb, void *cookie); + + // ICameraClient interface + virtual void shutterCallback(); + virtual void rawCallback(const sp& picture); + virtual void jpegCallback(const sp& picture); + virtual void previewCallback(const sp& frame); + virtual void errorCallback(status_t error); + virtual void autoFocusCallback(bool focused); + virtual void recordingCallback(const sp& frame); + + sp remote(); + +private: + Camera(); + virtual void binderDied(const wp& who); + + class DeathNotifier: public IBinder::DeathRecipient + { + public: + DeathNotifier() { + } + + virtual void binderDied(const wp& who); + }; + + static sp mDeathNotifier; + + // helper function to obtain camera service handle + static const sp& getCameraService(); + + sp mCamera; + status_t mStatus; + + shutter_callback mShutterCallback; + void *mShutterCallbackCookie; + frame_callback mRawCallback; + void *mRawCallbackCookie; + frame_callback mJpegCallback; + void *mJpegCallbackCookie; + frame_callback mPreviewCallback; + void *mPreviewCallbackCookie; + frame_callback mRecordingCallback; + void *mRecordingCallbackCookie; + error_callback mErrorCallback; + void *mErrorCallbackCookie; + autofocus_callback mAutoFocusCallback; + void *mAutoFocusCallbackCookie; + + friend class DeathNotifier; + + static Mutex mLock; + static sp mCameraService; + +}; + +}; // namespace android + +#endif + diff --git a/include/ui/CameraHardwareInterface.h b/include/ui/CameraHardwareInterface.h new file mode 100644 index 000000000..73036f0fa --- /dev/null +++ b/include/ui/CameraHardwareInterface.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H +#define ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H + +#include +#include +#include +#include + +namespace android { + +/** Callback for startPreview() */ +typedef void (*preview_callback)(const sp& mem, void* user); + +/** Callback for startRecord() */ +typedef void (*recording_callback)(const sp& mem, void* user); + +/** Callback for takePicture() */ +typedef void (*shutter_callback)(void* user); + +/** Callback for takePicture() */ +typedef void (*raw_callback)(const sp& mem, void* user); + +/** Callback for takePicture() */ +typedef void (*jpeg_callback)(const sp& mem, void* user); + +/** Callback for autoFocus() */ +typedef void (*autofocus_callback)(bool focused, void* user); + +/** + * CameraHardwareInterface.h defines the interface to the + * camera hardware abstraction layer, used for setting and getting + * parameters, live previewing, and taking pictures. + * + * It is a referenced counted interface with RefBase as its base class. + * CameraService calls openCameraHardware() to retrieve a strong pointer to the + * instance of this interface and may be called multiple times. The + * following steps describe a typical sequence: + * + * -# After CameraService calls openCameraHardware(), getParameters() and + * setParameters() are used to initialize the camera instance. + * CameraService calls getPreviewHeap() to establish access to the + * preview heap so it can be registered with SurfaceFlinger for + * efficient display updating while in preview mode. + * -# startPreview() is called, which is passed a preview_callback() + * function and a user parameter. The camera instance then periodically + * calls preview_callback() each time a new preview frame is available. + * The callback routine has two parameters: the first is a pointer to + * the IMemory containing the frame and the second a user parameter. If + * the preview_callback code needs to use this memory after returning, + * it must copy the data. + * + * Prior to taking a picture, CameraService calls autofocus() with + * autofocus_callback() and a user parameter. When auto focusing has + * completed, the camera instance calls autofocus_callback(), which informs + * the application whether focusing was successful. The camera instance + * only calls autofocus_callback() once and it is up to the application to + * call autoFocus() again if refocusing is desired. + * + * CameraService calls takePicture() to request the camera instance take a + * picture. This method has two callbacks: raw_callback() and jpeg_callback(). + * When the raw image is available, raw_callback() is called with a pointer + * to the IMemory containing the raw image. When the jpeg image is available, + * jpeg_callback() is called with a pointer to the IMemory containing the + * jpeg image. As with preview_callback(), the memory must be copied if it's + * needed after returning. + */ +class CameraHardwareInterface : public virtual RefBase { +public: + virtual ~CameraHardwareInterface() { } + + /** Return the IMemoryHeap for the preview image heap */ + virtual sp getPreviewHeap() const = 0; + + /** Return the IMemoryHeap for the raw image heap */ + virtual sp getRawHeap() const = 0; + + /** + * Start preview mode. When a preview image is available + * preview_callback is called with the user parameter. The + * call back parameter may be null. + */ + virtual status_t startPreview(preview_callback cb, void* user) = 0; + /** + * Only used if overlays are used for camera preview. + */ + virtual bool useOverlay() {return false;} + virtual status_t setOverlay(const sp &overlay) {return BAD_VALUE;} + + /** + * Stop a previously started preview. + */ + virtual void stopPreview() = 0; + + /** + * Returns true if preview is enabled. + */ + virtual bool previewEnabled() = 0; + + /** + * Start record mode. When a record image is available recording_callback() + * is called with the user parameter. Every record frame must be released + * by calling releaseRecordingFrame(). + */ + virtual status_t startRecording(recording_callback cb, void* user) = 0; + + /** + * Stop a previously started recording. + */ + virtual void stopRecording() = 0; + + /** + * Returns true if recording is enabled. + */ + virtual bool recordingEnabled() = 0; + + /** + * Release a record frame previously returned by the recording_callback() + * passed to startRecord(). + */ + virtual void releaseRecordingFrame(const sp& mem) = 0; + + /** + * Start auto focus, the callback routine is called + * once when focusing is complete. autoFocus() will + * be called again if another auto focus is needed. + */ + virtual status_t autoFocus(autofocus_callback, + void* user) = 0; + + /** + * Take a picture. The raw_callback is called when + * the uncompressed image is available. The jpeg_callback + * is called when the compressed image is available. These + * call backs may be null. The user parameter is passed + * to each of the call back routines. + */ + virtual status_t takePicture(shutter_callback, + raw_callback, + jpeg_callback, + void* user) = 0; + + /** + * Cancel a picture that was started with takePicture. You may cancel any + * of the shutter, raw, or jpeg callbacks. Calling this method when no + * picture is being taken is a no-op. + */ + virtual status_t cancelPicture(bool cancel_shutter, + bool cancel_raw, + bool cancel_jpeg) = 0; + + /** Set the camera parameters. */ + virtual status_t setParameters(const CameraParameters& params) = 0; + + /** Return the camera parameters. */ + virtual CameraParameters getParameters() const = 0; + + /** + * Release the hardware resources owned by this object. Note that this is + * *not* done in the destructor. + */ + virtual void release() = 0; + + /** + * Dump state of the camera hardware + */ + virtual status_t dump(int fd, const Vector& args) const = 0; +}; + +/** factory function to instantiate a camera hardware object */ +extern "C" sp openCameraHardware(); + +}; // namespace android + +#endif diff --git a/include/ui/CameraParameters.h b/include/ui/CameraParameters.h new file mode 100644 index 000000000..9ca18068e --- /dev/null +++ b/include/ui/CameraParameters.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_HARDWARE_CAMERA_PARAMETERS_H +#define ANDROID_HARDWARE_CAMERA_PARAMETERS_H + +#include +#include + +namespace android { + +class CameraParameters +{ +public: + CameraParameters(); + CameraParameters(const String8 ¶ms) { unflatten(params); } + ~CameraParameters(); + + enum { + CAMERA_ORIENTATION_UNKNOWN = 0, + CAMERA_ORIENTATION_PORTRAIT = 1, + CAMERA_ORIENTATION_LANDSCAPE = 2, + }; + + String8 flatten() const; + void unflatten(const String8 ¶ms); + + void set(const char *key, const char *value); + void set(const char *key, int value); + const char *get(const char *key) const; + int getInt(const char *key) const; + + /* preview-size=176x144 */ + void setPreviewSize(int width, int height); + void getPreviewSize(int *width, int *height) const; + + /* preview-fps=15 */ + void setPreviewFrameRate(int fps); + int getPreviewFrameRate() const; + + /* preview-format=rgb565|yuv422 */ + void setPreviewFormat(const char *format); + const char *getPreviewFormat() const; + + /* picture-size=1024x768 */ + void setPictureSize(int width, int height); + void getPictureSize(int *width, int *height) const; + + /* picture-format=yuv422|jpeg */ + void setPictureFormat(const char *format); + const char *getPictureFormat() const; + + int getOrientation() const; + void setOrientation(int orientation); + + void dump() const; + status_t dump(int fd, const Vector& args) const; + +private: + DefaultKeyedVector mMap; +}; + + +}; // namespace android + +#endif diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h new file mode 100644 index 000000000..c419efeb1 --- /dev/null +++ b/include/ui/DisplayInfo.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 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. + */ + + +#ifndef ANDROID_UI_DISPLAY_INFO_H +#define ANDROID_UI_DISPLAY_INFO_H + +#include +#include + +#include + +namespace android { + +struct DisplayInfo { + uint32_t w; + uint32_t h; + PixelFormatInfo pixelFormatInfo; + uint8_t orientation; + uint8_t reserved[3]; + float fps; + float density; + float xdpi; + float ydpi; +}; + +}; // namespace android + +#endif // ANDROID_COMPOSER_DISPLAY_INFO_H + diff --git a/include/ui/EGLDisplaySurface.h b/include/ui/EGLDisplaySurface.h new file mode 100644 index 000000000..a8b58539d --- /dev/null +++ b/include/ui/EGLDisplaySurface.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_EGL_DISPLAY_SURFACE_H +#define ANDROID_EGL_DISPLAY_SURFACE_H + +#include +#include + +#include + +#include + +#include +#include + +#include + +struct copybit_device_t; +struct copybit_image_t; + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +class Region; +class Rect; + +class EGLDisplaySurface : public EGLNativeSurface +{ +public: + EGLDisplaySurface(); + ~EGLDisplaySurface(); + + int32_t getPageFlipCount() const; + void copyFrontToBack(const Region& copyback); + void copyFrontToImage(const copybit_image_t& dst); + void copyBackToImage(const copybit_image_t& dst); + + void setSwapRectangle(int l, int t, int w, int h); + +private: + static void hook_incRef(NativeWindowType window); + static void hook_decRef(NativeWindowType window); + static uint32_t hook_swapBuffers(NativeWindowType window); + + uint32_t swapBuffers(); + + status_t mapFrameBuffer(); + + enum { + PAGE_FLIP = 0x00000001 + }; + GGLSurface mFb[2]; + int mIndex; + uint32_t mFlags; + size_t mSize; + fb_var_screeninfo mInfo; + fb_fix_screeninfo mFinfo; + int32_t mPageFlipCount; + nsecs_t mTime; + int32_t mSwapCount; + nsecs_t mSleep; + uint32_t mFeatureFlags; + copybit_device_t* mBlitEngine; +}; + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#endif // ANDROID_EGL_DISPLAY_SURFACE_H + diff --git a/include/ui/EGLNativeSurface.h b/include/ui/EGLNativeSurface.h new file mode 100644 index 000000000..7964e7c6b --- /dev/null +++ b/include/ui/EGLNativeSurface.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_EGL_NATIVE_SURFACE_H +#define ANDROID_EGL_NATIVE_SURFACE_H + +#include +#include + +#include +#include + +#include + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +template +class EGLNativeSurface : public egl_native_window_t, public LightRefBase +{ +public: + EGLNativeSurface() { + memset(egl_native_window_t::reserved, 0, + sizeof(egl_native_window_t::reserved)); + memset(egl_native_window_t::reserved_proc, 0, + sizeof(egl_native_window_t::reserved_proc)); + memset(egl_native_window_t::oem, 0, + sizeof(egl_native_window_t::oem)); + } +protected: + EGLNativeSurface& operator = (const EGLNativeSurface& rhs); + EGLNativeSurface(const EGLNativeSurface& rhs); + inline ~EGLNativeSurface() { }; +}; + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#endif // ANDROID_EGL_SURFACE_H + diff --git a/include/ui/EGLNativeWindowSurface.h b/include/ui/EGLNativeWindowSurface.h new file mode 100644 index 000000000..349423463 --- /dev/null +++ b/include/ui/EGLNativeWindowSurface.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_EGL_NATIVE_WINDOW_SURFACE_H +#define ANDROID_EGL_NATIVE_WINDOW_SURFACE_H + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +class Surface; + +class EGLNativeWindowSurface : public EGLNativeSurface +{ +public: + EGLNativeWindowSurface(const sp& surface); + ~EGLNativeWindowSurface(); + + void setSwapRectangle(int l, int t, int w, int h); + +private: + static void hook_incRef(NativeWindowType window); + static void hook_decRef(NativeWindowType window); + static uint32_t hook_swapBuffers(NativeWindowType window); + static void hook_connect(NativeWindowType window); + static void hook_disconnect(NativeWindowType window); + + uint32_t swapBuffers(); + void connect(); + void disconnect(); + + sp mSurface; + bool mConnected; +}; + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#endif // ANDROID_EGL_NATIVE_WINDOW_SURFACE_H + diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h new file mode 100644 index 000000000..3848d8c3b --- /dev/null +++ b/include/ui/EventHub.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2005 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. + */ + +// +#ifndef _RUNTIME_EVENT_HUB_H +#define _RUNTIME_EVENT_HUB_H + +#include +#include +#include + +#include + +struct pollfd; + +namespace android { + +class KeyLayoutMap; + +/* + * Grand Central Station for events. With a single call to waitEvent() + * you can wait for: + * - input events from the keypad of a real device + * - input events and meta-events (e.g. "quit") from the simulator + * - synthetic events from the runtime (e.g. "URL fetch completed") + * - real or forged "vsync" events + * + * Do not instantiate this class. Instead, call startUp(). + */ +class EventHub : public RefBase +{ +public: + EventHub(); + + status_t errorCheck() const; + + // bit fields for classes of devices. + enum { + CLASS_KEYBOARD = 0x00000001, + CLASS_ALPHAKEY = 0x00000002, + CLASS_TOUCHSCREEN = 0x00000004, + CLASS_TRACKBALL = 0x00000008 + }; + uint32_t getDeviceClasses(int32_t deviceId) const; + + String8 getDeviceName(int32_t deviceId) const; + + int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, + int* outMaxValue, int* outFlat, int* outFuzz) const; + + int getSwitchState(int sw) const; + int getSwitchState(int32_t deviceId, int sw) const; + + int getScancodeState(int key) const; + int getScancodeState(int32_t deviceId, int key) const; + + int getKeycodeState(int key) const; + int getKeycodeState(int32_t deviceId, int key) const; + + // special type codes when devices are added/removed. + enum { + DEVICE_ADDED = 0x10000000, + DEVICE_REMOVED = 0x20000000 + }; + + // examine key input devices for specific framework keycode support + bool hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags); + + virtual bool getEvent(int32_t* outDeviceId, int32_t* outType, + int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, + int32_t* outValue, nsecs_t* outWhen); + +protected: + virtual ~EventHub(); + virtual void onFirstRef(); + +private: + bool openPlatformInput(void); + int32_t convertDeviceKey_TI_P2(int code); + + int open_device(const char *device); + int close_device(const char *device); + int scan_dir(const char *dirname); + int read_notify(int nfd); + + status_t mError; + + struct device_t { + const int32_t id; + const String8 path; + String8 name; + uint32_t classes; + uint8_t* keyBitmask; + KeyLayoutMap* layoutMap; + String8 keylayoutFilename; + device_t* next; + + device_t(int32_t _id, const char* _path); + ~device_t(); + }; + + device_t* getDevice(int32_t deviceId) const; + + // Protect all internal state. + mutable Mutex mLock; + + bool mHaveFirstKeyboard; + int32_t mFirstKeyboardId; // the API is that the build in keyboard is id 0, so map it + + struct device_ent { + device_t* device; + uint32_t seq; + }; + device_ent *mDevicesById; + int mNumDevicesById; + + device_t *mOpeningDevices; + device_t *mClosingDevices; + + device_t **mDevices; + struct pollfd *mFDs; + int mFDCount; + + // device ids that report particular switches. +#ifdef EV_SW + int32_t mSwitches[SW_MAX+1]; +#endif +}; + +}; // namespace android + +#endif // _RUNTIME_EVENT_HUB_H diff --git a/include/ui/ICamera.h b/include/ui/ICamera.h new file mode 100644 index 000000000..241fb6326 --- /dev/null +++ b/include/ui/ICamera.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_HARDWARE_ICAMERA_H +#define ANDROID_HARDWARE_ICAMERA_H + +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +class ICameraClient; + +class ICamera: public IInterface +{ +public: + DECLARE_META_INTERFACE(Camera); + + virtual void disconnect() = 0; + + // connect new client with existing camera remote + virtual status_t connect(const sp& client) = 0; + + // prevent other processes from using this ICamera interface + virtual status_t lock() = 0; + + // allow other processes to use this ICamera interface + virtual status_t unlock() = 0; + + // pass the buffered ISurface to the camera service + virtual status_t setPreviewDisplay(const sp& surface) = 0; + + // set the preview callback flag to affect how the received frames from + // preview are handled. + virtual void setPreviewCallbackFlag(int flag) = 0; + + // start preview mode, must call setPreviewDisplay first + virtual status_t startPreview() = 0; + + // stop preview mode + virtual void stopPreview() = 0; + + // get preview state + virtual bool previewEnabled() = 0; + + // start recording mode + virtual status_t startRecording() = 0; + + // stop recording mode + virtual void stopRecording() = 0; + + // get recording state + virtual bool recordingEnabled() = 0; + + // release a recording frame + virtual void releaseRecordingFrame(const sp& mem) = 0; + + // auto focus + virtual status_t autoFocus() = 0; + + // take a picture + virtual status_t takePicture() = 0; + + // set preview/capture parameters - key/value pairs + virtual status_t setParameters(const String8& params) = 0; + + // get preview/capture parameters - key/value pairs + virtual String8 getParameters() const = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnCamera: public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif diff --git a/include/ui/ICameraClient.h b/include/ui/ICameraClient.h new file mode 100644 index 000000000..73b951cf3 --- /dev/null +++ b/include/ui/ICameraClient.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_HARDWARE_ICAMERA_APP_H +#define ANDROID_HARDWARE_ICAMERA_APP_H + +#include +#include +#include +#include + +namespace android { + +class ICameraClient: public IInterface +{ +public: + DECLARE_META_INTERFACE(CameraClient); + + virtual void shutterCallback() = 0; + virtual void rawCallback(const sp& picture) = 0; + virtual void jpegCallback(const sp& picture) = 0; + virtual void previewCallback(const sp& frame) = 0; + virtual void errorCallback(status_t error) = 0; + virtual void autoFocusCallback(bool focused) = 0; + virtual void recordingCallback(const sp& frame) = 0; + +}; + +// ---------------------------------------------------------------------------- + +class BnCameraClient: public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif diff --git a/include/ui/ICameraService.h b/include/ui/ICameraService.h new file mode 100644 index 000000000..dfd892300 --- /dev/null +++ b/include/ui/ICameraService.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_HARDWARE_ICAMERASERVICE_H +#define ANDROID_HARDWARE_ICAMERASERVICE_H + +#include +#include +#include + +#include +#include + +namespace android { + +class ICameraService : public IInterface +{ +protected: + enum { + CONNECT = IBinder::FIRST_CALL_TRANSACTION, + }; + +public: + DECLARE_META_INTERFACE(CameraService); + + virtual sp connect(const sp& cameraClient) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnCameraService: public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif diff --git a/include/ui/IOverlay.h b/include/ui/IOverlay.h new file mode 100644 index 000000000..699b1b063 --- /dev/null +++ b/include/ui/IOverlay.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_IOVERLAY_H +#define ANDROID_IOVERLAY_H + +#include +#include + +#include +#include +#include +#include + +namespace android { + +class IOverlay : public IInterface +{ +public: + DECLARE_META_INTERFACE(Overlay); + + virtual void destroy() = 0; // one-way +}; + +// ---------------------------------------------------------------------------- + +class BnOverlay : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IOVERLAY_H diff --git a/include/ui/ISurface.h b/include/ui/ISurface.h new file mode 100644 index 000000000..87b320f43 --- /dev/null +++ b/include/ui/ISurface.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_ISURFACE_H +#define ANDROID_ISURFACE_H + +#include +#include + +#include +#include +#include +#include + +#include + +namespace android { + +typedef int32_t SurfaceID; + +class IMemoryHeap; +class OverlayRef; + +class ISurface : public IInterface +{ +protected: + enum { + REGISTER_BUFFERS = IBinder::FIRST_CALL_TRANSACTION, + UNREGISTER_BUFFERS, + POST_BUFFER, // one-way transaction + CREATE_OVERLAY, + }; + +public: + DECLARE_META_INTERFACE(Surface); + + + class BufferHeap { + public: + enum { + /* rotate source image 90 degrees */ + ROT_90 = HAL_TRANSFORM_ROT_90, + }; + BufferHeap(); + + BufferHeap(uint32_t w, uint32_t h, + int32_t hor_stride, int32_t ver_stride, + PixelFormat format, const sp& heap); + + BufferHeap(uint32_t w, uint32_t h, + int32_t hor_stride, int32_t ver_stride, + PixelFormat format, uint32_t transform, uint32_t flags, + const sp& heap); + + ~BufferHeap(); + + uint32_t w; + uint32_t h; + int32_t hor_stride; + int32_t ver_stride; + PixelFormat format; + uint32_t transform; + uint32_t flags; + sp heap; + }; + + virtual status_t registerBuffers(const BufferHeap& buffers) = 0; + + virtual void postBuffer(ssize_t offset) = 0; // one-way + + virtual void unregisterBuffers() = 0; + + virtual sp createOverlay( + uint32_t w, uint32_t h, int32_t format) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnSurface : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ISURFACE_H diff --git a/include/ui/ISurfaceComposer.h b/include/ui/ISurfaceComposer.h new file mode 100644 index 000000000..f9eeb30e1 --- /dev/null +++ b/include/ui/ISurfaceComposer.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2006 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. + */ + +#ifndef ANDROID_ISURFACE_COMPOSER_H +#define ANDROID_ISURFACE_COMPOSER_H + +#include +#include + +#include +#include +#include + +#include +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +class DisplayInfo; +class IGPUCallback; + +class ISurfaceComposer : public IInterface +{ +public: + DECLARE_META_INTERFACE(SurfaceComposer); + + enum { // (keep in sync with Surface.java) + eHidden = 0x00000004, + eGPU = 0x00000008, + eHardware = 0x00000010, + eDestroyBackbuffer = 0x00000020, + eSecure = 0x00000080, + eNonPremultiplied = 0x00000100, + ePushBuffers = 0x00000200, + + eFXSurfaceNormal = 0x00000000, + eFXSurfaceBlur = 0x00010000, + eFXSurfaceDim = 0x00020000, + eFXSurfaceMask = 0x000F0000, + }; + + enum { + ePositionChanged = 0x00000001, + eLayerChanged = 0x00000002, + eSizeChanged = 0x00000004, + eAlphaChanged = 0x00000008, + eMatrixChanged = 0x00000010, + eTransparentRegionChanged = 0x00000020, + eVisibilityChanged = 0x00000040, + eFreezeTintChanged = 0x00000080, + eDestroyed = 0x00000100 + }; + + enum { + eLayerHidden = 0x01, + eLayerFrozen = 0x02, + eLayerDither = 0x04, + eLayerFilter = 0x08, + eLayerBlurFreeze = 0x10 + }; + + enum { + eOrientationDefault = 0, + eOrientation90 = 1, + eOrientation180 = 2, + eOrientation270 = 3, + eOrientationSwapMask = 0x01 + }; + + /* create connection with surface flinger, requires + * ACCESS_SURFACE_FLINGER permission + */ + + virtual sp createConnection() = 0; + + /* retrieve the control block */ + virtual sp getCblk() const = 0; + + /* open/close transactions. recquires ACCESS_SURFACE_FLINGER permission */ + virtual void openGlobalTransaction() = 0; + virtual void closeGlobalTransaction() = 0; + + /* [un]freeze display. recquires ACCESS_SURFACE_FLINGER permission */ + virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags) = 0; + virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags) = 0; + + /* Set display orientation. recquires ACCESS_SURFACE_FLINGER permission */ + virtual int setOrientation(DisplayID dpy, int orientation) = 0; + + /* signal that we're done booting. + * recquires ACCESS_SURFACE_FLINGER permission + */ + virtual void bootFinished() = 0; + + /* get access to the GPU. Access is relinquished when releasing regs */ + struct gpu_info_t { + struct gpu_region_t { + sp region; + size_t reserved; + }; + sp regs; + size_t count; + gpu_region_t regions[2]; + }; + virtual status_t requestGPU( + const sp& callback, + gpu_info_t* gpu) = 0; + + /* take the gpu back from any apps using it. They'll get a + * EGL_CONTEXT_LOST error */ + virtual status_t revokeGPU() = 0; + + /* Signal surfaceflinger that there might be some work to do + * This is an ASYNCHRONOUS call. + */ + virtual void signal() const = 0; +}; + +class IGPUCallback : public IInterface +{ +public: + DECLARE_META_INTERFACE(GPUCallback); + virtual void gpuLost() = 0; //one-way +}; + +// ---------------------------------------------------------------------------- + +class BnSurfaceComposer : public BnInterface +{ +public: + enum { + // Note: BOOT_FINISHED must remain this value, it is called from + // Java by ActivityManagerService. + BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, + CREATE_CONNECTION, + GET_CBLK, + OPEN_GLOBAL_TRANSACTION, + CLOSE_GLOBAL_TRANSACTION, + SET_ORIENTATION, + FREEZE_DISPLAY, + UNFREEZE_DISPLAY, + REQUEST_GPU, + REVOKE_GPU, + SIGNAL + }; + + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +class BnGPUCallback : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ISURFACE_COMPOSER_H diff --git a/include/ui/ISurfaceFlingerClient.h b/include/ui/ISurfaceFlingerClient.h new file mode 100644 index 000000000..5b9361d5d --- /dev/null +++ b/include/ui/ISurfaceFlingerClient.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_ISURFACE_FLINGER_CLIENT_H +#define ANDROID_ISURFACE_FLINGER_CLIENT_H + +#include +#include + +#include +#include +#include + +#include + +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +class Rect; +class Point; +class IMemory; +class ISurface; + +typedef int32_t ClientID; +typedef int32_t DisplayID; + +// ---------------------------------------------------------------------------- + +class layer_state_t; + +class ISurfaceFlingerClient : public IInterface +{ +public: + DECLARE_META_INTERFACE(SurfaceFlingerClient); + + struct surface_data_t { + int32_t token; + int32_t identity; + sp heap[2]; + status_t readFromParcel(const Parcel& parcel); + status_t writeToParcel(Parcel* parcel) const; + }; + + virtual void getControlBlocks(sp* ctl) const = 0; + + virtual sp createSurface( surface_data_t* data, + int pid, + DisplayID display, + uint32_t w, + uint32_t h, + PixelFormat format, + uint32_t flags) = 0; + + virtual status_t destroySurface(SurfaceID sid) = 0; + + virtual status_t setState(int32_t count, const layer_state_t* states) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnSurfaceFlingerClient : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ISURFACE_FLINGER_CLIENT_H diff --git a/include/ui/KeyCharacterMap.h b/include/ui/KeyCharacterMap.h new file mode 100644 index 000000000..bad2cf86f --- /dev/null +++ b/include/ui/KeyCharacterMap.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef _UI_KEY_CHARACTER_MAP_H +#define _UI_KEY_CHARACTER_MAP_H + +#include +#include + +using namespace android; + +class KeyCharacterMap +{ +public: + ~KeyCharacterMap(); + + // see the javadoc for android.text.method.KeyCharacterMap for what + // these do + unsigned short get(int keycode, int meta); + unsigned short getNumber(int keycode); + unsigned short getMatch(int keycode, const unsigned short* chars, + int charsize, uint32_t modifiers); + unsigned short getDisplayLabel(int keycode); + bool getKeyData(int keycode, unsigned short *displayLabel, + unsigned short *number, unsigned short* results); + inline unsigned int getKeyboardType() { return m_type; } + bool getEvents(uint16_t* chars, size_t len, + Vector* keys, Vector* modifiers); + + static KeyCharacterMap* load(int id); + + enum { + NUMERIC = 1, + Q14 = 2, + QWERTY = 3 // or AZERTY or whatever + }; + +#define META_MASK 3 + +private: + struct Key + { + int32_t keycode; + uint16_t display_label; + uint16_t number; + uint16_t data[META_MASK + 1]; + }; + + KeyCharacterMap(); + static KeyCharacterMap* try_file(const char* filename); + Key* find_key(int keycode); + bool find_char(uint16_t c, uint32_t* key, uint32_t* mods); + + unsigned int m_type; + unsigned int m_keyCount; + Key* m_keys; +}; + +#endif // _UI_KEY_CHARACTER_MAP_H diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h new file mode 100644 index 000000000..efa6d2bdd --- /dev/null +++ b/include/ui/KeycodeLabels.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef _UI_KEYCODE_LABELS_H +#define _UI_KEYCODE_LABELS_H + +struct KeycodeLabel { + const char *literal; + int value; +}; + +static const KeycodeLabel KEYCODES[] = { + { "SOFT_LEFT", 1 }, + { "SOFT_RIGHT", 2 }, + { "HOME", 3 }, + { "BACK", 4 }, + { "CALL", 5 }, + { "ENDCALL", 6 }, + { "0", 7 }, + { "1", 8 }, + { "2", 9 }, + { "3", 10 }, + { "4", 11 }, + { "5", 12 }, + { "6", 13 }, + { "7", 14 }, + { "8", 15 }, + { "9", 16 }, + { "STAR", 17 }, + { "POUND", 18 }, + { "DPAD_UP", 19 }, + { "DPAD_DOWN", 20 }, + { "DPAD_LEFT", 21 }, + { "DPAD_RIGHT", 22 }, + { "DPAD_CENTER", 23 }, + { "VOLUME_UP", 24 }, + { "VOLUME_DOWN", 25 }, + { "POWER", 26 }, + { "CAMERA", 27 }, + { "CLEAR", 28 }, + { "A", 29 }, + { "B", 30 }, + { "C", 31 }, + { "D", 32 }, + { "E", 33 }, + { "F", 34 }, + { "G", 35 }, + { "H", 36 }, + { "I", 37 }, + { "J", 38 }, + { "K", 39 }, + { "L", 40 }, + { "M", 41 }, + { "N", 42 }, + { "O", 43 }, + { "P", 44 }, + { "Q", 45 }, + { "R", 46 }, + { "S", 47 }, + { "T", 48 }, + { "U", 49 }, + { "V", 50 }, + { "W", 51 }, + { "X", 52 }, + { "Y", 53 }, + { "Z", 54 }, + { "COMMA", 55 }, + { "PERIOD", 56 }, + { "ALT_LEFT", 57 }, + { "ALT_RIGHT", 58 }, + { "SHIFT_LEFT", 59 }, + { "SHIFT_RIGHT", 60 }, + { "TAB", 61 }, + { "SPACE", 62 }, + { "SYM", 63 }, + { "EXPLORER", 64 }, + { "ENVELOPE", 65 }, + { "ENTER", 66 }, + { "DEL", 67 }, + { "GRAVE", 68 }, + { "MINUS", 69 }, + { "EQUALS", 70 }, + { "LEFT_BRACKET", 71 }, + { "RIGHT_BRACKET", 72 }, + { "BACKSLASH", 73 }, + { "SEMICOLON", 74 }, + { "APOSTROPHE", 75 }, + { "SLASH", 76 }, + { "AT", 77 }, + { "NUM", 78 }, + { "HEADSETHOOK", 79 }, + { "FOCUS", 80 }, + { "PLUS", 81 }, + { "MENU", 82 }, + { "NOTIFICATION", 83 }, + { "SEARCH", 84 }, + { "PLAYPAUSE", 85 }, + { "STOP", 86 }, + { "NEXTSONG", 87 }, + { "PREVIOUSSONG", 88 }, + { "REWIND", 89 }, + { "FORWARD", 90 }, + { "MUTE", 91 }, + + // NOTE: If you add a new keycode here you must also add it to: + // (enum KeyCode, in this file) + // frameworks/base/core/java/android/view/KeyEvent.java + // tools/puppet_master/PuppetMaster.nav_keys.py + // frameworks/base/core/res/res/values/attrs.xml + + { NULL, 0 } +}; + +// These constants need to match the above mappings. +typedef enum KeyCode { + kKeyCodeUnknown = 0, + + kKeyCodeSoftLeft = 1, + kKeyCodeSoftRight = 2, + kKeyCodeHome = 3, + kKeyCodeBack = 4, + kKeyCodeCall = 5, + kKeyCodeEndCall = 6, + kKeyCode0 = 7, + kKeyCode1 = 8, + kKeyCode2 = 9, + kKeyCode3 = 10, + kKeyCode4 = 11, + kKeyCode5 = 12, + kKeyCode6 = 13, + kKeyCode7 = 14, + kKeyCode8 = 15, + kKeyCode9 = 16, + kKeyCodeStar = 17, + kKeyCodePound = 18, + kKeyCodeDpadUp = 19, + kKeyCodeDpadDown = 20, + kKeyCodeDpadLeft = 21, + kKeyCodeDpadRight = 22, + kKeyCodeDpadCenter = 23, + kKeyCodeVolumeUp = 24, + kKeyCodeVolumeDown = 25, + kKeyCodePower = 26, + kKeyCodeCamera = 27, + kKeyCodeClear = 28, + kKeyCodeA = 29, + kKeyCodeB = 30, + kKeyCodeC = 31, + kKeyCodeD = 32, + kKeyCodeE = 33, + kKeyCodeF = 34, + kKeyCodeG = 35, + kKeyCodeH = 36, + kKeyCodeI = 37, + kKeyCodeJ = 38, + kKeyCodeK = 39, + kKeyCodeL = 40, + kKeyCodeM = 41, + kKeyCodeN = 42, + kKeyCodeO = 43, + kKeyCodeP = 44, + kKeyCodeQ = 45, + kKeyCodeR = 46, + kKeyCodeS = 47, + kKeyCodeT = 48, + kKeyCodeU = 49, + kKeyCodeV = 50, + kKeyCodeW = 51, + kKeyCodeX = 52, + kKeyCodeY = 53, + kKeyCodeZ = 54, + kKeyCodeComma = 55, + kKeyCodePeriod = 56, + kKeyCodeAltLeft = 57, + kKeyCodeAltRight = 58, + kKeyCodeShiftLeft = 59, + kKeyCodeShiftRight = 60, + kKeyCodeTab = 61, + kKeyCodeSpace = 62, + kKeyCodeSym = 63, + kKeyCodeExplorer = 64, + kKeyCodeEnvelope = 65, + kKeyCodeNewline = 66, + kKeyCodeDel = 67, + kKeyCodeGrave = 68, + kKeyCodeMinus = 69, + kKeyCodeEquals = 70, + kKeyCodeLeftBracket = 71, + kKeyCodeRightBracket = 72, + kKeyCodeBackslash = 73, + kKeyCodeSemicolon = 74, + kKeyCodeApostrophe = 75, + kKeyCodeSlash = 76, + kKeyCodeAt = 77, + kKeyCodeNum = 78, + kKeyCodeHeadSetHook = 79, + kKeyCodeFocus = 80, + kKeyCodePlus = 81, + kKeyCodeMenu = 82, + kKeyCodeNotification = 83, + kKeyCodeSearch = 84, + kKeyCodePlayPause = 85, + kKeyCodeStop = 86, + kKeyCodeNextSong = 87, + kKeyCodePreviousSong = 88, + kKeyCodeRewind = 89, + kKeyCodeForward = 90, + kKeyCodeMute = 91 +} KeyCode; + +static const KeycodeLabel FLAGS[] = { + { "WAKE", 0x00000001 }, + { "WAKE_DROPPED", 0x00000002 }, + { "SHIFT", 0x00000004 }, + { "CAPS_LOCK", 0x00000008 }, + { "ALT", 0x00000010 }, + { "ALT_GR", 0x00000020 }, + { "MENU", 0x00000040 }, + { "LAUNCHER", 0x00000080 }, + { NULL, 0 } +}; + +#endif // _UI_KEYCODE_LABELS_H diff --git a/include/ui/Overlay.h b/include/ui/Overlay.h new file mode 100644 index 000000000..66514b44a --- /dev/null +++ b/include/ui/Overlay.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_OVERLAY_H +#define ANDROID_OVERLAY_H + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace android { + +class IMemory; +class IMemoryHeap; + +// ---------------------------------------------------------------------------- + +class OverlayRef : public LightRefBase +{ +public: + OverlayRef(overlay_handle_t, const sp&, + uint32_t w, uint32_t h, int32_t f, uint32_t ws, uint32_t hs); + + static sp readFromParcel(const Parcel& data); + static status_t writeToParcel(Parcel* reply, const sp& o); + +private: + friend class LightRefBase; + friend class Overlay; + + OverlayRef(); + virtual ~OverlayRef(); + + overlay_handle_t mOverlayHandle; + sp mOverlayChannel; + uint32_t mWidth; + uint32_t mHeight; + int32_t mFormat; + int32_t mWidthStride; + int32_t mHeightStride; + bool mOwnHandle; +}; + +// ---------------------------------------------------------------------------- + +class Overlay : public virtual RefBase +{ +public: + Overlay(const sp& overlayRef); + + /* destroys this overlay */ + void destroy(); + + /* get the HAL handle for this overlay */ + overlay_handle_t getHandleRef() const; + + /* blocks until an overlay buffer is available and return that buffer. */ + status_t dequeueBuffer(overlay_buffer_t* buffer); + + /* release the overlay buffer and post it */ + status_t queueBuffer(overlay_buffer_t buffer); + + /* returns the address of a given buffer if supported, NULL otherwise. */ + void* getBufferAddress(overlay_buffer_t buffer); + + /* get physical informations about the overlay */ + uint32_t getWidth() const; + uint32_t getHeight() const; + int32_t getFormat() const; + int32_t getWidthStride() const; + int32_t getHeightStride() const; + int32_t getBufferCount() const; + status_t getStatus() const; + +private: + virtual ~Overlay(); + + sp mOverlayRef; + overlay_data_device_t *mOverlayData; + status_t mStatus; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_OVERLAY_H diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h new file mode 100644 index 000000000..14af8232b --- /dev/null +++ b/include/ui/PixelFormat.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2005 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. + */ + +// + +// Pixel formats used across the system. +// These formats might not all be supported by all renderers, for instance +// skia or SurfaceFlinger are not required to support all of these formats +// (either as source or destination) + +// XXX: we should consolidate these formats and skia's + +#ifndef UI_PIXELFORMAT_H +#define UI_PIXELFORMAT_H + +#include +#include +#include +#include + +namespace android { + +enum { + // + // these constants need to match those + // in graphics/PixelFormat.java & pixelflinger/format.h + // + PIXEL_FORMAT_UNKNOWN = 0, + PIXEL_FORMAT_NONE = 0, + + // logical pixel formats used by the SurfaceFlinger ----------------------- + PIXEL_FORMAT_CUSTOM = -4, + // Custom pixel-format described by a PixelFormatInfo structure + + PIXEL_FORMAT_TRANSLUCENT = -3, + // System chooses a format that supports translucency (many alpha bits) + + PIXEL_FORMAT_TRANSPARENT = -2, + // System chooses a format that supports transparency + // (at least 1 alpha bit) + + PIXEL_FORMAT_OPAQUE = -1, + // System chooses an opaque format (no alpha bits required) + + // real pixel formats supported for rendering ----------------------------- + + PIXEL_FORMAT_RGBA_8888 = GGL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA + PIXEL_FORMAT_RGBX_8888 = GGL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0 + PIXEL_FORMAT_RGB_888 = GGL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB + PIXEL_FORMAT_RGB_565 = GGL_PIXEL_FORMAT_RGB_565, // 16-bit RGB + PIXEL_FORMAT_BGRA_8888 = GGL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA + PIXEL_FORMAT_RGBA_5551 = GGL_PIXEL_FORMAT_RGBA_5551, // 16-bit ARGB + PIXEL_FORMAT_RGBA_4444 = GGL_PIXEL_FORMAT_RGBA_4444, // 16-bit ARGB + PIXEL_FORMAT_A_8 = GGL_PIXEL_FORMAT_A_8, // 8-bit A + PIXEL_FORMAT_L_8 = GGL_PIXEL_FORMAT_L_8, // 8-bit L (R=G=B=L) + PIXEL_FORMAT_LA_88 = GGL_PIXEL_FORMAT_LA_88, // 16-bit LA + PIXEL_FORMAT_RGB_332 = GGL_PIXEL_FORMAT_RGB_332, // 8-bit RGB + + PIXEL_FORMAT_YCbCr_422_SP= GGL_PIXEL_FORMAT_YCbCr_422_SP, + PIXEL_FORMAT_YCbCr_420_SP= GGL_PIXEL_FORMAT_YCbCr_420_SP, + PIXEL_FORMAT_YCbCr_422_P = GGL_PIXEL_FORMAT_YCbCr_422_P, + PIXEL_FORMAT_YCbCr_420_P = GGL_PIXEL_FORMAT_YCbCr_420_P, + PIXEL_FORMAT_YCbCr_422_I = GGL_PIXEL_FORMAT_YCbCr_422_I, + PIXEL_FORMAT_YCbCr_420_I = GGL_PIXEL_FORMAT_YCbCr_420_I, + + // New formats can be added if they're also defined in + // pixelflinger/format.h +}; + +typedef int32_t PixelFormat; + +struct PixelFormatInfo +{ + enum { // components + ALPHA = 1, + RGB = 2, + RGBA = 3, + LUMINANCE = 4, + LUMINANCE_ALPHA = 5, + Y_CB_CR_SP = 6, + Y_CB_CR_P = 7, + Y_CB_CR_I = 8, + }; + + inline PixelFormatInfo() : version(sizeof(PixelFormatInfo)) { } + size_t getScanlineSize(unsigned int width) const; + size_t version; + PixelFormat format; + size_t bytesPerPixel; + size_t bitsPerPixel; + uint8_t h_alpha; + uint8_t l_alpha; + uint8_t h_red; + uint8_t l_red; + uint8_t h_green; + uint8_t l_green; + uint8_t h_blue; + uint8_t l_blue; + uint8_t components; + uint8_t reserved0[3]; + uint32_t reserved1; +}; + +// Consider caching the results of these functions are they're not +// guaranteed to be fast. +ssize_t bytesPerPixel(PixelFormat format); +ssize_t bitsPerPixel(PixelFormat format); +status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info); + +}; // namespace android + +#endif // UI_PIXELFORMAT_H diff --git a/include/ui/Point.h b/include/ui/Point.h new file mode 100644 index 000000000..dbbad1ecc --- /dev/null +++ b/include/ui/Point.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2006 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. + */ + +#ifndef ANDROID_UI_POINT +#define ANDROID_UI_POINT + +#include + +namespace android { + +class Point +{ +public: + int x; + int y; + + // we don't provide copy-ctor and operator= on purpose + // because we want the compiler generated versions + + // Default constructor doesn't initialize the Point + inline Point() + { + } + + inline Point(int _x, int _y) : x(_x), y(_y) + { + } + + inline bool operator == (const Point& rhs) const { + return (x == rhs.x) && (y == rhs.y); + } + inline bool operator != (const Point& rhs) const { + return !operator == (rhs); + } + + inline bool isOrigin() const { + return !(x|y); + } + + // operator < defines an order which allows to use points in sorted + // vectors. + bool operator < (const Point& rhs) const { + return y +#include + +namespace android { + +class Rect +{ +public: + int left; + int top; + int right; + int bottom; + + // we don't provide copy-ctor and operator= on purpose + // because we want the compiler generated versions + + inline Rect() + { + } + + inline Rect(int w, int h) + : left(0), top(0), right(w), bottom(h) + { + } + + inline Rect(int l, int t, int r, int b) + : left(l), top(t), right(r), bottom(b) + { + } + + inline Rect(const Point& lt, const Point& rb) + : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y) + { + } + + void makeInvalid(); + + // a valid rectangle has a non negative width and height + inline bool isValid() const { + return (width()>=0) && (height()>=0); + } + + // an empty rect has a zero width or height, or is invalid + inline bool isEmpty() const { + return (width()<=0) || (height()<=0); + } + + inline void set(const Rect& rhs) { + operator = (rhs); + } + + // rectangle's width + inline int width() const { + return right-left; + } + + // rectangle's height + inline int height() const { + return bottom-top; + } + + // returns left-top Point non-const reference, can be assigned + inline Point& leftTop() { + return reinterpret_cast(left); + } + // returns right bottom non-const reference, can be assigned + inline Point& rightBottom() { + return reinterpret_cast(right); + } + + // the following 4 functions return the 4 corners of the rect as Point + inline const Point& leftTop() const { + return reinterpret_cast(left); + } + inline const Point& rightBottom() const { + return reinterpret_cast(right); + } + Point rightTop() const { + return Point(right, top); + } + Point leftBottom() const { + return Point(left, bottom); + } + + // comparisons + inline bool operator == (const Rect& rhs) const { + return (left == rhs.left) && (top == rhs.top) && + (right == rhs.right) && (bottom == rhs.bottom); + } + + inline bool operator != (const Rect& rhs) const { + return !operator == (rhs); + } + + // operator < defines an order which allows to use rectangles in sorted + // vectors. + bool operator < (const Rect& rhs) const; + + Rect& offsetToOrigin() { + right -= left; + bottom -= top; + left = top = 0; + return *this; + } + Rect& offsetTo(const Point& p) { + return offsetTo(p.x, p.y); + } + Rect& offsetBy(const Point& dp) { + return offsetBy(dp.x, dp.y); + } + Rect& operator += (const Point& rhs) { + return offsetBy(rhs.x, rhs.y); + } + Rect& operator -= (const Point& rhs) { + return offsetBy(-rhs.x, -rhs.y); + } + Rect operator + (const Point& rhs) const; + Rect operator - (const Point& rhs) const; + + void translate(int dx, int dy) { // legacy, don't use. + offsetBy(dx, dy); + } + + Rect& offsetTo(int x, int y); + Rect& offsetBy(int x, int y); + bool intersect(const Rect& with, Rect* result) const; +}; + +ANDROID_BASIC_TYPES_TRAITS(Rect) + +}; // namespace android + +#endif // ANDROID_UI_RECT diff --git a/include/ui/Region.h b/include/ui/Region.h new file mode 100644 index 000000000..76896737f --- /dev/null +++ b/include/ui/Region.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_UI_REGION_H +#define ANDROID_UI_REGION_H + +#include +#include + +#include +#include + +#include + +#include + +#include + +namespace android { +// --------------------------------------------------------------------------- + +class String8; + +// --------------------------------------------------------------------------- +class Region +{ +public: + Region(); + Region(const Region& rhs); + explicit Region(const SkRegion& rhs); + explicit Region(const Rect& rhs); + explicit Region(const Parcel& parcel); + explicit Region(const void* buffer); + ~Region(); + + Region& operator = (const Region& rhs); + + inline bool isEmpty() const { return mRegion.isEmpty(); } + inline bool isRect() const { return mRegion.isRect(); } + + Rect bounds() const; + + const SkRegion& toSkRegion() const; + + void clear(); + void set(const Rect& r); + + Region& orSelf(const Rect& rhs); + Region& andSelf(const Rect& rhs); + + // boolean operators, applied on this + Region& orSelf(const Region& rhs); + Region& andSelf(const Region& rhs); + Region& subtractSelf(const Region& rhs); + + // these translate rhs first + Region& translateSelf(int dx, int dy); + Region& orSelf(const Region& rhs, int dx, int dy); + Region& andSelf(const Region& rhs, int dx, int dy); + Region& subtractSelf(const Region& rhs, int dx, int dy); + + // boolean operators + Region merge(const Region& rhs) const; + Region intersect(const Region& rhs) const; + Region subtract(const Region& rhs) const; + + // these translate rhs first + Region translate(int dx, int dy) const; + Region merge(const Region& rhs, int dx, int dy) const; + Region intersect(const Region& rhs, int dx, int dy) const; + Region subtract(const Region& rhs, int dx, int dy) const; + + // convenience operators overloads + inline Region operator | (const Region& rhs) const; + inline Region operator & (const Region& rhs) const; + inline Region operator - (const Region& rhs) const; + inline Region operator + (const Point& pt) const; + + inline Region& operator |= (const Region& rhs); + inline Region& operator &= (const Region& rhs); + inline Region& operator -= (const Region& rhs); + inline Region& operator += (const Point& pt); + + class iterator { + SkRegion::Iterator mIt; + public: + iterator(const Region& r); + inline operator bool () const { return !done(); } + int iterate(Rect* rect); + private: + inline bool done() const { + return const_cast(mIt).done(); + } + }; + + size_t rects(Vector& rectList) const; + + // flatten/unflatten a region to/from a Parcel + status_t write(Parcel& parcel) const; + status_t read(const Parcel& parcel); + + // flatten/unflatten a region to/from a raw buffer + ssize_t write(void* buffer, size_t size) const; + static ssize_t writeEmpty(void* buffer, size_t size); + + ssize_t read(const void* buffer); + static bool isEmpty(void* buffer); + + void dump(String8& out, const char* what, uint32_t flags=0) const; + void dump(const char* what, uint32_t flags=0) const; + +private: + SkRegion mRegion; +}; + + +Region Region::operator | (const Region& rhs) const { + return merge(rhs); +} +Region Region::operator & (const Region& rhs) const { + return intersect(rhs); +} +Region Region::operator - (const Region& rhs) const { + return subtract(rhs); +} +Region Region::operator + (const Point& pt) const { + return translate(pt.x, pt.y); +} + + +Region& Region::operator |= (const Region& rhs) { + return orSelf(rhs); +} +Region& Region::operator &= (const Region& rhs) { + return andSelf(rhs); +} +Region& Region::operator -= (const Region& rhs) { + return subtractSelf(rhs); +} +Region& Region::operator += (const Point& pt) { + return translateSelf(pt.x, pt.y); +} + +// --------------------------------------------------------------------------- + +struct region_iterator : public copybit_region_t { + region_iterator(const Region& region) : i(region) { + this->next = iterate; + } +private: + static int iterate(copybit_region_t const * self, copybit_rect_t* rect) { + return static_cast(self) + ->i.iterate(reinterpret_cast(rect)); + } + mutable Region::iterator i; +}; +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_UI_REGION_H + diff --git a/include/ui/Surface.h b/include/ui/Surface.h new file mode 100644 index 000000000..33953a953 --- /dev/null +++ b/include/ui/Surface.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_UI_SURFACE_H +#define ANDROID_UI_SURFACE_H + +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace android { + +// --------------------------------------------------------------------------- + +class Rect; +class SurfaceComposerClient; + +class Surface : public RefBase +{ + +public: + struct SurfaceInfo { + uint32_t w; + uint32_t h; + uint32_t bpr; + PixelFormat format; + void* bits; + void* base; + uint32_t reserved[2]; + }; + + bool isValid() const { return this && mToken>=0 && mClient!=0; } + SurfaceID ID() const { return mToken; } + + status_t lock(SurfaceInfo* info, bool blocking = true); + status_t lock(SurfaceInfo* info, Region* dirty, bool blocking = true); + status_t unlockAndPost(); + status_t unlock(); + + void* heapBase(int i) const; + uint32_t getFlags() const { return mFlags; } + + // setSwapRectangle() is mainly used by EGL + void setSwapRectangle(const Rect& r); + const Rect& swapRectangle() const; + status_t nextBuffer(SurfaceInfo* info); + + sp dup() const; + static sp readFromParcel(Parcel* parcel); + static status_t writeToParcel(const sp& surface, Parcel* parcel); + static bool isSameSurface(const sp& lhs, const sp& rhs); + + status_t setLayer(int32_t layer); + status_t setPosition(int32_t x, int32_t y); + status_t setSize(uint32_t w, uint32_t h); + status_t hide(); + status_t show(int32_t layer = -1); + status_t freeze(); + status_t unfreeze(); + status_t setFlags(uint32_t flags, uint32_t mask); + status_t setTransparentRegionHint(const Region& transparent); + status_t setAlpha(float alpha=1.0f); + status_t setMatrix(float dsdx, float dtdx, float dsdy, float dtdy); + status_t setFreezeTint(uint32_t tint); + + uint32_t getIdentity() const { return mIdentity; } +private: + friend class SurfaceComposerClient; + + // camera and camcorder need access to the ISurface binder interface for preview + friend class Camera; + friend class MediaRecorder; + // mediaplayer needs access to ISurface for display + friend class MediaPlayer; + friend class Test; + const sp& getISurface() const { return mSurface; } + + // can't be copied + Surface& operator = (Surface& rhs); + Surface(const Surface& rhs); + + Surface(const sp& client, + const sp& surface, + const ISurfaceFlingerClient::surface_data_t& data, + uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, + bool owner = true); + + Surface(Surface const* rhs); + + ~Surface(); + + Region dirtyRegion() const; + void setDirtyRegion(const Region& region) const; + + // this locks protects calls to lockSurface() / unlockSurface() + // and is called by SurfaceComposerClient. + Mutex& getLock() const { return mSurfaceLock; } + + sp mClient; + sp mSurface; + sp mHeap[2]; + SurfaceID mToken; + uint32_t mIdentity; + PixelFormat mFormat; + uint32_t mFlags; + const bool mOwner; + mutable void* mSurfaceHeapBase[2]; + mutable Region mDirtyRegion; + mutable Rect mSwapRectangle; + mutable uint8_t mBackbufferIndex; + mutable Mutex mSurfaceLock; +}; + +}; // namespace android + +#endif // ANDROID_UI_SURFACE_H + diff --git a/include/ui/SurfaceComposerClient.h b/include/ui/SurfaceComposerClient.h new file mode 100644 index 000000000..5d9222d85 --- /dev/null +++ b/include/ui/SurfaceComposerClient.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_SURFACE_COMPOSER_CLIENT_H +#define ANDROID_SURFACE_COMPOSER_CLIENT_H + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { + +// --------------------------------------------------------------------------- + +class Region; +class SurfaceFlingerSynchro; +struct per_client_cblk_t; +struct layer_cblk_t; + +class SurfaceComposerClient : virtual public RefBase +{ +public: + SurfaceComposerClient(); + virtual ~SurfaceComposerClient(); + + // Always make sure we could initialize + status_t initCheck() const; + + // Return the connection of this client + sp connection() const; + + // Retrieve a client for an existing connection. + static sp + clientForConnection(const sp& conn); + + // Forcibly remove connection before all references have gone away. + void dispose(); + + // ------------------------------------------------------------------------ + // surface creation / destruction + + //! Create a surface + sp createSurface( + int pid, //!< pid of the process the surfacec is for + DisplayID display, //!< Display to create this surface on + uint32_t w, //!< width in pixel + uint32_t h, //!< height in pixel + PixelFormat format, //!< pixel-format desired + uint32_t flags = 0 //!< usage flags + ); + + // ------------------------------------------------------------------------ + // Composer parameters + // All composer parameters must be changed within a transaction + // several surfaces can be updated in one transaction, all changes are + // committed at once when the transaction is closed. + // CloseTransaction() usually requires an IPC with the server. + + //! Open a composer transaction + status_t openTransaction(); + + //! commit the transaction + status_t closeTransaction(); + + //! Open a composer transaction on all active SurfaceComposerClients. + static void openGlobalTransaction(); + + //! Close a composer transaction on all active SurfaceComposerClients. + static void closeGlobalTransaction(); + + //! Freeze the specified display but not transactions. + static status_t freezeDisplay(DisplayID dpy, uint32_t flags = 0); + + //! Resume updates on the specified display. + static status_t unfreezeDisplay(DisplayID dpy, uint32_t flags = 0); + + //! Set the orientation of the given display + static int setOrientation(DisplayID dpy, int orientation); + + // Query the number of displays + static ssize_t getNumberOfDisplays(); + + // Get information about a display + static status_t getDisplayInfo(DisplayID dpy, DisplayInfo* info); + static ssize_t getDisplayWidth(DisplayID dpy); + static ssize_t getDisplayHeight(DisplayID dpy); + static ssize_t getDisplayOrientation(DisplayID dpy); + + +private: + friend class Surface; + + SurfaceComposerClient(const sp& sm, + const sp& conn); + + status_t hide(Surface* surface); + status_t show(Surface* surface, int32_t layer = -1); + status_t freeze(Surface* surface); + status_t unfreeze(Surface* surface); + status_t setFlags(Surface* surface, uint32_t flags, uint32_t mask); + status_t setTransparentRegionHint(Surface* surface, const Region& transparent); + status_t setLayer(Surface* surface, int32_t layer); + status_t setAlpha(Surface* surface, float alpha=1.0f); + status_t setFreezeTint(Surface* surface, uint32_t tint); + status_t setMatrix(Surface* surface, float dsdx, float dtdx, float dsdy, float dtdy); + status_t setPosition(Surface* surface, int32_t x, int32_t y); + status_t setSize(Surface* surface, uint32_t w, uint32_t h); + + //! Unlock the surface, and specify the dirty region if any + status_t unlockAndPostSurface(Surface* surface); + status_t unlockSurface(Surface* surface); + + status_t lockSurface(Surface* surface, + Surface::SurfaceInfo* info, + Region* dirty, + bool blocking = true); + + status_t nextBuffer(Surface* surface, + Surface::SurfaceInfo* info); + + status_t destroySurface(SurfaceID sid); + + void _init(const sp& sm, + const sp& conn); + void _signal_server(); + static void _send_dirty_region(layer_cblk_t* lcblk, const Region& dirty); + + inline layer_state_t* _get_state_l(const sp& surface); + layer_state_t* _lockLayerState(const sp& surface); + inline void _unlockLayerState(); + + status_t validateSurface( + per_client_cblk_t const* cblk, Surface const * surface); + + void pinHeap(const sp& heap); + + mutable Mutex mLock; + layer_state_t* mPrebuiltLayerState; + SortedVector mStates; + int32_t mTransactionOpen; + + // these don't need to be protected because they never change + // after assignment + status_t mStatus; + per_client_cblk_t* mControl; + sp mControlMemory; + sp mClient; + sp mSurfaceHeap; + uint8_t* mSurfaceHeapBase; + void* mGL; + SurfaceFlingerSynchro* mSignalServer; +}; + +}; // namespace android + +#endif // ANDROID_SURFACE_COMPOSER_CLIENT_H + diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 000000000..30648b185 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Handy utility functions and portability code. This file includes all +// of the generally-useful headers in the "utils" directory. +// +#ifndef _LIBS_UTILS_H +#define _LIBS_UTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // _LIBS_UTILS_H diff --git a/include/utils/AndroidUnicode.h b/include/utils/AndroidUnicode.h new file mode 100644 index 000000000..563fcd019 --- /dev/null +++ b/include/utils/AndroidUnicode.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2006 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. + */ + +// + +#ifndef ANDROID_UNICODE_H +#define ANDROID_UNICODE_H + +#include +#include + +#define REPLACEMENT_CHAR (0xFFFD) + +// this part of code is copied from umachine.h under ICU +/** + * Define UChar32 as a type for single Unicode code points. + * UChar32 is a signed 32-bit integer (same as int32_t). + * + * The Unicode code point range is 0..0x10ffff. + * All other values (negative or >=0x110000) are illegal as Unicode code points. + * They may be used as sentinel values to indicate "done", "error" + * or similar non-code point conditions. + * + * @stable ICU 2.4 + */ +typedef int32_t UChar32; + +namespace android { + + class Encoding; + /** + * \class Unicode + * + * Helper class for getting properties of Unicode characters. Characters + * can have one of the types listed in CharType and each character can have the + * directionality of Direction. + */ + class Unicode + { + public: + /** + * Directions specified in the Unicode standard. These directions map directly + * to java.lang.Character. + */ + enum Direction { + DIRECTIONALITY_UNDEFINED = -1, + DIRECTIONALITY_LEFT_TO_RIGHT, + DIRECTIONALITY_RIGHT_TO_LEFT, + DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC, + DIRECTIONALITY_EUROPEAN_NUMBER, + DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR, + DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, + DIRECTIONALITY_ARABIC_NUMBER, + DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, + DIRECTIONALITY_NONSPACING_MARK, + DIRECTIONALITY_BOUNDARY_NEUTRAL, + DIRECTIONALITY_PARAGRAPH_SEPARATOR, + DIRECTIONALITY_SEGMENT_SEPARATOR, + DIRECTIONALITY_WHITESPACE, + DIRECTIONALITY_OTHER_NEUTRALS, + DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING, + DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE, + DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING, + DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE, + DIRECTIONALITY_POP_DIRECTIONAL_FORMAT + }; + + /** + * Character types as specified in the Unicode standard. These map directly to + * java.lang.Character. + */ + enum CharType { + CHARTYPE_UNASSIGNED = 0, + CHARTYPE_UPPERCASE_LETTER, + CHARTYPE_LOWERCASE_LETTER, + CHARTYPE_TITLECASE_LETTER, + CHARTYPE_MODIFIER_LETTER, + CHARTYPE_OTHER_LETTER, + CHARTYPE_NON_SPACING_MARK, + CHARTYPE_ENCLOSING_MARK, + CHARTYPE_COMBINING_SPACING_MARK, + CHARTYPE_DECIMAL_DIGIT_NUMBER, + CHARTYPE_LETTER_NUMBER, + CHARTYPE_OTHER_NUMBER, + CHARTYPE_SPACE_SEPARATOR, + CHARTYPE_LINE_SEPARATOR, + CHARTYPE_PARAGRAPH_SEPARATOR, + CHARTYPE_CONTROL, + CHARTYPE_FORMAT, + CHARTYPE_MISSING_VALUE_FOR_JAVA, /* This is the mysterious missing 17 value from the java constants */ + CHARTYPE_PRIVATE_USE, + CHARTYPE_SURROGATE, + CHARTYPE_DASH_PUNCTUATION, + CHARTYPE_START_PUNCTUATION, + CHARTYPE_END_PUNCTUATION, + CHARTYPE_CONNECTOR_PUNCTUATION, + CHARTYPE_OTHER_PUNCTUATION, + CHARTYPE_MATH_SYMBOL, + CHARTYPE_CURRENCY_SYMBOL, + CHARTYPE_MODIFIER_SYMBOL, + CHARTYPE_OTHER_SYMBOL, + CHARTYPE_INITIAL_QUOTE_PUNCTUATION, + CHARTYPE_FINAL_QUOTE_PUNCTUATION + }; + + /** + * Decomposition types as described by the unicode standard. These values map to + * the same values in uchar.h in ICU. + */ + enum DecompositionType { + DECOMPOSITION_NONE = 0, + DECOMPOSITION_CANONICAL, + DECOMPOSITION_COMPAT, + DECOMPOSITION_CIRCLE, + DECOMPOSITION_FINAL, + DECOMPOSITION_FONT, + DECOMPOSITION_FRACTION, + DECOMPOSITION_INITIAL, + DECOMPOSITION_ISOLATED, + DECOMPOSITION_MEDIAL, + DECOMPOSITION_NARROW, + DECOMPOSITION_NOBREAK, + DECOMPOSITION_SMALL, + DECOMPOSITION_SQUARE, + DECOMPOSITION_SUB, + DECOMPOSITION_SUPER, + DECOMPOSITION_VERTICAL, + DECOMPOSITION_WIDE + }; + + /** + * Returns the packed data for java calls + * @param c The unicode character. + * @return The packed data for the character. + * + * Copied from java.lang.Character implementation: + * 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + * F E D C B A 9 8 7 6 5 4 3 2 1 0 F E D C B A 9 8 7 6 5 4 3 2 1 0 + * + * 31 types --------- + * 18 directionalities --------- + * 2 mirroreds - + * ----------- 56 toupper diffs + * ----------- 48 tolower diffs + * --- 4 totitlecase diffs + * ------------- 84 numeric values + * --------- 24 mirror char diffs + */ + static uint32_t getPackedData(UChar32 c); + + /** + * Get the Character type. + * @param c The unicode character. + * @return The character's type or CHARTYPE_UNASSIGNED if the character is invalid + * or has an unassigned class. + */ + static CharType getType(UChar32 c); + + /** + * Get the Character's decomposition type. + * @param c The unicode character. + * @return The character's decomposition type or DECOMPOSITION_NONE is there + * is no decomposition. + */ + static DecompositionType getDecompositionType(UChar32 c); + + /** + * Returns the digit value of a character or -1 if the character + * is not within the specified radix. + * + * The digit value is computed for integer characters and letters + * within the given radix. This function does not handle Roman Numerals, + * fractions, or any other characters that may represent numbers. + * + * @param c The unicode character + * @param radix The intended radix. + * @return The digit value or -1 if there is no digit value or if the value is outside the radix. + */ + static int getDigitValue(UChar32 c, int radix = 10); + + /** + * Return the numeric value of a character + * + * @param c The unicode character. + * @return The numeric value of the character. -1 if the character has no numeric value, + * -2 if the character has a numeric value that is not representable by an integer. + */ + static int getNumericValue(UChar32 c); + + /** + * Convert the character to lowercase + * @param c The unicode character. + * @return The lowercase character equivalent of c. If c does not have a lowercase equivalent, + * the original character is returned. + */ + static UChar32 toLower(UChar32 c); + + /** + * Convert the character to uppercase + * @param c The unicode character. + * @return The uppercase character equivalent of c. If c does not have an uppercase equivalent, + * the original character is returned. + */ + static UChar32 toUpper(UChar32 c); + + /** + * Get the directionality of the character. + * @param c The unicode character. + * @return The direction of the character or DIRECTIONALITY_UNDEFINED. + */ + static Direction getDirectionality(UChar32 c); + + /** + * Check if the character is a mirrored character. This means that the character + * has an equivalent character that is the mirror image of itself. + * @param c The unicode character. + * @return True iff c has a mirror equivalent. + */ + static bool isMirrored(UChar32 c); + + /** + * Return the mirror of the given character. + * @param c The unicode character. + * @return The mirror equivalent of c. If c does not have a mirror equivalent, + * the original character is returned. + * @see isMirrored + */ + static UChar32 toMirror(UChar32 c); + + /** + * Convert the character to title case. + * @param c The unicode character. + * @return The titlecase equivalent of c. If c does not have a titlecase equivalent, + * the original character is returned. + */ + static UChar32 toTitle(UChar32 c); + + }; + +} + +#endif diff --git a/include/utils/Asset.h b/include/utils/Asset.h new file mode 100644 index 000000000..453a2049a --- /dev/null +++ b/include/utils/Asset.h @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Class providing access to a read-only asset. Asset objects are NOT +// thread-safe, and should not be shared across threads. +// +#ifndef __LIBS_ASSET_H +#define __LIBS_ASSET_H + +#include +#include +#include "FileMap.h" +#include "String8.h" +#include "Errors.h" + +namespace android { + +/* + * Instances of this class provide read-only operations on a byte stream. + * + * Access may be optimized for streaming, random, or whole buffer modes. All + * operations are supported regardless of how the file was opened, but some + * things will be less efficient. [pass that in??] + * + * "Asset" is the base class for all types of assets. The classes below + * provide most of the implementation. The AssetManager uses one of the + * static "create" functions defined here to create a new instance. + */ +class Asset { +public: + virtual ~Asset(void); + + static int32_t getGlobalCount(); + + /* used when opening an asset */ + typedef enum AccessMode { + ACCESS_UNKNOWN = 0, + + /* read chunks, and seek forward and backward */ + ACCESS_RANDOM, + + /* read sequentially, with an occasional forward seek */ + ACCESS_STREAMING, + + /* caller plans to ask for a read-only buffer with all data */ + ACCESS_BUFFER, + } AccessMode; + + enum { + /* data larger than this does not get uncompressed into a buffer */ +#ifdef HAVE_ANDROID_OS + UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024 +#else + UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024 +#endif + }; + + /* + * Read data from the current offset. Returns the actual number of + * bytes read, 0 on EOF, or -1 on error. + */ + virtual ssize_t read(void* buf, size_t count) = 0; + + /* + * Seek to the specified offset. "whence" uses the same values as + * lseek/fseek. Returns the new position on success, or (off_t) -1 + * on failure. + */ + virtual off_t seek(off_t offset, int whence) = 0; + + /* + * Close the asset, freeing all associated resources. + */ + virtual void close(void) = 0; + + /* + * Get a pointer to a buffer with the entire contents of the file. + */ + virtual const void* getBuffer(bool wordAligned) = 0; + + /* + * Get the total amount of data that can be read. + */ + virtual off_t getLength(void) const = 0; + + /* + * Get the total amount of data that can be read from the current position. + */ + virtual off_t getRemainingLength(void) const = 0; + + /* + * Open a new file descriptor that can be used to read this asset. + * Returns -1 if you can not use the file descriptor (for example if the + * asset is compressed). + */ + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const = 0; + + /* + * Get a string identifying the asset's source. This might be a full + * path, it might be a colon-separated list of identifiers. + * + * This is NOT intended to be used for anything except debug output. + * DO NOT try to parse this or use it to open a file. + */ + const char* getAssetSource(void) const { return mAssetSource.string(); } + +protected: + Asset(void); // constructor; only invoked indirectly + + /* handle common seek() housekeeping */ + off_t handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn); + + /* set the asset source string */ + void setAssetSource(const String8& path) { mAssetSource = path; } + + AccessMode getAccessMode(void) const { return mAccessMode; } + +private: + /* these operations are not implemented */ + Asset(const Asset& src); + Asset& operator=(const Asset& src); + + /* AssetManager needs access to our "create" functions */ + friend class AssetManager; + + /* + * Create the asset from a named file on disk. + */ + static Asset* createFromFile(const char* fileName, AccessMode mode); + + /* + * Create the asset from a named, compressed file on disk (e.g. ".gz"). + */ + static Asset* createFromCompressedFile(const char* fileName, + AccessMode mode); + +#if 0 + /* + * Create the asset from a segment of an open file. This will fail + * if "offset" and "length" don't fit within the bounds of the file. + * + * The asset takes ownership of the file descriptor. + */ + static Asset* createFromFileSegment(int fd, off_t offset, size_t length, + AccessMode mode); + + /* + * Create from compressed data. "fd" should be seeked to the start of + * the compressed data. This could be inside a gzip file or part of a + * Zip archive. + * + * The asset takes ownership of the file descriptor. + * + * This may not verify the validity of the compressed data until first + * use. + */ + static Asset* createFromCompressedData(int fd, off_t offset, + int compressionMethod, size_t compressedLength, + size_t uncompressedLength, AccessMode mode); +#endif + + /* + * Create the asset from a memory-mapped file segment. + * + * The asset takes ownership of the FileMap. + */ + static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode); + + /* + * Create the asset from a memory-mapped file segment with compressed + * data. "method" is a Zip archive compression method constant. + * + * The asset takes ownership of the FileMap. + */ + static Asset* createFromCompressedMap(FileMap* dataMap, int method, + size_t uncompressedLen, AccessMode mode); + + + /* + * Create from a reference-counted chunk of shared memory. + */ + // TODO + + AccessMode mAccessMode; // how the asset was opened + String8 mAssetSource; // debug string +}; + + +/* + * =========================================================================== + * + * Innards follow. Do not use these classes directly. + */ + +/* + * An asset based on an uncompressed file on disk. It may encompass the + * entire file or just a piece of it. Access is through fread/fseek. + */ +class _FileAsset : public Asset { +public: + _FileAsset(void); + virtual ~_FileAsset(void); + + /* + * Use a piece of an already-open file. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(const char* fileName, int fd, off_t offset, size_t length); + + /* + * Use a memory-mapped region. + * + * On success, the object takes ownership of "dataMap". + */ + status_t openChunk(FileMap* dataMap); + + /* + * Standard Asset interfaces. + */ + virtual ssize_t read(void* buf, size_t count); + virtual off_t seek(off_t offset, int whence); + virtual void close(void); + virtual const void* getBuffer(bool wordAligned); + virtual off_t getLength(void) const { return mLength; } + virtual off_t getRemainingLength(void) const { return mLength-mOffset; } + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const; + +private: + off_t mStart; // absolute file offset of start of chunk + off_t mLength; // length of the chunk + off_t mOffset; // current local offset, 0 == mStart + FILE* mFp; // for read/seek + char* mFileName; // for opening + + /* + * To support getBuffer() we either need to read the entire thing into + * a buffer or memory-map it. For small files it's probably best to + * just read them in. + */ + enum { kReadVsMapThreshold = 4096 }; + + FileMap* mMap; // for memory map + unsigned char* mBuf; // for read + + const void* ensureAlignment(FileMap* map); +}; + + +/* + * An asset based on compressed data in a file. + */ +class _CompressedAsset : public Asset { +public: + _CompressedAsset(void); + virtual ~_CompressedAsset(void); + + /* + * Use a piece of an already-open file. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(int fd, off_t offset, int compressionMethod, + size_t uncompressedLen, size_t compressedLen); + + /* + * Use a memory-mapped region. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(FileMap* dataMap, int compressionMethod, + size_t uncompressedLen); + + /* + * Standard Asset interfaces. + */ + virtual ssize_t read(void* buf, size_t count); + virtual off_t seek(off_t offset, int whence); + virtual void close(void); + virtual const void* getBuffer(bool wordAligned); + virtual off_t getLength(void) const { return mUncompressedLen; } + virtual off_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const { return -1; } + +private: + off_t mStart; // offset to start of compressed data + off_t mCompressedLen; // length of the compressed data + off_t mUncompressedLen; // length of the uncompressed data + off_t mOffset; // current offset, 0 == start of uncomp data + + FileMap* mMap; // for memory-mapped input + int mFd; // for file input + + unsigned char* mBuf; // for getBuffer() +}; + +// need: shared mmap version? + +}; // namespace android + +#endif // __LIBS_ASSET_H diff --git a/include/utils/AssetDir.h b/include/utils/AssetDir.h new file mode 100644 index 000000000..abf8a3595 --- /dev/null +++ b/include/utils/AssetDir.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access a chunk of the asset hierarchy as if it were a single directory. +// +#ifndef __LIBS_ASSETDIR_H +#define __LIBS_ASSETDIR_H + +#include +#include +#include +#include +#include + +namespace android { + +/* + * This provides vector-style access to a directory. We do this rather + * than modeling opendir/readdir access because it's simpler and the + * nature of the operation requires us to have all data on hand anyway. + * + * The list of files will be sorted in ascending order by ASCII value. + * + * The contents are populated by our friend, the AssetManager. + */ +class AssetDir { +public: + AssetDir(void) + : mFileInfo(NULL) + {} + virtual ~AssetDir(void) { + delete mFileInfo; + } + + /* + * Vector-style access. + */ + size_t getFileCount(void) { return mFileInfo->size(); } + const String8& getFileName(int idx) { + return mFileInfo->itemAt(idx).getFileName(); + } + const String8& getSourceName(int idx) { + return mFileInfo->itemAt(idx).getSourceName(); + } + + /* + * Get the type of a file (usually regular or directory). + */ + FileType getFileType(int idx) { + return mFileInfo->itemAt(idx).getFileType(); + } + +private: + /* these operations are not implemented */ + AssetDir(const AssetDir& src); + const AssetDir& operator=(const AssetDir& src); + + friend class AssetManager; + + /* + * This holds information about files in the asset hierarchy. + */ + class FileInfo { + public: + FileInfo(void) {} + FileInfo(const String8& path) // useful for e.g. svect.indexOf + : mFileName(path), mFileType(kFileTypeUnknown) + {} + ~FileInfo(void) {} + FileInfo(const FileInfo& src) { + copyMembers(src); + } + const FileInfo& operator= (const FileInfo& src) { + if (this != &src) + copyMembers(src); + return *this; + } + + void copyMembers(const FileInfo& src) { + mFileName = src.mFileName; + mFileType = src.mFileType; + mSourceName = src.mSourceName; + } + + /* need this for SortedVector; must compare only on file name */ + bool operator< (const FileInfo& rhs) const { + return mFileName < rhs.mFileName; + } + + /* used by AssetManager */ + bool operator== (const FileInfo& rhs) const { + return mFileName == rhs.mFileName; + } + + void set(const String8& path, FileType type) { + mFileName = path; + mFileType = type; + } + + const String8& getFileName(void) const { return mFileName; } + void setFileName(const String8& path) { mFileName = path; } + + FileType getFileType(void) const { return mFileType; } + void setFileType(FileType type) { mFileType = type; } + + const String8& getSourceName(void) const { return mSourceName; } + void setSourceName(const String8& path) { mSourceName = path; } + + /* + * Handy utility for finding an entry in a sorted vector of FileInfo. + * Returns the index of the matching entry, or -1 if none found. + */ + static int findEntry(const SortedVector* pVector, + const String8& fileName); + + private: + String8 mFileName; // filename only + FileType mFileType; // regular, directory, etc + + String8 mSourceName; // currently debug-only + }; + + /* AssetManager uses this to initialize us */ + void setFileList(SortedVector* list) { mFileInfo = list; } + + SortedVector* mFileInfo; +}; + +}; // namespace android + +#endif // __LIBS_ASSETDIR_H diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h new file mode 100644 index 000000000..e94c0e8fe --- /dev/null +++ b/include/utils/AssetManager.h @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Asset management class. AssetManager objects are thread-safe. +// +#ifndef __LIBS_ASSETMANAGER_H +#define __LIBS_ASSETMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +class Asset; // fwd decl for things that include Asset.h first +class ResTable; +struct ResTable_config; + +/* + * Every application that uses assets needs one instance of this. A + * single instance may be shared across multiple threads, and a single + * thread may have more than one instance (the latter is discouraged). + * + * The purpose of the AssetManager is to create Asset objects. To do + * this efficiently it may cache information about the locations of + * files it has seen. This can be controlled with the "cacheMode" + * argument. + * + * The asset hierarchy may be examined like a filesystem, using + * AssetDir objects to peruse a single directory. + */ +class AssetManager { +public: + typedef enum CacheMode { + CACHE_UNKNOWN = 0, + CACHE_OFF, // don't try to cache file locations + CACHE_DEFER, // construct cache as pieces are needed + //CACHE_SCAN, // scan full(!) asset hierarchy at init() time + } CacheMode; + + AssetManager(CacheMode cacheMode = CACHE_OFF); + virtual ~AssetManager(void); + + static int32_t getGlobalCount(); + + /* + * Add a new source for assets. This can be called multiple times to + * look in multiple places for assets. It can be either a directory (for + * finding assets as raw files on the disk) or a ZIP file. This newly + * added asset path will be examined first when searching for assets, + * before any that were previously added. + * + * Returns "true" on success, "false" on failure. If 'cookie' is non-NULL, + * then on success, *cookie is set to the value corresponding to the + * newly-added asset source. + */ + bool addAssetPath(const String8& path, void** cookie); + + /* + * Convenience for adding the standard system assets. Uses the + * ANDROID_ROOT environment variable to find them. + */ + bool addDefaultAssets(); + + /* + * Iterate over the asset paths in this manager. (Previously + * added via addAssetPath() and addDefaultAssets().) On first call, + * 'cookie' must be NULL, resulting in the first cookie being returned. + * Each next cookie will be returned there-after, until NULL indicating + * the end has been reached. + */ + void* nextAssetPath(void* cookie) const; + + /* + * Return an asset path in the manager. 'which' must be between 0 and + * countAssetPaths(). + */ + String8 getAssetPath(void* cookie) const; + + /* + * Set the current locale and vendor. The locale can change during + * the lifetime of an AssetManager if the user updates the device's + * language setting. The vendor is less likely to change. + * + * Pass in NULL to indicate no preference. + */ + void setLocale(const char* locale); + void setVendor(const char* vendor); + + /* + * Choose screen orientation for resources values returned. + */ + void setConfiguration(const ResTable_config& config, const char* locale = NULL); + + typedef Asset::AccessMode AccessMode; // typing shortcut + + /* + * Open an asset. + * + * This will search through locale-specific and vendor-specific + * directories and packages to find the file. + * + * The object returned does not depend on the AssetManager. It should + * be freed by calling Asset::close(). + */ + Asset* open(const char* fileName, AccessMode mode); + + /* + * Open a non-asset file as an asset. + * + * This is for opening files that are included in an asset package + * but aren't assets. These sit outside the usual "locale/vendor" + * path hierarchy, and will not be seen by "AssetDir" or included + * in our filename cache. + */ + Asset* openNonAsset(const char* fileName, AccessMode mode); + + /* + * Explicit non-asset file. The file explicitly named by the cookie (the + * resource set to look in) and fileName will be opened and returned. + */ + Asset* openNonAsset(void* cookie, const char* fileName, AccessMode mode); + + /* + * Open a directory within the asset hierarchy. + * + * The contents of the directory are an amalgam of vendor-specific, + * locale-specific, and generic assets stored loosely or in asset + * packages. Depending on the cache setting and previous accesses, + * this call may incur significant disk overhead. + * + * To open the top-level directory, pass in "". + */ + AssetDir* openDir(const char* dirName); + + /* + * Get the type of a file in the asset hierarchy. They will either + * be "regular" or "directory". [Currently only works for "regular".] + * + * Can also be used as a quick test for existence of a file. + */ + FileType getFileType(const char* fileName); + + /* + * Return the complete resource table to find things in the package. + */ + const ResTable& getResources(bool required = true) const; + + /* + * Discard cached filename information. This only needs to be called + * if somebody has updated the set of "loose" files, and we want to + * discard our cached notion of what's where. + */ + void purge(void) { purgeFileNameCacheLocked(); } + + /* + * Return true if the files this AssetManager references are all + * up-to-date (have not been changed since it was created). If false + * is returned, you will need to create a new AssetManager to get + * the current data. + */ + bool isUpToDate(); + + /** + * Get the known locales for this asset manager object. + */ + void getLocales(Vector* locales) const; + +private: + struct asset_path + { + String8 path; + FileType type; + }; + + Asset* openInPathLocked(const char* fileName, AccessMode mode, + const asset_path& path); + Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode, + const asset_path& path); + Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode, + const asset_path& path, const char* locale, const char* vendor); + String8 createPathNameLocked(const asset_path& path, const char* locale, + const char* vendor); + String8 createPathNameLocked(const asset_path& path, const char* rootDir); + String8 createZipSourceNameLocked(const String8& zipFileName, + const String8& dirName, const String8& fileName); + + ZipFileRO* getZipFileLocked(const asset_path& path); + Asset* openAssetFromFileLocked(const String8& fileName, AccessMode mode); + Asset* openAssetFromZipLocked(const ZipFileRO* pZipFile, + const ZipEntryRO entry, AccessMode mode, const String8& entryName); + + bool scanAndMergeDirLocked(SortedVector* pMergedInfo, + const asset_path& path, const char* rootDir, const char* dirName); + SortedVector* scanDirLocked(const String8& path); + bool scanAndMergeZipLocked(SortedVector* pMergedInfo, + const asset_path& path, const char* rootDir, const char* dirName); + void mergeInfoLocked(SortedVector* pMergedInfo, + const SortedVector* pContents); + + void loadFileNameCacheLocked(void); + void fncScanLocked(SortedVector* pMergedInfo, + const char* dirName); + bool fncScanAndMergeDirLocked( + SortedVector* pMergedInfo, + const asset_path& path, const char* locale, const char* vendor, + const char* dirName); + void purgeFileNameCacheLocked(void); + + const ResTable* getResTable(bool required = true) const; + void setLocaleLocked(const char* locale); + void updateResourceParamsLocked() const; + + class SharedZip : public RefBase { + public: + static sp get(const String8& path); + + ZipFileRO* getZip(); + + Asset* getResourceTableAsset(); + Asset* setResourceTableAsset(Asset* asset); + + bool isUpToDate(); + + protected: + ~SharedZip(); + + private: + SharedZip(const String8& path, time_t modWhen); + SharedZip(); // <-- not implemented + + String8 mPath; + ZipFileRO* mZipFile; + time_t mModWhen; + + Asset* mResourceTableAsset; + + static Mutex gLock; + static DefaultKeyedVector > gOpen; + }; + + /* + * Manage a set of Zip files. For each file we need a pointer to the + * ZipFile and a time_t with the file's modification date. + * + * We currently only have two zip files (current app, "common" app). + * (This was originally written for 8, based on app/locale/vendor.) + */ + class ZipSet { + public: + ZipSet(void); + ~ZipSet(void); + + /* + * Return a ZipFileRO structure for a ZipFileRO with the specified + * parameters. + */ + ZipFileRO* getZip(const String8& path); + + Asset* getZipResourceTable(const String8& path); + Asset* setZipResourceTable(const String8& path, Asset* asset); + + // generate path, e.g. "common/en-US-noogle.zip" + static String8 getPathName(const char* path); + + bool isUpToDate(); + + private: + void closeZip(int idx); + + int getIndex(const String8& zip) const; + mutable Vector mZipPath; + mutable Vector > mZipFile; + }; + + // Protect all internal state. + mutable Mutex mLock; + + ZipSet mZipSet; + + Vector mAssetPaths; + char* mLocale; + char* mVendor; + + mutable ResTable* mResources; + ResTable_config* mConfig; + + /* + * Cached data for "loose" files. This lets us avoid poking at the + * filesystem when searching for loose assets. Each entry is the + * "extended partial" path, e.g. "default/default/foo/bar.txt". The + * full set of files is present, including ".EXCLUDE" entries. + * + * We do not cache directory names. We don't retain the ".gz", + * because to our clients "foo" and "foo.gz" both look like "foo". + */ + CacheMode mCacheMode; // is the cache enabled? + bool mCacheValid; // clear when locale or vendor changes + SortedVector mCache; +}; + +}; // namespace android + +#endif // __LIBS_ASSETMANAGER_H diff --git a/include/utils/Atomic.h b/include/utils/Atomic.h new file mode 100644 index 000000000..7eb476c94 --- /dev/null +++ b/include/utils/Atomic.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_UTILS_ATOMIC_H +#define ANDROID_UTILS_ATOMIC_H + +#include + +#endif // ANDROID_UTILS_ATOMIC_H diff --git a/include/utils/Binder.h b/include/utils/Binder.h new file mode 100644 index 000000000..b5b8d9851 --- /dev/null +++ b/include/utils/Binder.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_BINDER_H +#define ANDROID_BINDER_H + +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BBinder : public IBinder +{ +public: + BBinder(); + + virtual String16 getInterfaceDescriptor() const; + virtual bool isBinderAlive() const; + virtual status_t pingBinder(); + virtual status_t dump(int fd, const Vector& args); + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0); + + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func); + virtual void* findObject(const void* objectID) const; + virtual void detachObject(const void* objectID); + + virtual BBinder* localBinder(); + +protected: + virtual ~BBinder(); + + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + +private: + BBinder(const BBinder& o); + BBinder& operator=(const BBinder& o); + + class Extras; + + Extras* mExtras; + void* mReserved0; +}; + +// --------------------------------------------------------------------------- + +class BpRefBase : public virtual RefBase +{ +protected: + BpRefBase(const sp& o); + virtual ~BpRefBase(); + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + + inline IBinder* remote() { return mRemote; } + inline IBinder* remote() const { return mRemote; } + +private: + BpRefBase(const BpRefBase& o); + BpRefBase& operator=(const BpRefBase& o); + + IBinder* const mRemote; + RefBase::weakref_type* mRefs; + volatile int32_t mState; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_BINDER_H diff --git a/include/utils/BpBinder.h b/include/utils/BpBinder.h new file mode 100644 index 000000000..7b96e296f --- /dev/null +++ b/include/utils/BpBinder.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_BPBINDER_H +#define ANDROID_BPBINDER_H + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BpBinder : public IBinder +{ +public: + BpBinder(int32_t handle); + + inline int32_t handle() const { return mHandle; } + + virtual String16 getInterfaceDescriptor() const; + virtual bool isBinderAlive() const; + virtual status_t pingBinder(); + virtual status_t dump(int fd, const Vector& args); + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0); + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func); + virtual void* findObject(const void* objectID) const; + virtual void detachObject(const void* objectID); + + virtual BpBinder* remoteBinder(); + + status_t setConstantData(const void* data, size_t size); + void sendObituary(); + + class ObjectManager + { + public: + ObjectManager(); + ~ObjectManager(); + + void attach( const void* objectID, + void* object, + void* cleanupCookie, + IBinder::object_cleanup_func func); + void* find(const void* objectID) const; + void detach(const void* objectID); + + void kill(); + + private: + ObjectManager(const ObjectManager&); + ObjectManager& operator=(const ObjectManager&); + + struct entry_t + { + void* object; + void* cleanupCookie; + IBinder::object_cleanup_func func; + }; + + KeyedVector mObjects; + }; + +protected: + virtual ~BpBinder(); + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + +private: + const int32_t mHandle; + + struct Obituary { + wp recipient; + void* cookie; + uint32_t flags; + }; + + void reportOneDeath(const Obituary& obit); + + mutable Mutex mLock; + volatile int32_t mAlive; + volatile int32_t mObitsSent; + Vector* mObituaries; + ObjectManager mObjects; + Parcel* mConstantData; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_BPBINDER_H diff --git a/include/utils/Buffer.h b/include/utils/Buffer.h new file mode 100644 index 000000000..8e22b0f21 --- /dev/null +++ b/include/utils/Buffer.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef __UTILS_BUFFER_H__ +#define __UTILS_BUFFER_H__ 1 + +#include +#include +#include + +namespace android { + +class Buffer +{ +private: + char *buf; + int bufsiz; + int used; + void ensureCapacity(int len); + + void + makeRoomFor(int len) + { + if (len + used >= bufsiz) { + bufsiz = (len + used) * 3/2 + 2; + char *blah = new char[bufsiz]; + + memcpy(blah, buf, used); + delete[] buf; + buf = blah; + } + } + +public: + Buffer() + { + bufsiz = 16; + buf = new char[bufsiz]; + clear(); + } + + ~Buffer() + { + delete[] buf; + } + + void + clear() + { + buf[0] = '\0'; + used = 0; + } + + int + length() + { + return used; + } + + void + append(const char c) + { + makeRoomFor(1); + buf[used] = c; + used++; + buf[used] = '\0'; + } + + void + append(const char *s, int len) + { + makeRoomFor(len); + + memcpy(buf + used, s, len); + used += len; + buf[used] = '\0'; + } + + void + append(const char *s) + { + append(s, strlen(s)); + } + + char * + getBytes() + { + return buf; + } +}; + +}; // namespace android + +#endif diff --git a/include/utils/BufferedTextOutput.h b/include/utils/BufferedTextOutput.h new file mode 100644 index 000000000..69c624037 --- /dev/null +++ b/include/utils/BufferedTextOutput.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 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. + */ + +#ifndef ANDROID_BUFFEREDTEXTOUTPUT_H +#define ANDROID_BUFFEREDTEXTOUTPUT_H + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BufferedTextOutput : public TextOutput +{ +public: + //** Flags for constructor */ + enum { + MULTITHREADED = 0x0001 + }; + + BufferedTextOutput(uint32_t flags = 0); + virtual ~BufferedTextOutput(); + + virtual status_t print(const char* txt, size_t len); + virtual void moveIndent(int delta); + + virtual void pushBundle(); + virtual void popBundle(); + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) = 0; + +private: + struct BufferState; + struct ThreadState; + + static ThreadState*getThreadState(); + static void threadDestructor(void *st); + + BufferState*getBuffer() const; + + uint32_t mFlags; + const int32_t mSeq; + const int32_t mIndex; + + Mutex mLock; + BufferState* mGlobalState; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_BUFFEREDTEXTOUTPUT_H diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h new file mode 100644 index 000000000..4c0606763 --- /dev/null +++ b/include/utils/ByteOrder.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006 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. + */ + +// + +#ifndef _LIBS_UTILS_BYTE_ORDER_H +#define _LIBS_UTILS_BYTE_ORDER_H + +#include +#include +#ifdef HAVE_WINSOCK +#include +#else +#include +#endif + +/* + * These macros are like the hton/ntoh byte swapping macros, + * except they allow you to swap to and from the "device" byte + * order. The device byte order is the endianness of the target + * device -- for the ARM CPUs we use today, this is little endian. + * + * Note that the byte swapping functions have not been optimized + * much; performance is currently not an issue for them since the + * intent is to allow us to avoid byte swapping on the device. + */ + +#define DEVICE_BYTE_ORDER LITTLE_ENDIAN + +#if BYTE_ORDER == DEVICE_BYTE_ORDER + +#define dtohl(x) (x) +#define dtohs(x) (x) +#define htodl(x) (x) +#define htods(x) (x) + +#else + +static inline uint32_t android_swap_long(uint32_t v) +{ + return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24); +} + +static inline uint16_t android_swap_short(uint16_t v) +{ + return (v<<8) | (v>>8); +} + +#define dtohl(x) (android_swap_long(x)) +#define dtohs(x) (android_swap_short(x)) +#define htodl(x) (android_swap_long(x)) +#define htods(x) (android_swap_short(x)) + +#endif + +#endif // _LIBS_UTILS_BYTE_ORDER_H diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h new file mode 100644 index 000000000..c2c8ce514 --- /dev/null +++ b/include/utils/CallStack.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_CALLSTACK_H +#define ANDROID_CALLSTACK_H + +#include +#include + +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class CallStack +{ +public: + enum { + MAX_DEPTH = 31 + }; + + CallStack(); + CallStack(const CallStack& rhs); + ~CallStack(); + + CallStack& operator = (const CallStack& rhs); + + bool operator == (const CallStack& rhs) const; + bool operator != (const CallStack& rhs) const; + bool operator < (const CallStack& rhs) const; + bool operator >= (const CallStack& rhs) const; + bool operator > (const CallStack& rhs) const; + bool operator <= (const CallStack& rhs) const; + + const void* operator [] (int index) const; + + void clear(); + + void update(int32_t ignoreDepth=0, int32_t maxDepth=MAX_DEPTH); + + // Dump a stack trace to the log + void dump(const char* prefix = 0) const; + + // Return a string (possibly very long) containing the complete stack trace + String8 toString(const char* prefix = 0) const; + + size_t size() const { return mCount; } + +private: + // Internal helper function + String8 toStringSingleLevel(const char* prefix, int32_t level) const; + + size_t mCount; + const void* mStack[MAX_DEPTH]; +}; + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_CALLSTACK_H diff --git a/include/utils/Debug.h b/include/utils/Debug.h new file mode 100644 index 000000000..a662b9cc2 --- /dev/null +++ b/include/utils/Debug.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Debugging tools. These should be able to be stripped +// in release builds. +// +#ifndef ANDROID_DEBUG_H +#define ANDROID_DEBUG_H + +#include +#include + +namespace android { + +template struct CompileTimeAssert; +template<> struct CompileTimeAssert {}; + +const char* stringForIndent(int32_t indentLevel); + +typedef void (*debugPrintFunc)(void* cookie, const char* txt); + +void printTypeCode(uint32_t typeCode, + debugPrintFunc func = 0, void* cookie = 0); +void printHexData(int32_t indent, const void *buf, size_t length, + size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16, + size_t alignment=0, bool cArrayStyle=false, + debugPrintFunc func = 0, void* cookie = 0); + +}; // namespace android + +#endif // ANDROID_DEBUG_H diff --git a/include/utils/Endian.h b/include/utils/Endian.h new file mode 100644 index 000000000..19f250494 --- /dev/null +++ b/include/utils/Endian.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Android endian-ness defines. +// +#ifndef _LIBS_UTILS_ENDIAN_H +#define _LIBS_UTILS_ENDIAN_H + +#if defined(HAVE_ENDIAN_H) + +#include + +#else /*not HAVE_ENDIAN_H*/ + +#define __BIG_ENDIAN 0x1000 +#define __LITTLE_ENDIAN 0x0001 + +#if defined(HAVE_LITTLE_ENDIAN) +# define __BYTE_ORDER __LITTLE_ENDIAN +#else +# define __BYTE_ORDER __BIG_ENDIAN +#endif + +#endif /*not HAVE_ENDIAN_H*/ + +#endif /*_LIBS_UTILS_ENDIAN_H*/ diff --git a/include/utils/Errors.h b/include/utils/Errors.h new file mode 100644 index 000000000..1bf9e6f2b --- /dev/null +++ b/include/utils/Errors.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_ERRORS_H +#define ANDROID_ERRORS_H + +#include +#include + +namespace android { + +// use this type to return error codes +#ifdef HAVE_MS_C_RUNTIME +typedef int status_t; +#else +typedef int32_t status_t; +#endif + +/* the MS C runtime lacks a few error codes */ + +/* + * Error codes. + * All error codes are negative values. + */ + +// Win32 #defines NO_ERROR as well. It has the same value, so there's no +// real conflict, though it's a bit awkward. +#ifdef _WIN32 +# undef NO_ERROR +#endif + +enum { + OK = 0, // Everything's swell. + NO_ERROR = 0, // No errors. + + UNKNOWN_ERROR = 0x80000000, + + NO_MEMORY = -ENOMEM, + INVALID_OPERATION = -ENOSYS, + BAD_VALUE = -EINVAL, + BAD_TYPE = 0x80000001, + NAME_NOT_FOUND = -ENOENT, + PERMISSION_DENIED = -EPERM, + NO_INIT = -ENODEV, + ALREADY_EXISTS = -EEXIST, + DEAD_OBJECT = -EPIPE, + FAILED_TRANSACTION = 0x80000002, + JPARKS_BROKE_IT = -EPIPE, +#if !defined(HAVE_MS_C_RUNTIME) + BAD_INDEX = -EOVERFLOW, + NOT_ENOUGH_DATA = -ENODATA, + WOULD_BLOCK = -EWOULDBLOCK, + TIMED_OUT = -ETIME, + UNKNOWN_TRANSACTION = -EBADMSG, +#else + BAD_INDEX = -E2BIG, + NOT_ENOUGH_DATA = 0x80000003, + WOULD_BLOCK = 0x80000004, + TIMED_OUT = 0x80000005, + UNKNOWN_TRANSACTION = 0x80000006, +#endif +}; + +// Restore define; enumeration is in "android" namespace, so the value defined +// there won't work for Win32 code in a different namespace. +#ifdef _WIN32 +# define NO_ERROR 0L +#endif + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_ERRORS_H diff --git a/include/utils/FileMap.h b/include/utils/FileMap.h new file mode 100644 index 000000000..8dfd3bea6 --- /dev/null +++ b/include/utils/FileMap.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Encapsulate a shared file mapping. +// +#ifndef __LIBS_FILE_MAP_H +#define __LIBS_FILE_MAP_H + +#include + +#ifdef HAVE_WIN32_FILEMAP +#include +#endif + +namespace android { + +/* + * This represents a memory-mapped file. It might be the entire file or + * only part of it. This requires a little bookkeeping because the mapping + * needs to be aligned on page boundaries, and in some cases we'd like to + * have multiple references to the mapped area without creating additional + * maps. + * + * This always uses MAP_SHARED. + * + * TODO: we should be able to create a new FileMap that is a subset of + * an existing FileMap and shares the underlying mapped pages. Requires + * completing the refcounting stuff and possibly introducing the notion + * of a FileMap hierarchy. + */ +class FileMap { +public: + FileMap(void); + + /* + * Create a new mapping on an open file. + * + * Closing the file descriptor does not unmap the pages, so we don't + * claim ownership of the fd. + * + * Returns "false" on failure. + */ + bool create(const char* origFileName, int fd, + off_t offset, size_t length, bool readOnly); + + /* + * Return the name of the file this map came from, if known. + */ + const char* getFileName(void) const { return mFileName; } + + /* + * Get a pointer to the piece of the file we requested. + */ + void* getDataPtr(void) const { return mDataPtr; } + + /* + * Get the length we requested. + */ + size_t getDataLength(void) const { return mDataLength; } + + /* + * Get the data offset used to create this map. + */ + off_t getDataOffset(void) const { return mDataOffset; } + + /* + * Get a "copy" of the object. + */ + FileMap* acquire(void) { mRefCount++; return this; } + + /* + * Call this when mapping is no longer needed. + */ + void release(void) { + if (--mRefCount <= 0) + delete this; + } + + /* + * This maps directly to madvise() values, but allows us to avoid + * including everywhere. + */ + enum MapAdvice { + NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED + }; + + /* + * Apply an madvise() call to the entire file. + * + * Returns 0 on success, -1 on failure. + */ + int advise(MapAdvice advice); + +protected: + // don't delete objects; call release() + ~FileMap(void); + +private: + // these are not implemented + FileMap(const FileMap& src); + const FileMap& operator=(const FileMap& src); + + int mRefCount; // reference count + char* mFileName; // original file name, if known + void* mBasePtr; // base of mmap area; page aligned + size_t mBaseLength; // length, measured from "mBasePtr" + off_t mDataOffset; // offset used when map was created + void* mDataPtr; // start of requested data, offset from base + size_t mDataLength; // length, measured from "mDataPtr" +#ifdef HAVE_WIN32_FILEMAP + HANDLE mFileHandle; // Win32 file handle + HANDLE mFileMapping; // Win32 file mapping handle +#endif + + static long mPageSize; +}; + +}; // namespace android + +#endif // __LIBS_FILE_MAP_H diff --git a/include/utils/IBinder.h b/include/utils/IBinder.h new file mode 100644 index 000000000..737033090 --- /dev/null +++ b/include/utils/IBinder.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_IBINDER_H +#define ANDROID_IBINDER_H + +#include +#include +#include +#include + + +#define B_PACK_CHARS(c1, c2, c3, c4) \ + ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4)) + +// --------------------------------------------------------------------------- +namespace android { + +class BBinder; +class BpBinder; +class IInterface; +class Parcel; + +/** + * Base class and low-level protocol for a remotable object. + * You can derive from this class to create an object for which other + * processes can hold references to it. Communication between processes + * (method calls, property get and set) is down through a low-level + * protocol implemented on top of the transact() API. + */ +class IBinder : public virtual RefBase +{ +public: + enum { + FIRST_CALL_TRANSACTION = 0x00000001, + LAST_CALL_TRANSACTION = 0x00ffffff, + + PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), + DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'), + INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), + + // Corresponds to tfOneWay -- an asynchronous call. + FLAG_ONEWAY = 0x00000001 + }; + + inline IBinder() { } + + /** + * Check if this IBinder implements the interface named by + * @a descriptor. If it does, the base pointer to it is returned, + * which you can safely static_cast<> to the concrete C++ interface. + */ + virtual sp queryLocalInterface(const String16& descriptor); + + /** + * Return the canonical name of the interface provided by this IBinder + * object. + */ + virtual String16 getInterfaceDescriptor() const = 0; + + virtual bool isBinderAlive() const = 0; + virtual status_t pingBinder() = 0; + virtual status_t dump(int fd, const Vector& args) = 0; + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0) = 0; + + /** + * This method allows you to add data that is transported through + * IPC along with your IBinder pointer. When implementing a Binder + * object, override it to write your desired data in to @a outData. + * You can then call getConstantData() on your IBinder to retrieve + * that data, from any process. You MUST return the number of bytes + * written in to the parcel (including padding). + */ + class DeathRecipient : public virtual RefBase + { + public: + virtual void binderDied(const wp& who) = 0; + }; + + /** + * Register the @a recipient for a notification if this binder + * goes away. If this binder object unexpectedly goes away + * (typically because its hosting process has been killed), + * then DeathRecipient::binderDied() will be called with a referene + * to this. + * + * The @a cookie is optional -- if non-NULL, it should be a + * memory address that you own (that is, you know it is unique). + * + * @note You will only receive death notifications for remote binders, + * as local binders by definition can't die without you dying as well. + * Trying to use this function on a local binder will result in an + * INVALID_OPERATION code being returned and nothing happening. + * + * @note This link always holds a weak reference to its recipient. + * + * @note You will only receive a weak reference to the dead + * binder. You should not try to promote this to a strong reference. + * (Nor should you need to, as there is nothing useful you can + * directly do with it now that it has passed on.) + */ + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0) = 0; + + /** + * Remove a previously registered death notification. + * The @a recipient will no longer be called if this object + * dies. The @a cookie is optional. If non-NULL, you can + * supply a NULL @a recipient, and the recipient previously + * added with that cookie will be unlinked. + */ + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL) = 0; + + virtual bool checkSubclass(const void* subclassID) const; + + typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func) = 0; + virtual void* findObject(const void* objectID) const = 0; + virtual void detachObject(const void* objectID) = 0; + + virtual BBinder* localBinder(); + virtual BpBinder* remoteBinder(); + +protected: + inline virtual ~IBinder() { } + +private: +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_IBINDER_H diff --git a/include/utils/IInterface.h b/include/utils/IInterface.h new file mode 100644 index 000000000..959722a4d --- /dev/null +++ b/include/utils/IInterface.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005 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. + */ + +// +#ifndef ANDROID_IINTERFACE_H +#define ANDROID_IINTERFACE_H + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IInterface : public virtual RefBase +{ +public: + sp asBinder(); + sp asBinder() const; + +protected: + virtual IBinder* onAsBinder() = 0; +}; + +// ---------------------------------------------------------------------- + +template +inline sp interface_cast(const sp& obj) +{ + return INTERFACE::asInterface(obj); +} + +// ---------------------------------------------------------------------- + +template +class BnInterface : public INTERFACE, public BBinder +{ +public: + virtual sp queryLocalInterface(const String16& _descriptor); + virtual String16 getInterfaceDescriptor() const; + +protected: + virtual IBinder* onAsBinder(); +}; + +// ---------------------------------------------------------------------- + +template +class BpInterface : public INTERFACE, public BpRefBase +{ +public: + BpInterface(const sp& remote); + +protected: + virtual IBinder* onAsBinder(); +}; + +// ---------------------------------------------------------------------- + +#define DECLARE_META_INTERFACE(INTERFACE) \ + static const String16 descriptor; \ + static sp asInterface(const sp& obj); \ + virtual String16 getInterfaceDescriptor() const; \ + +#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + const String16 I##INTERFACE::descriptor(NAME); \ + String16 I##INTERFACE::getInterfaceDescriptor() const { \ + return I##INTERFACE::descriptor; \ + } \ + sp I##INTERFACE::asInterface(const sp& obj) \ + { \ + sp intr; \ + if (obj != NULL) { \ + intr = static_cast( \ + obj->queryLocalInterface( \ + I##INTERFACE::descriptor).get()); \ + if (intr == NULL) { \ + intr = new Bp##INTERFACE(obj); \ + } \ + } \ + return intr; \ + } \ + +// ---------------------------------------------------------------------- +// No user-servicable parts after this... + +template +inline sp BnInterface::queryLocalInterface( + const String16& _descriptor) +{ + if (_descriptor == INTERFACE::descriptor) return this; + return NULL; +} + +template +inline String16 BnInterface::getInterfaceDescriptor() const +{ + return INTERFACE::getInterfaceDescriptor(); +} + +template +IBinder* BnInterface::onAsBinder() +{ + return this; +} + +template +inline BpInterface::BpInterface(const sp& remote) + : BpRefBase(remote) +{ +} + +template +inline IBinder* BpInterface::onAsBinder() +{ + return remote(); +} + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IINTERFACE_H diff --git a/include/utils/IMemory.h b/include/utils/IMemory.h new file mode 100644 index 000000000..35a3fd7da --- /dev/null +++ b/include/utils/IMemory.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_IMEMORY_H +#define ANDROID_IMEMORY_H + +#include +#include +#include + +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +class IMemoryHeap : public IInterface +{ +public: + DECLARE_META_INTERFACE(MemoryHeap); + + // flags returned by getFlags() + enum { + READ_ONLY = 0x00000001, + MAP_ONCE = 0x00000002 + }; + + virtual int getHeapID() const = 0; + virtual void* getBase() const = 0; + virtual size_t getSize() const = 0; + virtual uint32_t getFlags() const = 0; + + // these are there just for backward source compatibility + int32_t heapID() const { return getHeapID(); } + void* base() const { return getBase(); } + size_t virtualSize() const { return getSize(); } +}; + +class BnMemoryHeap : public BnInterface +{ +public: + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +class IMemory : public IInterface +{ +public: + DECLARE_META_INTERFACE(Memory); + + virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const = 0; + + // helpers + void* fastPointer(const sp& heap, ssize_t offset) const; + void* pointer() const; + size_t size() const; + ssize_t offset() const; +}; + +class BnMemory : public BnInterface +{ +public: + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IMEMORY_H diff --git a/include/utils/IPCThreadState.h b/include/utils/IPCThreadState.h new file mode 100644 index 000000000..0490fd3ec --- /dev/null +++ b/include/utils/IPCThreadState.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_IPC_THREAD_STATE_H +#define ANDROID_IPC_THREAD_STATE_H + +#include +#include +#include +#include + +#ifdef HAVE_WIN32_PROC +typedef int uid_t; +#endif + +// --------------------------------------------------------------------------- +namespace android { + +class IPCThreadState +{ +public: + static IPCThreadState* self(); + + sp process(); + + status_t clearLastError(); + + int getCallingPid(); + int getCallingUid(); + + int64_t clearCallingIdentity(); + void restoreCallingIdentity(int64_t token); + + void flushCommands(); + + void joinThreadPool(bool isMain = true); + + // Stop the local process. + void stopProcess(bool immediate = true); + + status_t transact(int32_t handle, + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + void incStrongHandle(int32_t handle); + void decStrongHandle(int32_t handle); + void incWeakHandle(int32_t handle); + void decWeakHandle(int32_t handle); + status_t attemptIncStrongHandle(int32_t handle); + static void expungeHandle(int32_t handle, IBinder* binder); + status_t requestDeathNotification( int32_t handle, + BpBinder* proxy); + status_t clearDeathNotification( int32_t handle, + BpBinder* proxy); + + static void shutdown(); + +private: + IPCThreadState(); + ~IPCThreadState(); + + status_t sendReply(const Parcel& reply, uint32_t flags); + status_t waitForResponse(Parcel *reply, + status_t *acquireResult=NULL); + status_t talkWithDriver(bool doReceive=true); + status_t writeTransactionData(int32_t cmd, + uint32_t binderFlags, + int32_t handle, + uint32_t code, + const Parcel& data, + status_t* statusBuffer); + status_t executeCommand(int32_t command); + + void clearCaller(); + + static void threadDestructor(void *st); + static void freeBuffer(Parcel* parcel, + const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie); + + const sp mProcess; + Vector mPendingStrongDerefs; + Vector mPendingWeakDerefs; + + Parcel mIn; + Parcel mOut; + status_t mLastError; + pid_t mCallingPid; + uid_t mCallingUid; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_IPC_THREAD_STATE_H diff --git a/include/utils/IPermissionController.h b/include/utils/IPermissionController.h new file mode 100644 index 000000000..cb1dd345d --- /dev/null +++ b/include/utils/IPermissionController.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005 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. + */ + +// +#ifndef ANDROID_IPERMISSION_CONTROLLER_H +#define ANDROID_IPERMISSION_CONTROLLER_H + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IPermissionController : public IInterface +{ +public: + DECLARE_META_INTERFACE(PermissionController); + + virtual bool checkPermission(const String16& permission, + int32_t pid, int32_t uid) = 0; + + enum { + CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + }; +}; + +// ---------------------------------------------------------------------- + +class BnPermissionController : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IPERMISSION_CONTROLLER_H + diff --git a/include/utils/IServiceManager.h b/include/utils/IServiceManager.h new file mode 100644 index 000000000..e3d99fe7e --- /dev/null +++ b/include/utils/IServiceManager.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005 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. + */ + +// +#ifndef ANDROID_ISERVICE_MANAGER_H +#define ANDROID_ISERVICE_MANAGER_H + +#include +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IServiceManager : public IInterface +{ +public: + DECLARE_META_INTERFACE(ServiceManager); + + /** + * Retrieve an existing service, blocking for a few seconds + * if it doesn't yet exist. + */ + virtual sp getService( const String16& name) const = 0; + + /** + * Retrieve an existing service, non-blocking. + */ + virtual sp checkService( const String16& name) const = 0; + + /** + * Register a service. + */ + virtual status_t addService( const String16& name, + const sp& service) = 0; + + /** + * Return list of all existing services. + */ + virtual Vector listServices() = 0; + + enum { + GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + CHECK_SERVICE_TRANSACTION, + ADD_SERVICE_TRANSACTION, + LIST_SERVICES_TRANSACTION, + }; +}; + +sp defaultServiceManager(); + +template +status_t getService(const String16& name, sp* outService) +{ + const sp sm = defaultServiceManager(); + if (sm != NULL) { + *outService = interface_cast(sm->getService(name)); + if ((*outService) != NULL) return NO_ERROR; + } + return NAME_NOT_FOUND; +} + +bool checkCallingPermission(const String16& permission); +bool checkCallingPermission(const String16& permission, + int32_t* outPid, int32_t* outUid); + +// ---------------------------------------------------------------------- + +class BnServiceManager : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ISERVICE_MANAGER_H + diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h new file mode 100644 index 000000000..f4513ee20 --- /dev/null +++ b/include/utils/KeyedVector.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_KEYED_VECTOR_H +#define ANDROID_KEYED_VECTOR_H + +#include +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +template +class KeyedVector +{ +public: + typedef KEY key_type; + typedef VALUE value_type; + + inline KeyedVector(); + + /* + * empty the vector + */ + + inline void clear() { mVector.clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return mVector.size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return mVector.isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return mVector.capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } + + /*! + * accessors + */ + const VALUE& valueFor(const KEY& key) const; + const VALUE& valueAt(size_t index) const; + const KEY& keyAt(size_t index) const; + ssize_t indexOfKey(const KEY& key) const; + + /*! + * modifing the array + */ + + VALUE& editValueFor(const KEY& key); + VALUE& editValueAt(size_t index); + + /*! + * add/insert/replace items + */ + + ssize_t add(const KEY& key, const VALUE& item); + ssize_t replaceValueFor(const KEY& key, const VALUE& item); + ssize_t replaceValueAt(size_t index, const VALUE& item); + + /*! + * remove items + */ + + ssize_t removeItem(const KEY& key); + ssize_t removeItemsAt(size_t index, size_t count = 1); + +private: + SortedVector< key_value_pair_t > mVector; +}; + +// --------------------------------------------------------------------------- + +/** + * Variation of KeyedVector that holds a default value to return when + * valueFor() is called with a key that doesn't exist. + */ +template +class DefaultKeyedVector : public KeyedVector +{ +public: + inline DefaultKeyedVector(const VALUE& defValue = VALUE()); + const VALUE& valueFor(const KEY& key) const; + +private: + VALUE mDefault; +}; + +// --------------------------------------------------------------------------- + +template inline +KeyedVector::KeyedVector() +{ +} + +template inline +ssize_t KeyedVector::indexOfKey(const KEY& key) const { + return mVector.indexOf( key_value_pair_t(key) ); +} + +template inline +const VALUE& KeyedVector::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.itemAt(i).value; +} + +template inline +const VALUE& KeyedVector::valueAt(size_t index) const { + return mVector.itemAt(index).value; +} + +template inline +const KEY& KeyedVector::keyAt(size_t index) const { + return mVector.itemAt(index).key; +} + +template inline +VALUE& KeyedVector::editValueFor(const KEY& key) { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.editItemAt(i).value; +} + +template inline +VALUE& KeyedVector::editValueAt(size_t index) { + return mVector.editItemAt(index).value; +} + +template inline +ssize_t KeyedVector::add(const KEY& key, const VALUE& value) { + return mVector.add( key_value_pair_t(key, value) ); +} + +template inline +ssize_t KeyedVector::replaceValueFor(const KEY& key, const VALUE& value) { + key_value_pair_t pair(key, value); + mVector.remove(pair); + return mVector.add(pair); +} + +template inline +ssize_t KeyedVector::replaceValueAt(size_t index, const VALUE& item) { + if (index inline +ssize_t KeyedVector::removeItem(const KEY& key) { + return mVector.remove(key_value_pair_t(key)); +} + +template inline +ssize_t KeyedVector::removeItemsAt(size_t index, size_t count) { + return mVector.removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template inline +DefaultKeyedVector::DefaultKeyedVector(const VALUE& defValue) + : mDefault(defValue) +{ +} + +template inline +const VALUE& DefaultKeyedVector::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + return i >= 0 ? KeyedVector::valueAt(i) : mDefault; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_KEYED_VECTOR_H diff --git a/include/utils/List.h b/include/utils/List.h new file mode 100644 index 000000000..1a6be9ac9 --- /dev/null +++ b/include/utils/List.h @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Templated list class. Normally we'd use STL, but we don't have that. +// This class mimics STL's interfaces. +// +// Objects are copied into the list with the '=' operator or with copy- +// construction, so if the compiler's auto-generated versions won't work for +// you, define your own. +// +// The only class you want to use from here is "List". Do not use classes +// starting with "_" directly. +// +#ifndef _LIBS_UTILS_LIST_H +#define _LIBS_UTILS_LIST_H + +namespace android { + +/* + * One element in the list. + */ +template class _ListNode { +public: + typedef _ListNode _Node; + + _ListNode(const T& val) : mVal(val) {} + ~_ListNode(void) {} + + T& getRef(void) { return mVal; } + void setVal(const T& val) { mVal = val; } + + _Node* getPrev(void) const { return mpPrev; } + void setPrev(_Node* ptr) { mpPrev = ptr; } + _Node* getNext(void) const { return mpNext; } + void setNext(_Node* ptr) { mpNext = ptr; } + +private: + T mVal; + _Node* mpPrev; + _Node* mpNext; +}; + +/* + * Iterator for walking through the list. + */ +template class _ListIterator { +public: + typedef _ListIterator _Iter; + typedef _ListNode _Node; + + _ListIterator(void) {} + _ListIterator(_Node* ptr) : mpNode(ptr) {} + ~_ListIterator(void) {} + + /* + * Dereference operator. Used to get at the juicy insides. + */ + Tref operator*() const { return mpNode->getRef(); } + + /* + * Iterator comparison. + */ + bool operator==(const _Iter& right) const { return mpNode == right.mpNode; } + bool operator!=(const _Iter& right) const { return mpNode != right.mpNode; } + + /* + * Incr/decr, used to move through the list. + */ + _Iter& operator++(void) { // pre-increment + mpNode = mpNode->getNext(); + return *this; + } + _Iter operator++(int) { // post-increment + _Iter tmp = *this; + ++*this; + return tmp; + } + _Iter& operator--(void) { // pre-increment + mpNode = mpNode->getPrev(); + return *this; + } + _Iter operator--(int) { // post-increment + _Iter tmp = *this; + --*this; + return tmp; + } + + _Node* getNode(void) const { return mpNode; } + +private: + _Node* mpNode; +}; + + +/* + * Doubly-linked list. Instantiate with "List myList". + * + * Objects added to the list are copied using the assignment operator, + * so this must be defined. + */ +template class List { +public: + typedef _ListNode _Node; + + List(void) { + prep(); + } + List(const List& src) { // copy-constructor + prep(); + insert(begin(), src.begin(), src.end()); + } + virtual ~List(void) { + clear(); + delete[] (unsigned char*) mpMiddle; + } + + typedef _ListIterator iterator; + typedef _ListIterator const_iterator; + + List& operator=(const List& right); + + /* returns true if the list is empty */ + bool empty(void) const { return mpMiddle->getNext() == mpMiddle; } + + /* return #of elements in list */ + unsigned int size(void) const { + return distance(begin(), end()); + } + + /* + * Return the first element or one past the last element. The + * _ListNode* we're returning is converted to an "iterator" by a + * constructor in _ListIterator. + */ + iterator begin() { return mpMiddle->getNext(); } + const_iterator begin() const { return mpMiddle->getNext(); } + iterator end() { return mpMiddle; } + const_iterator end() const { return mpMiddle; } + + /* add the object to the head or tail of the list */ + void push_front(const T& val) { insert(begin(), val); } + void push_back(const T& val) { insert(end(), val); } + + /* insert before the current node; returns iterator at new node */ + iterator insert(iterator posn, const T& val) { + _Node* newNode = new _Node(val); // alloc & copy-construct + newNode->setNext(posn.getNode()); + newNode->setPrev(posn.getNode()->getPrev()); + posn.getNode()->getPrev()->setNext(newNode); + posn.getNode()->setPrev(newNode); + return newNode; + } + + /* insert a range of elements before the current node */ + void insert(iterator posn, const_iterator first, const_iterator last) { + for ( ; first != last; ++first) + insert(posn, *first); + } + + /* remove one entry; returns iterator at next node */ + iterator erase(iterator posn) { + _Node* pNext = posn.getNode()->getNext(); + _Node* pPrev = posn.getNode()->getPrev(); + pPrev->setNext(pNext); + pNext->setPrev(pPrev); + delete posn.getNode(); + return pNext; + } + + /* remove a range of elements */ + iterator erase(iterator first, iterator last) { + while (first != last) + erase(first++); // don't erase than incr later! + return last; + } + + /* remove all contents of the list */ + void clear(void) { + _Node* pCurrent = mpMiddle->getNext(); + _Node* pNext; + + while (pCurrent != mpMiddle) { + pNext = pCurrent->getNext(); + delete pCurrent; + pCurrent = pNext; + } + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * Measure the distance between two iterators. On exist, "first" + * will be equal to "last". The iterators must refer to the same + * list. + * + * (This is actually a generic iterator function. It should be part + * of some other class, possibly an iterator base class. It needs to + * know the difference between a list, which has to march through, + * and a vector, which can just do pointer math.) + */ + unsigned int distance(iterator first, iterator last) { + unsigned int count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + unsigned int distance(const_iterator first, const_iterator last) const { + unsigned int count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + +private: + /* + * I want a _ListNode but don't need it to hold valid data. More + * to the point, I don't want T's constructor to fire, since it + * might have side-effects or require arguments. So, we do this + * slightly uncouth storage alloc. + */ + void prep(void) { + mpMiddle = (_Node*) new unsigned char[sizeof(_Node)]; + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * This node plays the role of "pointer to head" and "pointer to tail". + * It sits in the middle of a circular list of nodes. The iterator + * runs around the circle until it encounters this one. + */ + _Node* mpMiddle; +}; + +/* + * Assignment operator. + * + * The simplest way to do this would be to clear out the target list and + * fill it with the source. However, we can speed things along by + * re-using existing elements. + */ +template +List& List::operator=(const List& right) +{ + if (this == &right) + return *this; // self-assignment + iterator firstDst = begin(); + iterator lastDst = end(); + const_iterator firstSrc = right.begin(); + const_iterator lastSrc = right.end(); + while (firstSrc != lastSrc && firstDst != lastDst) + *firstDst++ = *firstSrc++; + if (firstSrc == lastSrc) // ran out of elements in source? + erase(firstDst, lastDst); // yes, erase any extras + else + insert(lastDst, firstSrc, lastSrc); // copy remaining over + return *this; +} + +}; // namespace android + +#endif // _LIBS_UTILS_LIST_H diff --git a/include/utils/Log.h b/include/utils/Log.h new file mode 100644 index 000000000..3c6cc8bdc --- /dev/null +++ b/include/utils/Log.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// C/C++ logging functions. See the logging documentation for API details. +// +// We'd like these to be available from C code (in case we import some from +// somewhere), so this has a C interface. +// +// The output will be correct when the log file is shared between multiple +// threads and/or multiple processes so long as the operating system +// supports O_APPEND. These calls have mutex-protected data structures +// and so are NOT reentrant. Do not use LOG in a signal handler. +// +#ifndef _LIBS_UTILS_LOG_H +#define _LIBS_UTILS_LOG_H + +#include + +#endif // _LIBS_UTILS_LOG_H diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h new file mode 100644 index 000000000..01fbfb50e --- /dev/null +++ b/include/utils/LogSocket.h @@ -0,0 +1,20 @@ +/* utils/LogSocket.h +** +** Copyright 2008, The Android Open Source Project +** +** This file is dual licensed. It may be redistributed and/or modified +** under the terms of the Apache 2.0 License OR version 2 of the GNU +** General Public License. +*/ + +#ifndef _UTILS_LOGSOCKET_H +#define _UTILS_LOGSOCKET_H + +#define SOCKET_CLOSE_LOCAL 0 + +void add_send_stats(int fd, int send); +void add_recv_stats(int fd, int recv); +void log_socket_close(int fd, short reason); +void log_socket_connect(int fd, unsigned int ip, unsigned short port); + +#endif /* _UTILS_LOGSOCKET_H */ diff --git a/include/utils/MemoryBase.h b/include/utils/MemoryBase.h new file mode 100644 index 000000000..eb5a9d275 --- /dev/null +++ b/include/utils/MemoryBase.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_MEMORY_BASE_H +#define ANDROID_MEMORY_BASE_H + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryBase : public BnMemory +{ +public: + MemoryBase(const sp& heap, ssize_t offset, size_t size); + virtual ~MemoryBase(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; + +protected: + size_t getSize() const { return mSize; } + ssize_t getOffset() const { return mOffset; } + const sp& getHeap() const { return mHeap; } + +private: + size_t mSize; + ssize_t mOffset; + sp mHeap; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_BASE_H diff --git a/include/utils/MemoryDealer.h b/include/utils/MemoryDealer.h new file mode 100644 index 000000000..454b6270e --- /dev/null +++ b/include/utils/MemoryDealer.h @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_MEMORY_DEALER_H +#define ANDROID_MEMORY_DEALER_H + + +#include +#include + +#include +#include +#include + +namespace android { +// ---------------------------------------------------------------------------- +class String8; + +/* + * interface for implementing a "heap". A heap basically provides + * the IMemoryHeap interface for cross-process sharing and the + * ability to map/unmap pages within the heap. + */ +class HeapInterface : public virtual BnMemoryHeap +{ +public: + // all values must be page-aligned + virtual sp mapMemory(size_t offset, size_t size) = 0; +}; + +// ---------------------------------------------------------------------------- + +/* + * interface for implementing an allocator. An allocator provides + * methods for allocating and freeing memory blocks and dumping + * its state. + */ +class AllocatorInterface : public RefBase +{ +public: + enum { + PAGE_ALIGNED = 0x00000001 + }; + + virtual size_t allocate(size_t size, uint32_t flags = 0) = 0; + virtual status_t deallocate(size_t offset) = 0; + virtual size_t size() const = 0; + virtual void dump(const char* what, uint32_t flags = 0) const = 0; + virtual void dump(String8& res, + const char* what, uint32_t flags = 0) const = 0; +}; + +// ---------------------------------------------------------------------------- + +/* + * concrete implementation of HeapInterface on top of mmap() + */ +class SharedHeap : public HeapInterface, public MemoryHeapBase +{ +public: + SharedHeap(size_t size, uint32_t flags = 0, char const * name = NULL); + virtual ~SharedHeap(); + virtual sp mapMemory(size_t offset, size_t size); +}; + +// ---------------------------------------------------------------------------- + +/* + * A simple templatized doubly linked-list implementation + */ + +template +class LinkedList +{ + NODE* mFirst; + NODE* mLast; + +public: + LinkedList() : mFirst(0), mLast(0) { } + bool isEmpty() const { return mFirst == 0; } + NODE const* head() const { return mFirst; } + NODE* head() { return mFirst; } + NODE const* tail() const { return mLast; } + NODE* tail() { return mLast; } + + void insertAfter(NODE* node, NODE* newNode) { + newNode->prev = node; + newNode->next = node->next; + if (node->next == 0) mLast = newNode; + else node->next->prev = newNode; + node->next = newNode; + } + + void insertBefore(NODE* node, NODE* newNode) { + newNode->prev = node->prev; + newNode->next = node; + if (node->prev == 0) mFirst = newNode; + else node->prev->next = newNode; + node->prev = newNode; + } + + void insertHead(NODE* newNode) { + if (mFirst == 0) { + mFirst = mLast = newNode; + newNode->prev = newNode->next = 0; + } else { + insertBefore(mFirst, newNode); + } + } + + void insertTail(NODE* newNode) { + if (mLast == 0) insertBeginning(newNode); + else insertAfter(mLast, newNode); + } + + NODE* remove(NODE* node) { + if (node->prev == 0) mFirst = node->next; + else node->prev->next = node->next; + if (node->next == 0) mLast = node->prev; + else node->next->prev = node->prev; + return node; + } +}; + + +/* + * concrete implementation of AllocatorInterface using a simple + * best-fit allocation scheme + */ +class SimpleBestFitAllocator : public AllocatorInterface +{ +public: + + SimpleBestFitAllocator(size_t size); + virtual ~SimpleBestFitAllocator(); + + virtual size_t allocate(size_t size, uint32_t flags = 0); + virtual status_t deallocate(size_t offset); + virtual size_t size() const; + virtual void dump(const char* what, uint32_t flags = 0) const; + virtual void dump(String8& res, + const char* what, uint32_t flags = 0) const; + +private: + + struct chunk_t { + chunk_t(size_t start, size_t size) + : start(start), size(size), free(1), prev(0), next(0) { + } + size_t start; + size_t size : 28; + int free : 4; + mutable chunk_t* prev; + mutable chunk_t* next; + }; + + ssize_t alloc(size_t size, uint32_t flags); + chunk_t* dealloc(size_t start); + void dump_l(const char* what, uint32_t flags = 0) const; + void dump_l(String8& res, const char* what, uint32_t flags = 0) const; + + static const int kMemoryAlign; + mutable Mutex mLock; + LinkedList mList; + size_t mHeapSize; +}; + +// ---------------------------------------------------------------------------- + +class MemoryDealer : public RefBase +{ +public: + + enum { + READ_ONLY = MemoryHeapBase::READ_ONLY, + PAGE_ALIGNED = AllocatorInterface::PAGE_ALIGNED + }; + + // creates a memory dealer with the SharedHeap and SimpleBestFitAllocator + MemoryDealer(size_t size, uint32_t flags = 0, const char* name = 0); + + // provide a custom heap but use the SimpleBestFitAllocator + MemoryDealer(const sp& heap); + + // provide both custom heap and allocotar + MemoryDealer( + const sp& heap, + const sp& allocator); + + virtual ~MemoryDealer(); + + virtual sp allocate(size_t size, uint32_t flags = 0); + virtual void deallocate(size_t offset); + virtual void dump(const char* what, uint32_t flags = 0) const; + + + sp getMemoryHeap() const { return heap(); } + sp getAllocator() const { return allocator(); } + +private: + const sp& heap() const; + const sp& allocator() const; + + class Allocation : public BnMemory { + public: + Allocation(const sp& dealer, + ssize_t offset, size_t size, const sp& memory); + virtual ~Allocation(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; + private: + sp mDealer; + ssize_t mOffset; + size_t mSize; + sp mMemory; + }; + + sp mHeap; + sp mAllocator; +}; + + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_DEALER_H diff --git a/include/utils/MemoryHeapBase.h b/include/utils/MemoryHeapBase.h new file mode 100644 index 000000000..574acf4f9 --- /dev/null +++ b/include/utils/MemoryHeapBase.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_MEMORY_HEAP_BASE_H +#define ANDROID_MEMORY_HEAP_BASE_H + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryHeapBase : public virtual BnMemoryHeap +{ +public: + enum { + READ_ONLY = IMemoryHeap::READ_ONLY, + MAP_ONCE = IMemoryHeap::MAP_ONCE, + // memory won't be mapped locally, but will be mapped in the remote + // process. + DONT_MAP_LOCALLY = 0x00000100 + }; + + /* + * maps the memory referenced by fd. but DOESN'T take ownership + * of the filedescriptor (it makes a copy with dup() + */ + MemoryHeapBase(int fd, size_t size, uint32_t flags = 0); + + /* + * maps memory from the given device + */ + MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); + + /* + * maps memory from ashmem, with the given name for debugging + */ + MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL); + + virtual ~MemoryHeapBase(); + + /* implement IMemoryHeap interface */ + virtual int getHeapID() const; + virtual void* getBase() const; + virtual size_t getSize() const; + virtual uint32_t getFlags() const; + + const char* getDevice() const; + + /* this closes this heap -- use carefully */ + void dispose(); + + /* this is only needed as a workaround, use only if you know + * what you are doing */ + status_t setDevice(const char* device) { + if (mDevice == 0) + mDevice = device; + return mDevice ? NO_ERROR : ALREADY_EXISTS; + } + +protected: + MemoryHeapBase(); + // init() takes ownership of fd + status_t init(int fd, void *base, int size, + int flags = 0, const char* device = NULL); + +private: + status_t mapfd(int fd, size_t size); + + int mFD; + size_t mSize; + void* mBase; + uint32_t mFlags; + const char* mDevice; + bool mNeedUnmap; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_HEAP_BASE_H diff --git a/include/utils/MemoryHeapPmem.h b/include/utils/MemoryHeapPmem.h new file mode 100644 index 000000000..60335adae --- /dev/null +++ b/include/utils/MemoryHeapPmem.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_MEMORY_HEAP_PMEM_H +#define ANDROID_MEMORY_HEAP_PMEM_H + +#include +#include + +#include +#include +#include +#include + +namespace android { + +class MemoryHeapBase; + +// --------------------------------------------------------------------------- + +class MemoryHeapPmem : public HeapInterface, public MemoryHeapBase +{ +public: + class MemoryPmem : public BnMemory { + public: + MemoryPmem(const sp& heap); + ~MemoryPmem(); + protected: + const sp& getHeap() const { return mClientHeap; } + private: + friend class MemoryHeapPmem; + virtual void revoke() = 0; + sp mClientHeap; + }; + + MemoryHeapPmem(const sp& pmemHeap, + uint32_t flags = IMemoryHeap::MAP_ONCE); + ~MemoryHeapPmem(); + + /* HeapInterface additions */ + virtual sp mapMemory(size_t offset, size_t size); + + /* make the whole heap visible (you know who you are) */ + virtual status_t slap(); + + /* hide (revoke) the whole heap (the client will see the garbage page) */ + virtual status_t unslap(); + + /* revoke all allocations made by this heap */ + virtual void revoke(); + +private: + /* use this to create your own IMemory for mapMemory */ + virtual sp createMemory(size_t offset, size_t size); + void remove(const wp& memory); + +private: + sp mParentHeap; + mutable Mutex mLock; + SortedVector< wp > mAllocations; +}; + + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_HEAP_PMEM_H diff --git a/include/utils/Parcel.h b/include/utils/Parcel.h new file mode 100644 index 000000000..9087c4465 --- /dev/null +++ b/include/utils/Parcel.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_PARCEL_H +#define ANDROID_PARCEL_H + +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class IBinder; +class ProcessState; +class String8; +class TextOutput; + +struct flat_binder_object; // defined in support_p/binder_module.h + +class Parcel +{ +public: + Parcel(); + ~Parcel(); + + const uint8_t* data() const; + size_t dataSize() const; + size_t dataAvail() const; + size_t dataPosition() const; + size_t dataCapacity() const; + + status_t setDataSize(size_t size); + void setDataPosition(size_t pos) const; + status_t setDataCapacity(size_t size); + + status_t setData(const uint8_t* buffer, size_t len); + + status_t appendFrom(Parcel *parcel, size_t start, size_t len); + + bool hasFileDescriptors() const; + + status_t writeInterfaceToken(const String16& interface); + bool enforceInterface(const String16& interface) const; + + void freeData(); + + const size_t* objects() const; + size_t objectsCount() const; + + status_t errorCheck() const; + void setError(status_t err); + + status_t write(const void* data, size_t len); + void* writeInplace(size_t len); + status_t writeUnpadded(const void* data, size_t len); + status_t writeInt32(int32_t val); + status_t writeInt64(int64_t val); + status_t writeFloat(float val); + status_t writeDouble(double val); + status_t writeCString(const char* str); + status_t writeString8(const String8& str); + status_t writeString16(const String16& str); + status_t writeString16(const char16_t* str, size_t len); + status_t writeStrongBinder(const sp& val); + status_t writeWeakBinder(const wp& val); + + // doesn't take ownership of the native_handle + status_t writeNativeHandle(const native_handle& handle); + + // Place a file descriptor into the parcel. The given fd must remain + // valid for the lifetime of the parcel. + status_t writeFileDescriptor(int fd); + + // Place a file descriptor into the parcel. A dup of the fd is made, which + // will be closed once the parcel is destroyed. + status_t writeDupFileDescriptor(int fd); + + status_t writeObject(const flat_binder_object& val, bool nullMetaData); + + void remove(size_t start, size_t amt); + + status_t read(void* outData, size_t len) const; + const void* readInplace(size_t len) const; + int32_t readInt32() const; + status_t readInt32(int32_t *pArg) const; + int64_t readInt64() const; + status_t readInt64(int64_t *pArg) const; + float readFloat() const; + status_t readFloat(float *pArg) const; + double readDouble() const; + status_t readDouble(double *pArg) const; + + const char* readCString() const; + String8 readString8() const; + String16 readString16() const; + const char16_t* readString16Inplace(size_t* outLen) const; + sp readStrongBinder() const; + wp readWeakBinder() const; + + + // if alloc is NULL, native_handle is allocated with malloc(), otherwise + // alloc is used. If the function fails, the effects of alloc() must be + // reverted by the caller. + native_handle* readNativeHandle( + native_handle* (*alloc)(void* cookie, int numFds, int ints), + void* cookie) const; + + + // Retrieve a file descriptor from the parcel. This returns the raw fd + // in the parcel, which you do not own -- use dup() to get your own copy. + int readFileDescriptor() const; + + const flat_binder_object* readObject(bool nullMetaData) const; + + // Explicitly close all file descriptors in the parcel. + void closeFileDescriptors(); + + typedef void (*release_func)(Parcel* parcel, + const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie); + + const uint8_t* ipcData() const; + size_t ipcDataSize() const; + const size_t* ipcObjects() const; + size_t ipcObjectsCount() const; + void ipcSetDataReference(const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsCount, + release_func relFunc, void* relCookie); + + void print(TextOutput& to, uint32_t flags = 0) const; + +private: + Parcel(const Parcel& o); + Parcel& operator=(const Parcel& o); + + status_t finishWrite(size_t len); + void releaseObjects(); + void acquireObjects(); + status_t growData(size_t len); + status_t restartWrite(size_t desired); + status_t continueWrite(size_t desired); + void freeDataNoInit(); + void initState(); + void scanForFds() const; + + status_t mError; + uint8_t* mData; + size_t mDataSize; + size_t mDataCapacity; + mutable size_t mDataPos; + size_t* mObjects; + size_t mObjectsSize; + size_t mObjectsCapacity; + mutable size_t mNextObjectHint; + + mutable bool mFdsKnown; + mutable bool mHasFds; + + release_func mOwner; + void* mOwnerCookie; +}; + +// --------------------------------------------------------------------------- + +inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) +{ + parcel.print(to); + return to; +} + +// --------------------------------------------------------------------------- + +// Generic acquire and release of objects. +void acquire_object(const sp& proc, + const flat_binder_object& obj, const void* who); +void release_object(const sp& proc, + const flat_binder_object& obj, const void* who); + +void flatten_binder(const sp& proc, + const sp& binder, flat_binder_object* out); +void flatten_binder(const sp& proc, + const wp& binder, flat_binder_object* out); +status_t unflatten_binder(const sp& proc, + const flat_binder_object& flat, sp* out); +status_t unflatten_binder(const sp& proc, + const flat_binder_object& flat, wp* out); + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PARCEL_H diff --git a/include/utils/Pipe.h b/include/utils/Pipe.h new file mode 100644 index 000000000..6404168a2 --- /dev/null +++ b/include/utils/Pipe.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// FIFO I/O. +// +#ifndef _LIBS_UTILS_PIPE_H +#define _LIBS_UTILS_PIPE_H + +#ifdef HAVE_ANDROID_OS +#error DO NOT USE THIS FILE IN THE DEVICE BUILD +#endif + +namespace android { + +/* + * Simple anonymous unidirectional pipe. + * + * The primary goal is to create an implementation with minimal overhead + * under Linux. Making Windows, Mac OS X, and Linux all work the same way + * is a secondary goal. Part of this goal is to have something that can + * be fed to a select() call, so that the application can sleep in the + * kernel until something interesting happens. + */ +class Pipe { +public: + Pipe(void); + virtual ~Pipe(void); + + /* Create the pipe */ + bool create(void); + + /* Create a read-only pipe, using the supplied handle as read handle */ + bool createReader(unsigned long handle); + /* Create a write-only pipe, using the supplied handle as write handle */ + bool createWriter(unsigned long handle); + + /* Is this object ready to go? */ + bool isCreated(void); + + /* + * Read "count" bytes from the pipe. Returns the amount of data read, + * or 0 if no data available and we're non-blocking. + * Returns -1 on error. + */ + int read(void* buf, int count); + + /* + * Write "count" bytes into the pipe. Returns number of bytes written, + * or 0 if there's no room for more data and we're non-blocking. + * Returns -1 on error. + */ + int write(const void* buf, int count); + + /* Returns "true" if data is available to read */ + bool readReady(void); + + /* Enable or disable non-blocking I/O for reads */ + bool setReadNonBlocking(bool val); + /* Enable or disable non-blocking I/O for writes. Only works on Linux. */ + bool setWriteNonBlocking(bool val); + + /* + * Get the handle. Only useful in some platform-specific situations. + */ + unsigned long getReadHandle(void); + unsigned long getWriteHandle(void); + + /* + * Modify inheritance, i.e. whether or not a child process will get + * copies of the descriptors. Systems with fork+exec allow us to close + * the descriptors before launching the child process, but Win32 + * doesn't allow it. + */ + bool disallowReadInherit(void); + bool disallowWriteInherit(void); + + /* + * Close one side or the other. Useful in the parent after launching + * a child process. + */ + bool closeRead(void); + bool closeWrite(void); + +private: + bool mReadNonBlocking; + bool mWriteNonBlocking; + + unsigned long mReadHandle; + unsigned long mWriteHandle; +}; + +}; // android + +#endif // _LIBS_UTILS_PIPE_H diff --git a/include/utils/ProcessState.h b/include/utils/ProcessState.h new file mode 100644 index 000000000..39584f42c --- /dev/null +++ b/include/utils/ProcessState.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_PROCESS_STATE_H +#define ANDROID_PROCESS_STATE_H + +#include +#include +#include +#include + +#include + +// --------------------------------------------------------------------------- +namespace android { + +// Global variables +extern int mArgC; +extern const char* const* mArgV; +extern int mArgLen; + +class IPCThreadState; + +class ProcessState : public virtual RefBase +{ +public: + static sp self(); + + static void setSingleProcess(bool singleProcess); + + void setContextObject(const sp& object); + sp getContextObject(const sp& caller); + + void setContextObject(const sp& object, + const String16& name); + sp getContextObject(const String16& name, + const sp& caller); + + bool supportsProcesses() const; + + void startThreadPool(); + + typedef bool (*context_check_func)(const String16& name, + const sp& caller, + void* userData); + + bool isContextManager(void) const; + bool becomeContextManager( + context_check_func checkFunc, + void* userData); + + sp getStrongProxyForHandle(int32_t handle); + wp getWeakProxyForHandle(int32_t handle); + void expungeHandle(int32_t handle, IBinder* binder); + + void setArgs(int argc, const char* const argv[]); + int getArgC() const; + const char* const* getArgV() const; + + void setArgV0(const char* txt); + + void spawnPooledThread(bool isMain); + +private: + friend class IPCThreadState; + + ProcessState(); + ~ProcessState(); + + ProcessState(const ProcessState& o); + ProcessState& operator=(const ProcessState& o); + + struct handle_entry { + IBinder* binder; + RefBase::weakref_type* refs; + }; + + handle_entry* lookupHandleLocked(int32_t handle); + + int mDriverFD; + void* mVMStart; + + mutable Mutex mLock; // protects everything below. + + VectormHandleToObject; + + bool mManagesContexts; + context_check_func mBinderContextCheckFunc; + void* mBinderContextUserData; + + KeyedVector > + mContexts; + + + String8 mRootDir; + bool mThreadPoolStarted; + volatile int32_t mThreadPoolSeq; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PROCESS_STATE_H diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h new file mode 100644 index 000000000..cbda0fd80 --- /dev/null +++ b/include/utils/RefBase.h @@ -0,0 +1,550 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_REF_BASE_H +#define ANDROID_REF_BASE_H + +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +template class wp; + +// --------------------------------------------------------------------------- + +#define COMPARE(_op_) \ +inline bool operator _op_ (const sp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const T* o) const { \ + return m_ptr _op_ o; \ +} \ +template \ +inline bool operator _op_ (const sp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template \ +inline bool operator _op_ (const U* o) const { \ + return m_ptr _op_ o; \ +} + +// --------------------------------------------------------------------------- + +class RefBase +{ +public: + void incStrong(const void* id) const; + void decStrong(const void* id) const; + + void forceIncStrong(const void* id) const; + + //! DEBUGGING ONLY: Get current strong ref count. + int32_t getStrongCount() const; + + class weakref_type + { + public: + RefBase* refBase() const; + + void incWeak(const void* id); + void decWeak(const void* id); + + bool attemptIncStrong(const void* id); + + //! This is only safe if you have set OBJECT_LIFETIME_FOREVER. + bool attemptIncWeak(const void* id); + + //! DEBUGGING ONLY: Get current weak ref count. + int32_t getWeakCount() const; + + //! DEBUGGING ONLY: Print references held on object. + void printRefs() const; + + //! DEBUGGING ONLY: Enable tracking for this object. + // enable -- enable/disable tracking + // retain -- when tracking is enable, if true, then we save a stack trace + // for each reference and dereference; when retain == false, we + // match up references and dereferences and keep only the + // outstanding ones. + + void trackMe(bool enable, bool retain); + }; + + weakref_type* createWeak(const void* id) const; + + weakref_type* getWeakRefs() const; + + //! DEBUGGING ONLY: Print references held on object. + inline void printRefs() const { getWeakRefs()->printRefs(); } + + //! DEBUGGING ONLY: Enable tracking of object. + inline void trackMe(bool enable, bool retain) + { + getWeakRefs()->trackMe(enable, retain); + } + +protected: + RefBase(); + virtual ~RefBase(); + + //! Flags for extendObjectLifetime() + enum { + OBJECT_LIFETIME_WEAK = 0x0001, + OBJECT_LIFETIME_FOREVER = 0x0003 + }; + + void extendObjectLifetime(int32_t mode); + + //! Flags for onIncStrongAttempted() + enum { + FIRST_INC_STRONG = 0x0001 + }; + + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + virtual void onLastWeakRef(const void* id); + +private: + friend class weakref_type; + class weakref_impl; + + RefBase(const RefBase& o); + RefBase& operator=(const RefBase& o); + + weakref_impl* const mRefs; +}; + +// --------------------------------------------------------------------------- + +template +class LightRefBase +{ +public: + inline LightRefBase() : mCount(0) { } + inline void incStrong(const void* id) const { + android_atomic_inc(&mCount); + } + inline void decStrong(const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete static_cast(this); + } + } + +protected: + inline ~LightRefBase() { } + +private: + mutable volatile int32_t mCount; +}; + +// --------------------------------------------------------------------------- + +template +class sp +{ +public: + typedef typename RefBase::weakref_type weakref_type; + + inline sp() : m_ptr(0) { } + + sp(T* other); + sp(const sp& other); + template sp(U* other); + template sp(const sp& other); + + ~sp(); + + // Assignment + + sp& operator = (T* other); + sp& operator = (const sp& other); + + template sp& operator = (const sp& other); + template sp& operator = (U* other); + + //! Special optimization for use by ProcessState (and nobody else). + void force_set(T* other); + + // Reset + + void clear(); + + // Accessors + + inline T& operator* () const { return *m_ptr; } + inline T* operator-> () const { return m_ptr; } + inline T* get() const { return m_ptr; } + + // Operators + + COMPARE(==) + COMPARE(!=) + COMPARE(>) + COMPARE(<) + COMPARE(<=) + COMPARE(>=) + +private: + template friend class sp; + template friend class wp; + + // Optimization for wp::promote(). + sp(T* p, weakref_type* refs); + + T* m_ptr; +}; + +template +TextOutput& operator<<(TextOutput& to, const sp& val); + +// --------------------------------------------------------------------------- + +template +class wp +{ +public: + typedef typename RefBase::weakref_type weakref_type; + + inline wp() : m_ptr(0) { } + + wp(T* other); + wp(const wp& other); + wp(const sp& other); + template wp(U* other); + template wp(const sp& other); + template wp(const wp& other); + + ~wp(); + + // Assignment + + wp& operator = (T* other); + wp& operator = (const wp& other); + wp& operator = (const sp& other); + + template wp& operator = (U* other); + template wp& operator = (const wp& other); + template wp& operator = (const sp& other); + + void set_object_and_refs(T* other, weakref_type* refs); + + // promotion to sp + + sp promote() const; + + // Reset + + void clear(); + + // Accessors + + inline weakref_type* get_refs() const { return m_refs; } + + inline T* unsafe_get() const { return m_ptr; } + + // Operators + + COMPARE(==) + COMPARE(!=) + COMPARE(>) + COMPARE(<) + COMPARE(<=) + COMPARE(>=) + +private: + template friend class sp; + template friend class wp; + + T* m_ptr; + weakref_type* m_refs; +}; + +template +TextOutput& operator<<(TextOutput& to, const wp& val); + +#undef COMPARE + +// --------------------------------------------------------------------------- +// No user serviceable parts below here. + +template +sp::sp(T* other) + : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template +sp::sp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) m_ptr->incStrong(this); +} + +template template +sp::sp(U* other) : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template template +sp::sp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) m_ptr->incStrong(this); +} + +template +sp::~sp() +{ + if (m_ptr) m_ptr->decStrong(this); +} + +template +sp& sp::operator = (const sp& other) { + if (other.m_ptr) other.m_ptr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other.m_ptr; + return *this; +} + +template +sp& sp::operator = (T* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template template +sp& sp::operator = (const sp& other) +{ + if (other.m_ptr) other.m_ptr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other.m_ptr; + return *this; +} + +template template +sp& sp::operator = (U* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template +void sp::force_set(T* other) +{ + other->forceIncStrong(this); + m_ptr = other; +} + +template +void sp::clear() +{ + if (m_ptr) { + m_ptr->decStrong(this); + m_ptr = 0; + } +} + +template +sp::sp(T* p, weakref_type* refs) + : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0) +{ +} + +template +inline TextOutput& operator<<(TextOutput& to, const sp& val) +{ + to << "sp<>(" << val.get() << ")"; + return to; +} + +// --------------------------------------------------------------------------- + +template +wp::wp(T* other) + : m_ptr(other) +{ + if (other) m_refs = other->createWeak(this); +} + +template +wp::wp(const wp& other) + : m_ptr(other.m_ptr), m_refs(other.m_refs) +{ + if (m_ptr) m_refs->incWeak(this); +} + +template +wp::wp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = m_ptr->createWeak(this); + } +} + +template template +wp::wp(U* other) + : m_ptr(other) +{ + if (other) m_refs = other->createWeak(this); +} + +template template +wp::wp(const wp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = other.m_refs; + m_refs->incWeak(this); + } +} + +template template +wp::wp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = m_ptr->createWeak(this); + } +} + +template +wp::~wp() +{ + if (m_ptr) m_refs->decWeak(this); +} + +template +wp& wp::operator = (T* other) +{ + weakref_type* newRefs = + other ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = newRefs; + return *this; +} + +template +wp& wp::operator = (const wp& other) +{ + if (other.m_ptr) other.m_refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.m_ptr; + m_refs = other.m_refs; + return *this; +} + +template +wp& wp::operator = (const sp& other) +{ + weakref_type* newRefs = + other != NULL ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.get(); + m_refs = newRefs; + return *this; +} + +template template +wp& wp::operator = (U* other) +{ + weakref_type* newRefs = + other ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = newRefs; + return *this; +} + +template template +wp& wp::operator = (const wp& other) +{ + if (other.m_ptr) other.m_refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.m_ptr; + m_refs = other.m_refs; + return *this; +} + +template template +wp& wp::operator = (const sp& other) +{ + weakref_type* newRefs = + other != NULL ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.get(); + m_refs = newRefs; + return *this; +} + +template +void wp::set_object_and_refs(T* other, weakref_type* refs) +{ + if (other) refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = refs; +} + +template +sp wp::promote() const +{ + return sp(m_ptr, m_refs); +} + +template +void wp::clear() +{ + if (m_ptr) { + m_refs->decWeak(this); + m_ptr = 0; + } +} + +template +inline TextOutput& operator<<(TextOutput& to, const wp& val) +{ + to << "wp<>(" << val.unsafe_get() << ")"; + return to; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_REF_BASE_H diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h new file mode 100644 index 000000000..7d3fcf2a8 --- /dev/null +++ b/include/utils/ResourceTypes.h @@ -0,0 +1,1720 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Definitions of resource data structures. +// +#ifndef _LIBS_UTILS_RESOURCE_TYPES_H +#define _LIBS_UTILS_RESOURCE_TYPES_H + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace android { + +/** ******************************************************************** + * PNG Extensions + * + * New private chunks that may be placed in PNG images. + * + *********************************************************************** */ + +/** + * This chunk specifies how to split an image into segments for + * scaling. + * + * There are J horizontal and K vertical segments. These segments divide + * the image into J*K regions as follows (where J=4 and K=3): + * + * F0 S0 F1 S1 + * +-----+----+------+-------+ + * S2| 0 | 1 | 2 | 3 | + * +-----+----+------+-------+ + * | | | | | + * | | | | | + * F2| 4 | 5 | 6 | 7 | + * | | | | | + * | | | | | + * +-----+----+------+-------+ + * S3| 8 | 9 | 10 | 11 | + * +-----+----+------+-------+ + * + * Each horizontal and vertical segment is considered to by either + * stretchable (marked by the Sx labels) or fixed (marked by the Fy + * labels), in the horizontal or vertical axis, respectively. In the + * above example, the first is horizontal segment (F0) is fixed, the + * next is stretchable and then they continue to alternate. Note that + * the segment list for each axis can begin or end with a stretchable + * or fixed segment. + * + * The relative sizes of the stretchy segments indicates the relative + * amount of stretchiness of the regions bordered by the segments. For + * example, regions 3, 7 and 11 above will take up more horizontal space + * than regions 1, 5 and 9 since the horizonal segment associated with + * the first set of regions is larger than the other set of regions. The + * ratios of the amount of horizontal (or vertical) space taken by any + * two stretchable slices is exactly the ratio of their corresponding + * segment lengths. + * + * xDivs and yDivs point to arrays of horizontal and vertical pixel + * indices. The first pair of Divs (in either array) indicate the + * starting and ending points of the first stretchable segment in that + * axis. The next pair specifies the next stretchable segment, etc. So + * in the above example xDiv[0] and xDiv[1] specify the horizontal + * coordinates for the regions labeled 1, 5 and 9. xDiv[2] and + * xDiv[3] specify the coordinates for regions 3, 7 and 11. Note that + * the leftmost slices always start at x=0 and the rightmost slices + * always end at the end of the image. So, for example, the regions 0, + * 4 and 8 (which are fixed along the X axis) start at x value 0 and + * go to xDiv[0] amd slices 2, 6 and 10 start at xDiv[1] and end at + * xDiv[2]. + * + * The array pointed to by the colors field lists contains hints for + * each of the regions. They are ordered according left-to-right and + * top-to-bottom as indicated above. For each segment that is a solid + * color the array entry will contain that color value; otherwise it + * will contain NO_COLOR. Segments that are completely transparent + * will always have the value TRANSPARENT_COLOR. + * + * The PNG chunk type is "npTc". + */ +struct Res_png_9patch +{ + Res_png_9patch() : wasDeserialized(false), xDivs(NULL), + yDivs(NULL), colors(NULL) { } + + int8_t wasDeserialized; + int8_t numXDivs; + int8_t numYDivs; + int8_t numColors; + + // These tell where the next section of a patch starts. + // For example, the first patch includes the pixels from + // 0 to xDivs[0]-1 and the second patch includes the pixels + // from xDivs[0] to xDivs[1]-1. + // Note: allocation/free of these pointers is left to the caller. + int32_t* xDivs; + int32_t* yDivs; + + int32_t paddingLeft, paddingRight; + int32_t paddingTop, paddingBottom; + + enum { + // The 9 patch segment is not a solid color. + NO_COLOR = 0x00000001, + + // The 9 patch segment is completely transparent. + TRANSPARENT_COLOR = 0x00000000 + }; + // Note: allocation/free of this pointer is left to the caller. + uint32_t* colors; + + // Convert data from device representation to PNG file representation. + void deviceToFile(); + // Convert data from PNG file representation to device representation. + void fileToDevice(); + // Serialize/Marshall the patch data into a newly malloc-ed block + void* serialize(); + // Serialize/Marshall the patch data + void serialize(void* outData); + // Deserialize/Unmarshall the patch data + static Res_png_9patch* deserialize(const void* data); + // Compute the size of the serialized data structure + size_t serializedSize(); +}; + +/** ******************************************************************** + * Base Types + * + * These are standard types that are shared between multiple specific + * resource types. + * + *********************************************************************** */ + +/** + * Header that appears at the front of every data chunk in a resource. + */ +struct ResChunk_header +{ + // Type identifier for this chunk. The meaning of this value depends + // on the containing chunk. + uint16_t type; + + // Size of the chunk header (in bytes). Adding this value to + // the address of the chunk allows you to find its associated data + // (if any). + uint16_t headerSize; + + // Total size of this chunk (in bytes). This is the chunkSize plus + // the size of any data associated with the chunk. Adding this value + // to the chunk allows you to completely skip its contents (including + // any child chunks). If this value is the same as chunkSize, there is + // no data associated with the chunk. + uint32_t size; +}; + +enum { + RES_NULL_TYPE = 0x0000, + RES_STRING_POOL_TYPE = 0x0001, + RES_TABLE_TYPE = 0x0002, + RES_XML_TYPE = 0x0003, + + // Chunk types in RES_XML_TYPE + RES_XML_FIRST_CHUNK_TYPE = 0x0100, + RES_XML_START_NAMESPACE_TYPE= 0x0100, + RES_XML_END_NAMESPACE_TYPE = 0x0101, + RES_XML_START_ELEMENT_TYPE = 0x0102, + RES_XML_END_ELEMENT_TYPE = 0x0103, + RES_XML_CDATA_TYPE = 0x0104, + RES_XML_LAST_CHUNK_TYPE = 0x017f, + // This contains a uint32_t array mapping strings in the string + // pool back to resource identifiers. It is optional. + RES_XML_RESOURCE_MAP_TYPE = 0x0180, + + // Chunk types in RES_TABLE_TYPE + RES_TABLE_PACKAGE_TYPE = 0x0200, + RES_TABLE_TYPE_TYPE = 0x0201, + RES_TABLE_TYPE_SPEC_TYPE = 0x0202 +}; + +/** + * Macros for building/splitting resource identifiers. + */ +#define Res_VALIDID(resid) (resid != 0) +#define Res_CHECKID(resid) ((resid&0xFFFF0000) != 0) +#define Res_MAKEID(package, type, entry) \ + (((package+1)<<24) | (((type+1)&0xFF)<<16) | (entry&0xFFFF)) +#define Res_GETPACKAGE(id) ((id>>24)-1) +#define Res_GETTYPE(id) (((id>>16)&0xFF)-1) +#define Res_GETENTRY(id) (id&0xFFFF) + +#define Res_INTERNALID(resid) ((resid&0xFFFF0000) != 0 && (resid&0xFF0000) == 0) +#define Res_MAKEINTERNAL(entry) (0x01000000 | (entry&0xFFFF)) +#define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF)) + +#define Res_MAXPACKAGE 255 + +/** + * Representation of a value in a resource, supplying type + * information. + */ +struct Res_value +{ + // Number of bytes in this structure. + uint16_t size; + + // Always set to 0. + uint8_t res0; + + // Type of the data value. + enum { + // Contains no data. + TYPE_NULL = 0x00, + // The 'data' holds a ResTable_ref, a reference to another resource + // table entry. + TYPE_REFERENCE = 0x01, + // The 'data' holds an attribute resource identifier. + TYPE_ATTRIBUTE = 0x02, + // The 'data' holds an index into the containing resource table's + // global value string pool. + TYPE_STRING = 0x03, + // The 'data' holds a single-precision floating point number. + TYPE_FLOAT = 0x04, + // The 'data' holds a complex number encoding a dimension value, + // such as "100in". + TYPE_DIMENSION = 0x05, + // The 'data' holds a complex number encoding a fraction of a + // container. + TYPE_FRACTION = 0x06, + + // Beginning of integer flavors... + TYPE_FIRST_INT = 0x10, + + // The 'data' is a raw integer value of the form n..n. + TYPE_INT_DEC = 0x10, + // The 'data' is a raw integer value of the form 0xn..n. + TYPE_INT_HEX = 0x11, + // The 'data' is either 0 or 1, for input "false" or "true" respectively. + TYPE_INT_BOOLEAN = 0x12, + + // Beginning of color integer flavors... + TYPE_FIRST_COLOR_INT = 0x1c, + + // The 'data' is a raw integer value of the form #aarrggbb. + TYPE_INT_COLOR_ARGB8 = 0x1c, + // The 'data' is a raw integer value of the form #rrggbb. + TYPE_INT_COLOR_RGB8 = 0x1d, + // The 'data' is a raw integer value of the form #argb. + TYPE_INT_COLOR_ARGB4 = 0x1e, + // The 'data' is a raw integer value of the form #rgb. + TYPE_INT_COLOR_RGB4 = 0x1f, + + // ...end of integer flavors. + TYPE_LAST_COLOR_INT = 0x1f, + + // ...end of integer flavors. + TYPE_LAST_INT = 0x1f + }; + uint8_t dataType; + + // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION) + enum { + // Where the unit type information is. This gives us 16 possible + // types, as defined below. + COMPLEX_UNIT_SHIFT = 0, + COMPLEX_UNIT_MASK = 0xf, + + // TYPE_DIMENSION: Value is raw pixels. + COMPLEX_UNIT_PX = 0, + // TYPE_DIMENSION: Value is Device Independent Pixels. + COMPLEX_UNIT_DIP = 1, + // TYPE_DIMENSION: Value is a Scaled device independent Pixels. + COMPLEX_UNIT_SP = 2, + // TYPE_DIMENSION: Value is in points. + COMPLEX_UNIT_PT = 3, + // TYPE_DIMENSION: Value is in inches. + COMPLEX_UNIT_IN = 4, + // TYPE_DIMENSION: Value is in millimeters. + COMPLEX_UNIT_MM = 5, + + // TYPE_FRACTION: A basic fraction of the overall size. + COMPLEX_UNIT_FRACTION = 0, + // TYPE_FRACTION: A fraction of the parent size. + COMPLEX_UNIT_FRACTION_PARENT = 1, + + // Where the radix information is, telling where the decimal place + // appears in the mantissa. This give us 4 possible fixed point + // representations as defined below. + COMPLEX_RADIX_SHIFT = 4, + COMPLEX_RADIX_MASK = 0x3, + + // The mantissa is an integral number -- i.e., 0xnnnnnn.0 + COMPLEX_RADIX_23p0 = 0, + // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn + COMPLEX_RADIX_16p7 = 1, + // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn + COMPLEX_RADIX_8p15 = 2, + // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn + COMPLEX_RADIX_0p23 = 3, + + // Where the actual value is. This gives us 23 bits of + // precision. The top bit is the sign. + COMPLEX_MANTISSA_SHIFT = 8, + COMPLEX_MANTISSA_MASK = 0xffffff + }; + + // The data for this item, as interpreted according to dataType. + uint32_t data; + + void copyFrom_dtoh(const Res_value& src); +}; + +/** + * This is a reference to a unique entry (a ResTable_entry structure) + * in a resource table. The value is structured as: 0xpptteeee, + * where pp is the package index, tt is the type index in that + * package, and eeee is the entry index in that type. The package + * and type values start at 1 for the first item, to help catch cases + * where they have not been supplied. + */ +struct ResTable_ref +{ + uint32_t ident; +}; + +/** + * Reference to a string in a string pool. + */ +struct ResStringPool_ref +{ + // Index into the string pool table (uint32_t-offset from the indices + // immediately after ResStringPool_header) at which to find the location + // of the string data in the pool. + uint32_t index; +}; + +/** ******************************************************************** + * String Pool + * + * A set of strings that can be references by others through a + * ResStringPool_ref. + * + *********************************************************************** */ + +/** + * Definition for a pool of strings. The data of this chunk is an + * array of uint32_t providing indices into the pool, relative to + * stringsStart. At stringsStart are all of the UTF-16 strings + * concatenated together; each starts with a uint16_t of the string's + * length and each ends with a 0x0000 terminator. If a string is > + * 32767 characters, the high bit of the length is set meaning to take + * those 15 bits as a high word and it will be followed by another + * uint16_t containing the low word. + * + * If styleCount is not zero, then immediately following the array of + * uint32_t indices into the string table is another array of indices + * into a style table starting at stylesStart. Each entry in the + * style table is an array of ResStringPool_span structures. + */ +struct ResStringPool_header +{ + struct ResChunk_header header; + + // Number of strings in this pool (number of uint32_t indices that follow + // in the data). + uint32_t stringCount; + + // Number of style span arrays in the pool (number of uint32_t indices + // follow the string indices). + uint32_t styleCount; + + // Flags. + enum { + // If set, the string index is sorted by the string values (based + // on strcmp16()). + SORTED_FLAG = 1<<0 + }; + uint32_t flags; + + // Index from header of the string data. + uint32_t stringsStart; + + // Index from header of the style data. + uint32_t stylesStart; +}; + +/** + * This structure defines a span of style information associated with + * a string in the pool. + */ +struct ResStringPool_span +{ + enum { + END = 0xFFFFFFFF + }; + + // This is the name of the span -- that is, the name of the XML + // tag that defined it. The special value END (0xFFFFFFFF) indicates + // the end of an array of spans. + ResStringPool_ref name; + + // The range of characters in the string that this span applies to. + uint32_t firstChar, lastChar; +}; + +/** + * Convenience class for accessing data in a ResStringPool resource. + */ +class ResStringPool +{ +public: + ResStringPool(); + ResStringPool(const void* data, size_t size, bool copyData=false); + ~ResStringPool(); + + status_t setTo(const void* data, size_t size, bool copyData=false); + + status_t getError() const; + + void uninit(); + + inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { + return stringAt(ref.index, outLen); + } + const char16_t* stringAt(size_t idx, size_t* outLen) const; + + const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; + const ResStringPool_span* styleAt(size_t idx) const; + + ssize_t indexOfString(const char16_t* str, size_t strLen) const; + + size_t size() const; + +private: + status_t mError; + void* mOwnedData; + const ResStringPool_header* mHeader; + size_t mSize; + const uint32_t* mEntries; + const uint32_t* mEntryStyles; + const char16_t* mStrings; + uint32_t mStringPoolSize; // number of uint16_t + const uint32_t* mStyles; + uint32_t mStylePoolSize; // number of uint32_t +}; + +/** ******************************************************************** + * XML Tree + * + * Binary representation of an XML document. This is designed to + * express everything in an XML document, in a form that is much + * easier to parse on the device. + * + *********************************************************************** */ + +/** + * XML tree header. This appears at the front of an XML tree, + * describing its content. It is followed by a flat array of + * ResXMLTree_node structures; the hierarchy of the XML document + * is described by the occurrance of RES_XML_START_ELEMENT_TYPE + * and corresponding RES_XML_END_ELEMENT_TYPE nodes in the array. + */ +struct ResXMLTree_header +{ + struct ResChunk_header header; +}; + +/** + * Basic XML tree node. A single item in the XML document. Extended info + * about the node can be found after header.headerSize. + */ +struct ResXMLTree_node +{ + struct ResChunk_header header; + + // Line number in original source file at which this element appeared. + uint32_t lineNumber; + + // Optional XML comment that was associated with this element; -1 if none. + struct ResStringPool_ref comment; +}; + +/** + * Extended XML tree node for CDATA tags -- includes the CDATA string. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_cdataExt +{ + // The raw CDATA character data. + struct ResStringPool_ref data; + + // The typed value of the character data if this is a CDATA node. + struct Res_value typedData; +}; + +/** + * Extended XML tree node for namespace start/end nodes. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_namespaceExt +{ + // The prefix of the namespace. + struct ResStringPool_ref prefix; + + // The URI of the namespace. + struct ResStringPool_ref uri; +}; + +/** + * Extended XML tree node for element start/end nodes. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_endElementExt +{ + // String of the full namespace of this element. + struct ResStringPool_ref ns; + + // String name of this node if it is an ELEMENT; the raw + // character data if this is a CDATA node. + struct ResStringPool_ref name; +}; + +/** + * Extended XML tree node for start tags -- includes attribute + * information. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_attrExt +{ + // String of the full namespace of this element. + struct ResStringPool_ref ns; + + // String name of this node if it is an ELEMENT; the raw + // character data if this is a CDATA node. + struct ResStringPool_ref name; + + // Byte offset from the start of this structure where the attributes start. + uint16_t attributeStart; + + // Size of the ResXMLTree_attribute structures that follow. + uint16_t attributeSize; + + // Number of attributes associated with an ELEMENT. These are + // available as an array of ResXMLTree_attribute structures + // immediately following this node. + uint16_t attributeCount; + + // Index (1-based) of the "id" attribute. 0 if none. + uint16_t idIndex; + + // Index (1-based) of the "class" attribute. 0 if none. + uint16_t classIndex; + + // Index (1-based) of the "style" attribute. 0 if none. + uint16_t styleIndex; +}; + +struct ResXMLTree_attribute +{ + // Namespace of this attribute. + struct ResStringPool_ref ns; + + // Name of this attribute. + struct ResStringPool_ref name; + + // The original raw string value of this attribute. + struct ResStringPool_ref rawValue; + + // Processesd typed value of this attribute. + struct Res_value typedValue; +}; + +class ResXMLTree; + +class ResXMLParser +{ +public: + ResXMLParser(const ResXMLTree& tree); + + enum event_code_t { + BAD_DOCUMENT = -1, + START_DOCUMENT = 0, + END_DOCUMENT = 1, + + FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE, + + START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE, + END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE, + START_TAG = RES_XML_START_ELEMENT_TYPE, + END_TAG = RES_XML_END_ELEMENT_TYPE, + TEXT = RES_XML_CDATA_TYPE + }; + + struct ResXMLPosition + { + event_code_t eventCode; + const ResXMLTree_node* curNode; + const void* curExt; + }; + + void restart(); + + event_code_t getEventType() const; + // Note, unlike XmlPullParser, the first call to next() will return + // START_TAG of the first element. + event_code_t next(); + + // These are available for all nodes: + const int32_t getCommentID() const; + const uint16_t* getComment(size_t* outLen) const; + const uint32_t getLineNumber() const; + + // This is available for TEXT: + const int32_t getTextID() const; + const uint16_t* getText(size_t* outLen) const; + ssize_t getTextValue(Res_value* outValue) const; + + // These are available for START_NAMESPACE and END_NAMESPACE: + const int32_t getNamespacePrefixID() const; + const uint16_t* getNamespacePrefix(size_t* outLen) const; + const int32_t getNamespaceUriID() const; + const uint16_t* getNamespaceUri(size_t* outLen) const; + + // These are available for START_TAG and END_TAG: + const int32_t getElementNamespaceID() const; + const uint16_t* getElementNamespace(size_t* outLen) const; + const int32_t getElementNameID() const; + const uint16_t* getElementName(size_t* outLen) const; + + // Remaining methods are for retrieving information about attributes + // associated with a START_TAG: + + size_t getAttributeCount() const; + + // Returns -1 if no namespace, -2 if idx out of range. + const int32_t getAttributeNamespaceID(size_t idx) const; + const uint16_t* getAttributeNamespace(size_t idx, size_t* outLen) const; + + const int32_t getAttributeNameID(size_t idx) const; + const uint16_t* getAttributeName(size_t idx, size_t* outLen) const; + const uint32_t getAttributeNameResID(size_t idx) const; + + const int32_t getAttributeValueStringID(size_t idx) const; + const uint16_t* getAttributeStringValue(size_t idx, size_t* outLen) const; + + int32_t getAttributeDataType(size_t idx) const; + int32_t getAttributeData(size_t idx) const; + ssize_t getAttributeValue(size_t idx, Res_value* outValue) const; + + ssize_t indexOfAttribute(const char* ns, const char* attr) const; + ssize_t indexOfAttribute(const char16_t* ns, size_t nsLen, + const char16_t* attr, size_t attrLen) const; + + ssize_t indexOfID() const; + ssize_t indexOfClass() const; + ssize_t indexOfStyle() const; + + void getPosition(ResXMLPosition* pos) const; + void setPosition(const ResXMLPosition& pos); + +private: + friend class ResXMLTree; + + event_code_t nextNode(); + + const ResXMLTree& mTree; + event_code_t mEventCode; + const ResXMLTree_node* mCurNode; + const void* mCurExt; +}; + +/** + * Convenience class for accessing data in a ResXMLTree resource. + */ +class ResXMLTree : public ResXMLParser +{ +public: + ResXMLTree(); + ResXMLTree(const void* data, size_t size, bool copyData=false); + ~ResXMLTree(); + + status_t setTo(const void* data, size_t size, bool copyData=false); + + status_t getError() const; + + void uninit(); + + const ResStringPool& getStrings() const; + +private: + friend class ResXMLParser; + + status_t validateNode(const ResXMLTree_node* node) const; + + status_t mError; + void* mOwnedData; + const ResXMLTree_header* mHeader; + size_t mSize; + const uint8_t* mDataEnd; + ResStringPool mStrings; + const uint32_t* mResIds; + size_t mNumResIds; + const ResXMLTree_node* mRootNode; + const void* mRootExt; + event_code_t mRootCode; +}; + +/** ******************************************************************** + * RESOURCE TABLE + * + *********************************************************************** */ + +/** + * Header for a resource table. Its data contains a series of + * additional chunks: + * * A ResStringPool_header containing all table values. + * * One or more ResTable_package chunks. + * + * Specific entries within a resource table can be uniquely identified + * with a single integer as defined by the ResTable_ref structure. + */ +struct ResTable_header +{ + struct ResChunk_header header; + + // The number of ResTable_package structures. + uint32_t packageCount; +}; + +/** + * A collection of resource data types within a package. Followed by + * one or more ResTable_type and ResTable_typeSpec structures containing the + * entry values for each resource type. + */ +struct ResTable_package +{ + struct ResChunk_header header; + + // If this is a base package, its ID. Package IDs start + // at 1 (corresponding to the value of the package bits in a + // resource identifier). 0 means this is not a base package. + uint32_t id; + + // Actual name of this package, \0-terminated. + char16_t name[128]; + + // Offset to a ResStringPool_header defining the resource + // type symbol table. If zero, this package is inheriting from + // another base package (overriding specific values in it). + uint32_t typeStrings; + + // Last index into typeStrings that is for public use by others. + uint32_t lastPublicType; + + // Offset to a ResStringPool_header defining the resource + // key symbol table. If zero, this package is inheriting from + // another base package (overriding specific values in it). + uint32_t keyStrings; + + // Last index into keyStrings that is for public use by others. + uint32_t lastPublicKey; +}; + +/** + * Describes a particular resource configuration. + */ +struct ResTable_config +{ + // Number of bytes in this structure. + uint32_t size; + + union { + struct { + // Mobile country code (from SIM). 0 means "any". + uint16_t mcc; + // Mobile network code (from SIM). 0 means "any". + uint16_t mnc; + }; + uint32_t imsi; + }; + + union { + struct { + // \0\0 means "any". Otherwise, en, fr, etc. + char language[2]; + + // \0\0 means "any". Otherwise, US, CA, etc. + char country[2]; + }; + uint32_t locale; + }; + + enum { + ORIENTATION_ANY = 0x0000, + ORIENTATION_PORT = 0x0001, + ORIENTATION_LAND = 0x0002, + ORIENTATION_SQUARE = 0x0002, + }; + + enum { + TOUCHSCREEN_ANY = 0x0000, + TOUCHSCREEN_NOTOUCH = 0x0001, + TOUCHSCREEN_STYLUS = 0x0002, + TOUCHSCREEN_FINGER = 0x0003, + }; + + enum { + DENSITY_ANY = 0 + }; + + union { + struct { + uint8_t orientation; + uint8_t touchscreen; + uint16_t density; + }; + uint32_t screenType; + }; + + enum { + KEYBOARD_ANY = 0x0000, + KEYBOARD_NOKEYS = 0x0001, + KEYBOARD_QWERTY = 0x0002, + KEYBOARD_12KEY = 0x0003, + }; + + enum { + NAVIGATION_ANY = 0x0000, + NAVIGATION_NONAV = 0x0001, + NAVIGATION_DPAD = 0x0002, + NAVIGATION_TRACKBALL = 0x0003, + NAVIGATION_WHEEL = 0x0004, + }; + + enum { + MASK_KEYSHIDDEN = 0x0003, + SHIFT_KEYSHIDDEN = 0, + KEYSHIDDEN_ANY = 0x0000, + KEYSHIDDEN_NO = 0x0001, + KEYSHIDDEN_YES = 0x0002, + KEYSHIDDEN_SOFT = 0x0003, + }; + + union { + struct { + uint8_t keyboard; + uint8_t navigation; + uint8_t inputFlags; + uint8_t pad0; + }; + uint32_t input; + }; + + enum { + SCREENWIDTH_ANY = 0 + }; + + enum { + SCREENHEIGHT_ANY = 0 + }; + + union { + struct { + uint16_t screenWidth; + uint16_t screenHeight; + }; + uint32_t screenSize; + }; + + enum { + SDKVERSION_ANY = 0 + }; + + enum { + MINORVERSION_ANY = 0 + }; + + union { + struct { + uint16_t sdkVersion; + // For now minorVersion must always be 0!!! Its meaning + // is currently undefined. + uint16_t minorVersion; + }; + uint32_t version; + }; + + inline void copyFromDeviceNoSwap(const ResTable_config& o) { + const size_t size = dtohl(o.size); + if (size >= sizeof(ResTable_config)) { + *this = o; + } else { + memcpy(this, &o, size); + memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); + } + } + + inline void copyFromDtoH(const ResTable_config& o) { + copyFromDeviceNoSwap(o); + size = sizeof(ResTable_config); + mcc = dtohs(mcc); + mnc = dtohs(mnc); + density = dtohs(density); + screenWidth = dtohs(screenWidth); + screenHeight = dtohs(screenHeight); + sdkVersion = dtohs(sdkVersion); + minorVersion = dtohs(minorVersion); + } + + inline void swapHtoD() { + size = htodl(size); + mcc = htods(mcc); + mnc = htods(mnc); + density = htods(density); + screenWidth = htods(screenWidth); + screenHeight = htods(screenHeight); + sdkVersion = htods(sdkVersion); + minorVersion = htods(minorVersion); + } + + inline int compare(const ResTable_config& o) const { + int32_t diff = (int32_t)(imsi - o.imsi); + if (diff != 0) return diff; + diff = (int32_t)(locale - o.locale); + if (diff != 0) return diff; + diff = (int32_t)(screenType - o.screenType); + if (diff != 0) return diff; + diff = (int32_t)(input - o.input); + if (diff != 0) return diff; + diff = (int32_t)(screenSize - o.screenSize); + if (diff != 0) return diff; + diff = (int32_t)(version - o.version); + return (int)diff; + } + + // Flags indicating a set of config values. These flag constants must + // match the corresponding ones in android.content.pm.ActivityInfo and + // attrs_manifest.xml. + enum { + CONFIG_MCC = 0x0001, + CONFIG_MNC = 0x0002, + CONFIG_LOCALE = 0x0004, + CONFIG_TOUCHSCREEN = 0x0008, + CONFIG_KEYBOARD = 0x0010, + CONFIG_KEYBOARD_HIDDEN = 0x0020, + CONFIG_NAVIGATION = 0x0040, + CONFIG_ORIENTATION = 0x0080, + CONFIG_DENSITY = 0x0100, + CONFIG_SCREEN_SIZE = 0x0200, + CONFIG_VERSION = 0x0400 + }; + + // Compare two configuration, returning CONFIG_* flags set for each value + // that is different. + inline int diff(const ResTable_config& o) const { + int diffs = 0; + if (mcc != o.mcc) diffs |= CONFIG_MCC; + if (mnc != o.mnc) diffs |= CONFIG_MNC; + if (locale != o.locale) diffs |= CONFIG_LOCALE; + if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; + if (density != o.density) diffs |= CONFIG_DENSITY; + if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; + if (((inputFlags^o.inputFlags)&MASK_KEYSHIDDEN) != 0) diffs |= CONFIG_KEYBOARD_HIDDEN; + if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; + if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; + if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; + if (version != o.version) diffs |= CONFIG_VERSION; + return diffs; + } + + // Return true if 'this' is more specific than 'o'. Optionally, if + // 'requested' is null, then they will also be compared against the + // requested configuration and true will only be returned if 'this' + // is a better candidate than 'o' for the configuration. This assumes that + // match() has already been used to remove any configurations that don't + // match the requested configuration at all; if they are not first filtered, + // non-matching results can be considered better than matching ones. + inline bool + isBetterThan(const ResTable_config& o, const ResTable_config* requested = NULL) const { + // The order of the following tests defines the importance of one + // configuration parameter over another. Those tests first are more + // important, trumping any values in those following them. + if (imsi != 0 && (!requested || requested->imsi != 0)) { + if (mcc != 0 && (!requested || requested->mcc != 0)) { + if (o.mcc == 0) { + return true; + } + } + if (mnc != 0 && (!requested || requested->mnc != 0)) { + if (o.mnc == 0) { + return true; + } + } + } + if (locale != 0 && (!requested || requested->locale != 0)) { + if (language[0] != 0 && (!requested || requested->language[0] != 0)) { + if (o.language[0] == 0) { + return true; + } + } + if (country[0] != 0 && (!requested || requested->country[0] != 0)) { + if (o.country[0] == 0) { + return true; + } + } + } + if (screenType != 0 && (!requested || requested->screenType != 0)) { + if (orientation != 0 && (!requested || requested->orientation != 0)) { + if (o.orientation == 0) { + return true; + } + } + if (density != 0 && (!requested || requested->density != 0)) { + if (o.density == 0) { + return true; + } + } + if (touchscreen != 0 && (!requested || requested->touchscreen != 0)) { + if (o.touchscreen == 0) { + return true; + } + } + } + if (input != 0 && (!requested || requested->input != 0)) { + const int keysHidden = inputFlags&MASK_KEYSHIDDEN; + const int reqKeysHidden = requested + ? requested->inputFlags&MASK_KEYSHIDDEN : 0; + if (keysHidden != 0 && reqKeysHidden != 0) { + const int oKeysHidden = o.inputFlags&MASK_KEYSHIDDEN; + //LOGI("isBetterThan keysHidden: cur=%d, given=%d, config=%d\n", + // keysHidden, oKeysHidden, reqKeysHidden); + if (oKeysHidden == 0) { + //LOGI("Better because 0!"); + return true; + } + // For compatibility, we count KEYSHIDDEN_NO as being + // the same as KEYSHIDDEN_SOFT. Here we disambiguate these + // may making an exact match more specific. + if (keysHidden == reqKeysHidden && oKeysHidden != reqKeysHidden) { + // The current configuration is an exact match, and + // the given one is not, so the current one is better. + //LOGI("Better because other not same!"); + return true; + } + } + if (keyboard != 0 && (!requested || requested->keyboard != 0)) { + if (o.keyboard == 0) { + return true; + } + } + if (navigation != 0 && (!requested || requested->navigation != 0)) { + if (o.navigation == 0) { + return true; + } + } + } + if (screenSize != 0 && (!requested || requested->screenSize != 0)) { + if (screenWidth != 0 && (!requested || requested->screenWidth != 0)) { + if (o.screenWidth == 0) { + return true; + } + } + if (screenHeight != 0 && (!requested || requested->screenHeight != 0)) { + if (o.screenHeight == 0) { + return true; + } + } + } + if (version != 0 && (!requested || requested->version != 0)) { + if (sdkVersion != 0 && (!requested || requested->sdkVersion != 0)) { + if (o.sdkVersion == 0) { + return true; + } + } + if (minorVersion != 0 && (!requested || requested->minorVersion != 0)) { + if (o.minorVersion == 0) { + return true; + } + } + } + return false; + } + + // Return true if 'this' can be considered a match for the parameters in + // 'settings'. + // Note this is asymetric. A default piece of data will match every request + // but a request for the default should not match odd specifics + // (ie, request with no mcc should not match a particular mcc's data) + // settings is the requested settings + inline bool match(const ResTable_config& settings) const { + if (imsi != 0) { + if ((settings.mcc != 0 && mcc != 0 + && mcc != settings.mcc) || + (settings.mcc == 0 && mcc != 0)) { + return false; + } + if ((settings.mnc != 0 && mnc != 0 + && mnc != settings.mnc) || + (settings.mnc == 0 && mnc != 0)) { + return false; + } + } + if (locale != 0) { + if (settings.language[0] != 0 && language[0] != 0 + && (language[0] != settings.language[0] + || language[1] != settings.language[1])) { + return false; + } + if (settings.country[0] != 0 && country[0] != 0 + && (country[0] != settings.country[0] + || country[1] != settings.country[1])) { + return false; + } + } + if (screenType != 0) { + if (settings.orientation != 0 && orientation != 0 + && orientation != settings.orientation) { + return false; + } + // Density not taken into account, always match, no matter what + // density is specified for the resource + if (settings.touchscreen != 0 && touchscreen != 0 + && touchscreen != settings.touchscreen) { + return false; + } + } + if (input != 0) { + const int keysHidden = inputFlags&MASK_KEYSHIDDEN; + const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; + if (setKeysHidden != 0 && keysHidden != 0 + && keysHidden != setKeysHidden) { + // For compatibility, we count a request for KEYSHIDDEN_NO as also + // matching the more recent KEYSHIDDEN_SOFT. Basically + // KEYSHIDDEN_NO means there is some kind of keyboard available. + //LOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); + if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { + //LOGI("No match!"); + return false; + } + } + if (settings.keyboard != 0 && keyboard != 0 + && keyboard != settings.keyboard) { + return false; + } + if (settings.navigation != 0 && navigation != 0 + && navigation != settings.navigation) { + return false; + } + } + if (screenSize != 0) { + if (settings.screenWidth != 0 && screenWidth != 0 + && screenWidth != settings.screenWidth) { + return false; + } + if (settings.screenHeight != 0 && screenHeight != 0 + && screenHeight != settings.screenHeight) { + return false; + } + } + if (version != 0) { + if (settings.sdkVersion != 0 && sdkVersion != 0 + && sdkVersion != settings.sdkVersion) { + return false; + } + if (settings.minorVersion != 0 && minorVersion != 0 + && minorVersion != settings.minorVersion) { + return false; + } + } + return true; + } + + void getLocale(char str[6]) const { + memset(str, 0, 6); + if (language[0]) { + str[0] = language[0]; + str[1] = language[1]; + if (country[0]) { + str[2] = '_'; + str[3] = country[0]; + str[4] = country[1]; + } + } + } + + String8 toString() const { + char buf[200]; + sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=0x%02x touch=0x%02x dens=0x%02x " + "kbd=0x%02x nav=0x%02x input=0x%02x screenW=0x%04x screenH=0x%04x vers=%d.%d", + mcc, mnc, + language[0] ? language[0] : '-', language[1] ? language[1] : '-', + country[0] ? country[0] : '-', country[1] ? country[1] : '-', + orientation, touchscreen, density, keyboard, navigation, inputFlags, + screenWidth, screenHeight, sdkVersion, minorVersion); + return String8(buf); + } +}; + +/** + * A specification of the resources defined by a particular type. + * + * There should be one of these chunks for each resource type. + * + * This structure is followed by an array of integers providing the set of + * configuation change flags (ResTable_config::CONFIG_*) that have multiple + * resources for that configuration. In addition, the high bit is set if that + * resource has been made public. + */ +struct ResTable_typeSpec +{ + struct ResChunk_header header; + + // The type identifier this chunk is holding. Type IDs start + // at 1 (corresponding to the value of the type bits in a + // resource identifier). 0 is invalid. + uint8_t id; + + // Must be 0. + uint8_t res0; + // Must be 0. + uint16_t res1; + + // Number of uint32_t entry configuration masks that follow. + uint32_t entryCount; + + enum { + // Additional flag indicating an entry is public. + SPEC_PUBLIC = 0x40000000 + }; +}; + +/** + * A collection of resource entries for a particular resource data + * type. Followed by an array of uint32_t defining the resource + * values, corresponding to the array of type strings in the + * ResTable_package::typeStrings string block. Each of these hold an + * index from entriesStart; a value of NO_ENTRY means that entry is + * not defined. + * + * There may be multiple of these chunks for a particular resource type, + * supply different configuration variations for the resource values of + * that type. + * + * It would be nice to have an additional ordered index of entries, so + * we can do a binary search if trying to find a resource by string name. + */ +struct ResTable_type +{ + struct ResChunk_header header; + + enum { + NO_ENTRY = 0xFFFFFFFF + }; + + // The type identifier this chunk is holding. Type IDs start + // at 1 (corresponding to the value of the type bits in a + // resource identifier). 0 is invalid. + uint8_t id; + + // Must be 0. + uint8_t res0; + // Must be 0. + uint16_t res1; + + // Number of uint32_t entry indices that follow. + uint32_t entryCount; + + // Offset from header where ResTable_entry data starts. + uint32_t entriesStart; + + // Configuration this collection of entries is designed for. + ResTable_config config; +}; + +/** + * This is the beginning of information about an entry in the resource + * table. It holds the reference to the name of this entry, and is + * immediately followed by one of: + * * A Res_value structures, if FLAG_COMPLEX is -not- set. + * * An array of ResTable_map structures, if FLAG_COMPLEX is set. + * These supply a set of name/value mappings of data. + */ +struct ResTable_entry +{ + // Number of bytes in this structure. + uint16_t size; + + enum { + // If set, this is a complex entry, holding a set of name/value + // mappings. It is followed by an array of ResTable_map structures. + FLAG_COMPLEX = 0x0001, + // If set, this resource has been declared public, so libraries + // are allowed to reference it. + FLAG_PUBLIC = 0x0002 + }; + uint16_t flags; + + // Reference into ResTable_package::keyStrings identifying this entry. + struct ResStringPool_ref key; +}; + +/** + * Extended form of a ResTable_entry for map entries, defining a parent map + * resource from which to inherit values. + */ +struct ResTable_map_entry : public ResTable_entry +{ + // Resource identifier of the parent mapping, or 0 if there is none. + ResTable_ref parent; + // Number of name/value pairs that follow for FLAG_COMPLEX. + uint32_t count; +}; + +/** + * A single name/value mapping that is part of a complex resource + * entry. + */ +struct ResTable_map +{ + // The resource identifier defining this mapping's name. For attribute + // resources, 'name' can be one of the following special resource types + // to supply meta-data about the attribute; for all other resource types + // it must be an attribute resource. + ResTable_ref name; + + // Special values for 'name' when defining attribute resources. + enum { + // This entry holds the attribute's type code. + ATTR_TYPE = Res_MAKEINTERNAL(0), + + // For integral attributes, this is the minimum value it can hold. + ATTR_MIN = Res_MAKEINTERNAL(1), + + // For integral attributes, this is the maximum value it can hold. + ATTR_MAX = Res_MAKEINTERNAL(2), + + // Localization of this resource is can be encouraged or required with + // an aapt flag if this is set + ATTR_L10N = Res_MAKEINTERNAL(3), + + // for plural support, see android.content.res.PluralRules#attrForQuantity(int) + ATTR_OTHER = Res_MAKEINTERNAL(4), + ATTR_ZERO = Res_MAKEINTERNAL(5), + ATTR_ONE = Res_MAKEINTERNAL(6), + ATTR_TWO = Res_MAKEINTERNAL(7), + ATTR_FEW = Res_MAKEINTERNAL(8), + ATTR_MANY = Res_MAKEINTERNAL(9) + + }; + + // Bit mask of allowed types, for use with ATTR_TYPE. + enum { + // No type has been defined for this attribute, use generic + // type handling. The low 16 bits are for types that can be + // handled generically; the upper 16 require additional information + // in the bag so can not be handled generically for TYPE_ANY. + TYPE_ANY = 0x0000FFFF, + + // Attribute holds a references to another resource. + TYPE_REFERENCE = 1<<0, + + // Attribute holds a generic string. + TYPE_STRING = 1<<1, + + // Attribute holds an integer value. ATTR_MIN and ATTR_MIN can + // optionally specify a constrained range of possible integer values. + TYPE_INTEGER = 1<<2, + + // Attribute holds a boolean integer. + TYPE_BOOLEAN = 1<<3, + + // Attribute holds a color value. + TYPE_COLOR = 1<<4, + + // Attribute holds a floating point value. + TYPE_FLOAT = 1<<5, + + // Attribute holds a dimension value, such as "20px". + TYPE_DIMENSION = 1<<6, + + // Attribute holds a fraction value, such as "20%". + TYPE_FRACTION = 1<<7, + + // Attribute holds an enumeration. The enumeration values are + // supplied as additional entries in the map. + TYPE_ENUM = 1<<16, + + // Attribute holds a bitmaks of flags. The flag bit values are + // supplied as additional entries in the map. + TYPE_FLAGS = 1<<17 + }; + + // Enum of localization modes, for use with ATTR_L10N. + enum { + L10N_NOT_REQUIRED = 0, + L10N_SUGGESTED = 1 + }; + + // This mapping's value. + Res_value value; +}; + +/** + * Convenience class for accessing data in a ResTable resource. + */ +class ResTable +{ +public: + ResTable(); + ResTable(const void* data, size_t size, void* cookie, + bool copyData=false); + ~ResTable(); + + status_t add(const void* data, size_t size, void* cookie, + bool copyData=false); + status_t add(Asset* asset, void* cookie, + bool copyData=false); + + status_t getError() const; + + void uninit(); + + struct resource_name + { + const char16_t* package; + size_t packageLen; + const char16_t* type; + size_t typeLen; + const char16_t* name; + size_t nameLen; + }; + + bool getResourceName(uint32_t resID, resource_name* outName) const; + + /** + * Retrieve the value of a resource. If the resource is found, returns a + * value >= 0 indicating the table it is in (for use with + * getTableStringBlock() and getTableCookie()) and fills in 'outValue'. If + * not found, returns a negative error code. + * + * Note that this function does not do reference traversal. If you want + * to follow references to other resources to get the "real" value to + * use, you need to call resolveReference() after this function. + * + * @param resID The desired resoruce identifier. + * @param outValue Filled in with the resource data that was found. + * + * @return ssize_t Either a >= 0 table index or a negative error code. + */ + ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag=false, + uint32_t* outSpecFlags=NULL, ResTable_config* outConfig=NULL) const; + + inline ssize_t getResource(const ResTable_ref& res, Res_value* outValue, + uint32_t* outSpecFlags=NULL) const { + return getResource(res.ident, outValue, false, outSpecFlags, NULL); + } + + ssize_t resolveReference(Res_value* inOutValue, + ssize_t blockIndex, + uint32_t* outLastRef = NULL, + uint32_t* inoutTypeSpecFlags = NULL) const; + + enum { + TMP_BUFFER_SIZE = 16 + }; + const char16_t* valueToString(const Res_value* value, size_t stringBlock, + char16_t tmpBuffer[TMP_BUFFER_SIZE], + size_t* outLen); + + struct bag_entry { + ssize_t stringBlock; + ResTable_map map; + }; + + /** + * Retrieve the bag of a resource. If the resoruce is found, returns the + * number of bags it contains and 'outBag' points to an array of their + * values. If not found, a negative error code is returned. + * + * Note that this function -does- do reference traversal of the bag data. + * + * @param resID The desired resource identifier. + * @param outBag Filled inm with a pointer to the bag mappings. + * + * @return ssize_t Either a >= 0 bag count of negative error code. + */ + ssize_t lockBag(uint32_t resID, const bag_entry** outBag) const; + + void unlockBag(const bag_entry* bag) const; + + void lock() const; + + ssize_t getBagLocked(uint32_t resID, const bag_entry** outBag, + uint32_t* outTypeSpecFlags=NULL) const; + + void unlock() const; + + class Theme { + public: + Theme(const ResTable& table); + ~Theme(); + + inline const ResTable& getResTable() const { return mTable; } + + status_t applyStyle(uint32_t resID, bool force=false); + status_t setTo(const Theme& other); + + /** + * Retrieve a value in the theme. If the theme defines this + * value, returns a value >= 0 indicating the table it is in + * (for use with getTableStringBlock() and getTableCookie) and + * fills in 'outValue'. If not found, returns a negative error + * code. + * + * Note that this function does not do reference traversal. If you want + * to follow references to other resources to get the "real" value to + * use, you need to call resolveReference() after this function. + * + * @param resID A resource identifier naming the desired theme + * attribute. + * @param outValue Filled in with the theme value that was + * found. + * + * @return ssize_t Either a >= 0 table index or a negative error code. + */ + ssize_t getAttribute(uint32_t resID, Res_value* outValue, + uint32_t* outTypeSpecFlags = NULL) const; + + /** + * This is like ResTable::resolveReference(), but also takes + * care of resolving attribute references to the theme. + */ + ssize_t resolveAttributeReference(Res_value* inOutValue, + ssize_t blockIndex, uint32_t* outLastRef = NULL, + uint32_t* inoutTypeSpecFlags = NULL) const; + + void dumpToLog() const; + + private: + Theme(const Theme&); + Theme& operator=(const Theme&); + + struct theme_entry { + ssize_t stringBlock; + uint32_t typeSpecFlags; + Res_value value; + }; + struct type_info { + size_t numEntries; + theme_entry* entries; + }; + struct package_info { + size_t numTypes; + type_info types[]; + }; + + void free_package(package_info* pi); + package_info* copy_package(package_info* pi); + + const ResTable& mTable; + package_info* mPackages[Res_MAXPACKAGE]; + }; + + void setParameters(const ResTable_config* params); + void getParameters(ResTable_config* params) const; + + // Retrieve an identifier (which can be passed to getResource) + // for a given resource name. The 'name' can be fully qualified + // (:.) or the package or type components + // can be dropped if default values are supplied here. + // + // Returns 0 if no such resource was found, else a valid resource ID. + uint32_t identifierForName(const char16_t* name, size_t nameLen, + const char16_t* type = 0, size_t typeLen = 0, + const char16_t* defPackage = 0, + size_t defPackageLen = 0, + uint32_t* outTypeSpecFlags = NULL) const; + + static bool expandResourceRef(const uint16_t* refStr, size_t refLen, + String16* outPackage, + String16* outType, + String16* outName, + const String16* defType = NULL, + const String16* defPackage = NULL, + const char** outErrorMsg = NULL); + + static bool stringToInt(const char16_t* s, size_t len, Res_value* outValue); + static bool stringToFloat(const char16_t* s, size_t len, Res_value* outValue); + + // Used with stringToValue. + class Accessor + { + public: + inline virtual ~Accessor() { } + + virtual uint32_t getCustomResource(const String16& package, + const String16& type, + const String16& name) const = 0; + virtual uint32_t getCustomResourceWithCreation(const String16& package, + const String16& type, + const String16& name, + const bool createIfNeeded = false) = 0; + virtual uint32_t getRemappedPackage(uint32_t origPackage) const = 0; + virtual bool getAttributeType(uint32_t attrID, uint32_t* outType) = 0; + virtual bool getAttributeMin(uint32_t attrID, uint32_t* outMin) = 0; + virtual bool getAttributeMax(uint32_t attrID, uint32_t* outMax) = 0; + virtual bool getAttributeEnum(uint32_t attrID, + const char16_t* name, size_t nameLen, + Res_value* outValue) = 0; + virtual bool getAttributeFlags(uint32_t attrID, + const char16_t* name, size_t nameLen, + Res_value* outValue) = 0; + virtual uint32_t getAttributeL10N(uint32_t attrID) = 0; + virtual bool getLocalizationSetting() = 0; + virtual void reportError(void* accessorCookie, const char* fmt, ...) = 0; + }; + + // Convert a string to a resource value. Handles standard "@res", + // "#color", "123", and "0x1bd" types; performs escaping of strings. + // The resulting value is placed in 'outValue'; if it is a string type, + // 'outString' receives the string. If 'attrID' is supplied, the value is + // type checked against this attribute and it is used to perform enum + // evaluation. If 'acccessor' is supplied, it will be used to attempt to + // resolve resources that do not exist in this ResTable. If 'attrType' is + // supplied, the value will be type checked for this format if 'attrID' + // is not supplied or found. + bool stringToValue(Res_value* outValue, String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, bool coerceType, + uint32_t attrID = 0, + const String16* defType = NULL, + const String16* defPackage = NULL, + Accessor* accessor = NULL, + void* accessorCookie = NULL, + uint32_t attrType = ResTable_map::TYPE_ANY, + bool enforcePrivate = true) const; + + // Perform processing of escapes and quotes in a string. + static bool collectString(String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, + const char** outErrorMsg = NULL, + bool append = false); + + size_t getBasePackageCount() const; + const char16_t* getBasePackageName(size_t idx) const; + uint32_t getBasePackageId(size_t idx) const; + + size_t getTableCount() const; + const ResStringPool* getTableStringBlock(size_t index) const; + void* getTableCookie(size_t index) const; + + // Return the configurations (ResTable_config) that we know about + void getConfigurations(Vector* configs) const; + + void getLocales(Vector* locales) const; + +#ifndef HAVE_ANDROID_OS + void print() const; +#endif + +private: + struct Header; + struct Type; + struct Package; + struct PackageGroup; + struct bag_set; + + status_t add(const void* data, size_t size, void* cookie, + Asset* asset, bool copyData); + + ssize_t getResourcePackageIndex(uint32_t resID) const; + ssize_t getEntry( + const Package* package, int typeIndex, int entryIndex, + const ResTable_config* config, + const ResTable_type** outType, const ResTable_entry** outEntry, + const Type** outTypeClass) const; + status_t parsePackage( + const ResTable_package* const pkg, const Header* const header); + + mutable Mutex mLock; + + status_t mError; + + ResTable_config mParams; + + // Array of all resource tables. + Vector mHeaders; + + // Array of packages in all resource tables. + Vector mPackageGroups; + + // Mapping from resource package IDs to indices into the internal + // package array. + uint8_t mPackageMap[256]; +}; + +} // namespace android + +#endif // _LIBS_UTILS_RESOURCE_TYPES_H diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h new file mode 100644 index 000000000..24508b0f7 --- /dev/null +++ b/include/utils/SharedBuffer.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_SHARED_BUFFER_H +#define ANDROID_SHARED_BUFFER_H + +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class SharedBuffer +{ +public: + + /* flags to use with release() */ + enum { + eKeepStorage = 0x00000001 + }; + + /*! allocate a buffer of size 'size' and acquire() it. + * call release() to free it. + */ + static SharedBuffer* alloc(size_t size); + + /*! free the memory associated with the SharedBuffer. + * Fails if there are any users associated with this SharedBuffer. + * In other words, the buffer must have been release by all its + * users. + */ + static ssize_t dealloc(const SharedBuffer* released); + + //! get the SharedBuffer from the data pointer + static inline const SharedBuffer* sharedBuffer(const void* data); + + //! access the data for read + inline const void* data() const; + + //! access the data for read/write + inline void* data(); + + //! get size of the buffer + inline size_t size() const; + + //! get back a SharedBuffer object from its data + static inline SharedBuffer* bufferFromData(void* data); + + //! get back a SharedBuffer object from its data + static inline const SharedBuffer* bufferFromData(const void* data); + + //! get the size of a SharedBuffer object from its data + static inline size_t sizeFromData(const void* data); + + //! edit the buffer (get a writtable, or non-const, version of it) + SharedBuffer* edit() const; + + //! edit the buffer, resizing if needed + SharedBuffer* editResize(size_t size) const; + + //! like edit() but fails if a copy is required + SharedBuffer* attemptEdit() const; + + //! resize and edit the buffer, loose it's content. + SharedBuffer* reset(size_t size) const; + + //! acquire/release a reference on this buffer + void acquire() const; + + /*! release a reference on this buffer, with the option of not + * freeing the memory associated with it if it was the last reference + * returns the previous reference count + */ + int32_t release(uint32_t flags = 0) const; + + //! returns wether or not we're the only owner + inline bool onlyOwner() const; + + +private: + inline SharedBuffer() { } + inline ~SharedBuffer() { } + inline SharedBuffer(const SharedBuffer&); + + // 16 bytes. must be sized to preserve correct alingment. + mutable int32_t mRefs; + size_t mSize; + uint32_t mReserved[2]; +}; + +// --------------------------------------------------------------------------- + +const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) { + return data ? reinterpret_cast(data)-1 : 0; +} + +const void* SharedBuffer::data() const { + return this + 1; +} + +void* SharedBuffer::data() { + return this + 1; +} + +size_t SharedBuffer::size() const { + return mSize; +} + +SharedBuffer* SharedBuffer::bufferFromData(void* data) +{ + return ((SharedBuffer*)data)-1; +} + +const SharedBuffer* SharedBuffer::bufferFromData(const void* data) +{ + return ((const SharedBuffer*)data)-1; +} + +size_t SharedBuffer::sizeFromData(const void* data) +{ + return (((const SharedBuffer*)data)-1)->mSize; +} + +bool SharedBuffer::onlyOwner() const { + return (mRefs == 1); +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/include/utils/Socket.h b/include/utils/Socket.h new file mode 100644 index 000000000..8b7f40617 --- /dev/null +++ b/include/utils/Socket.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Socket class. Modeled after Java classes. +// +#ifndef _RUNTIME_SOCKET_H +#define _RUNTIME_SOCKET_H + +#include +#include + +namespace android { + +/* + * Basic socket class, needed to abstract away the differences between + * BSD sockets and WinSock. This establishes a streaming network + * connection (TCP/IP) to somebody. + */ +class Socket { +public: + Socket(void); + ~Socket(void); + + // Create a connection to somewhere. + // Return 0 on success. + int connect(const char* host, int port); + int connect(const InetAddress* addr, int port); + + + // Close the socket. Don't try to use this object again after + // calling this. Returns false on failure. + bool close(void); + + // If we created the socket without an address, we can use these + // to finish the connection. Returns 0 on success. + int bind(const SocketAddress& bindPoint); + int connect(const SocketAddress& endPoint); + + // Here we deviate from the traditional object-oriented fanciness + // and just provide read/write operators instead of getters for + // objects that abstract a stream. + // + // Standard read/write semantics. + int read(void* buf, ssize_t len) const; + int write(const void* buf, ssize_t len) const; + + // This must be called once, at program startup. + static bool bootInit(void); + static void finalShutdown(void); + +private: + // Internal function that establishes a connection. + int doConnect(const InetSocketAddress& addr); + + unsigned long mSock; // holds SOCKET or int + + static bool mBootInitialized; +}; + + +// debug -- unit tests +void TestSockets(void); + +}; // namespace android + +#endif // _RUNTIME_SOCKET_H diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h new file mode 100644 index 000000000..c8a61531f --- /dev/null +++ b/include/utils/SortedVector.h @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_SORTED_VECTOR_H +#define ANDROID_SORTED_VECTOR_H + +#include +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +template +class SortedVector : private SortedVectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + SortedVector(); + SortedVector(const SortedVector& rhs); + virtual ~SortedVector(); + + /*! copy operator */ + const SortedVector& operator = (const SortedVector& rhs) const; + SortedVector& operator = (const SortedVector& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + + //! read-write C-style access. BE VERY CAREFUL when modifying the array + //! you ust keep it sorted! You usually don't use this function. + TYPE* editArray(); + + //! finds the index of an item + ssize_t indexOf(const TYPE& item) const; + + //! finds where this item should be inserted + size_t orderOf(const TYPE& item) const; + + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! add an item in the right place (and replace the one that is there) + ssize_t add(const TYPE& item); + + //! editItemAt() MUST NOT change the order of this item + TYPE& editItemAt(size_t index) { + return *( static_cast(VectorImpl::editItemLocation(index)) ); + } + + //! merges a vector into this one + ssize_t merge(const Vector& vector); + ssize_t merge(const SortedVector& vector); + + //! removes an item + ssize_t remove(const TYPE&); + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; + virtual int do_compare(const void* lhs, const void* rhs) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template inline +SortedVector::SortedVector() + : SortedVectorImpl(sizeof(TYPE), + ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template inline +SortedVector::SortedVector(const SortedVector& rhs) + : SortedVectorImpl(rhs) { +} + +template inline +SortedVector::~SortedVector() { + finish_vector(); +} + +template inline +SortedVector& SortedVector::operator = (const SortedVector& rhs) { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template inline +const SortedVector& SortedVector::operator = (const SortedVector& rhs) const { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template inline +const TYPE* SortedVector::array() const { + return static_cast(arrayImpl()); +} + +template inline +TYPE* SortedVector::editArray() { + return static_cast(editArrayImpl()); +} + + +template inline +const TYPE& SortedVector::operator[](size_t index) const { + assert( index inline +const TYPE& SortedVector::itemAt(size_t index) const { + return operator[](index); +} + +template inline +const TYPE& SortedVector::mirrorItemAt(ssize_t index) const { + assert( (index>0 ? index : -index) inline +const TYPE& SortedVector::top() const { + return *(array() + size() - 1); +} + +template inline +ssize_t SortedVector::add(const TYPE& item) { + return SortedVectorImpl::add(&item); +} + +template inline +ssize_t SortedVector::indexOf(const TYPE& item) const { + return SortedVectorImpl::indexOf(&item); +} + +template inline +size_t SortedVector::orderOf(const TYPE& item) const { + return SortedVectorImpl::orderOf(&item); +} + +template inline +ssize_t SortedVector::merge(const Vector& vector) { + return SortedVectorImpl::merge(reinterpret_cast(vector)); +} + +template inline +ssize_t SortedVector::merge(const SortedVector& vector) { + return SortedVectorImpl::merge(reinterpret_cast(vector)); +} + +template inline +ssize_t SortedVector::remove(const TYPE& item) { + return SortedVectorImpl::remove(&item); +} + +template inline +ssize_t SortedVector::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template +void SortedVector::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast(storage), num ); +} + +template +void SortedVector::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast(storage), num ); +} + +template +void SortedVector::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void SortedVector::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast(dest), reinterpret_cast(item), num ); +} + +template +void SortedVector::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void SortedVector::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +int SortedVector::do_compare(const void* lhs, const void* rhs) const { + return compare_type( *reinterpret_cast(lhs), *reinterpret_cast(rhs) ); +} + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_SORTED_VECTOR_H diff --git a/include/utils/StopWatch.h b/include/utils/StopWatch.h new file mode 100644 index 000000000..cc0bebc40 --- /dev/null +++ b/include/utils/StopWatch.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_STOPWATCH_H +#define ANDROID_STOPWATCH_H + +#include +#include + +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class StopWatch +{ +public: + StopWatch( const char *name, + int clock = SYSTEM_TIME_MONOTONIC, + uint32_t flags = 0); + ~StopWatch(); + + const char* name() const; + nsecs_t lap(); + nsecs_t elapsedTime() const; + +private: + const char* mName; + int mClock; + uint32_t mFlags; + + struct lap_t { + nsecs_t soFar; + nsecs_t thisLap; + }; + + nsecs_t mStartTime; + lap_t mLaps[8]; + int mNumLaps; +}; + + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STOPWATCH_H diff --git a/include/utils/String16.h b/include/utils/String16.h new file mode 100644 index 000000000..a2d22eea9 --- /dev/null +++ b/include/utils/String16.h @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_STRING16_H +#define ANDROID_STRING16_H + +#include +#include + +#include +#include + +// --------------------------------------------------------------------------- + +extern "C" { + +typedef uint16_t char16_t; + +// Standard string functions on char16 strings. +int strcmp16(const char16_t *, const char16_t *); +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); +size_t strlen16(const char16_t *); +size_t strnlen16(const char16_t *, size_t); +char16_t *strcpy16(char16_t *, const char16_t *); +char16_t *strncpy16(char16_t *, const char16_t *, size_t); + +// Version of comparison that supports embedded nulls. +// This is different than strncmp() because we don't stop +// at a nul character and consider the strings to be different +// if the lengths are different (thus we need to supply the +// lengths of both strings). This can also be used when +// your string is not nul-terminated as it will have the +// equivalent result as strcmp16 (unlike strncmp16). +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); + +// Version of strzcmp16 for comparing strings in different endianness. +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); + +} + +// --------------------------------------------------------------------------- + +namespace android { + +class String8; +class TextOutput; + +//! This is a string holding UTF-16 characters. +class String16 +{ +public: + String16(); + String16(const String16& o); + String16(const String16& o, + size_t len, + size_t begin=0); + explicit String16(const char16_t* o); + explicit String16(const char16_t* o, size_t len); + explicit String16(const String8& o); + explicit String16(const char* o); + explicit String16(const char* o, size_t len); + + ~String16(); + + inline const char16_t* string() const; + inline size_t size() const; + + inline const SharedBuffer* sharedBuffer() const; + + void setTo(const String16& other); + status_t setTo(const char16_t* other); + status_t setTo(const char16_t* other, size_t len); + status_t setTo(const String16& other, + size_t len, + size_t begin=0); + + status_t append(const String16& other); + status_t append(const char16_t* other, size_t len); + + inline String16& operator=(const String16& other); + + inline String16& operator+=(const String16& other); + inline String16 operator+(const String16& other) const; + + status_t insert(size_t pos, const char16_t* chrs); + status_t insert(size_t pos, + const char16_t* chrs, size_t len); + + ssize_t findFirst(char16_t c) const; + ssize_t findLast(char16_t c) const; + + bool startsWith(const String16& prefix) const; + bool startsWith(const char16_t* prefix) const; + + status_t makeLower(); + + status_t replaceAll(char16_t replaceThis, + char16_t withThis); + + status_t remove(size_t len, size_t begin=0); + + inline int compare(const String16& other) const; + + inline bool operator<(const String16& other) const; + inline bool operator<=(const String16& other) const; + inline bool operator==(const String16& other) const; + inline bool operator!=(const String16& other) const; + inline bool operator>=(const String16& other) const; + inline bool operator>(const String16& other) const; + + inline bool operator<(const char16_t* other) const; + inline bool operator<=(const char16_t* other) const; + inline bool operator==(const char16_t* other) const; + inline bool operator!=(const char16_t* other) const; + inline bool operator>=(const char16_t* other) const; + inline bool operator>(const char16_t* other) const; + + inline operator const char16_t*() const; + +private: + const char16_t* mString; +}; + +TextOutput& operator<<(TextOutput& to, const String16& val); + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline int compare_type(const String16& lhs, const String16& rhs) +{ + return lhs.compare(rhs); +} + +inline int strictly_order_type(const String16& lhs, const String16& rhs) +{ + return compare_type(lhs, rhs) < 0; +} + +inline const char16_t* String16::string() const +{ + return mString; +} + +inline size_t String16::size() const +{ + return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1; +} + +inline const SharedBuffer* String16::sharedBuffer() const +{ + return SharedBuffer::bufferFromData(mString); +} + +inline String16& String16::operator=(const String16& other) +{ + setTo(other); + return *this; +} + +inline String16& String16::operator+=(const String16& other) +{ + append(other); + return *this; +} + +inline String16 String16::operator+(const String16& other) const +{ + String16 tmp; + tmp += other; + return tmp; +} + +inline int String16::compare(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()); +} + +inline bool String16::operator<(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) < 0; +} + +inline bool String16::operator<=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) <= 0; +} + +inline bool String16::operator==(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) == 0; +} + +inline bool String16::operator!=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) != 0; +} + +inline bool String16::operator>=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) >= 0; +} + +inline bool String16::operator>(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) > 0; +} + +inline bool String16::operator<(const char16_t* other) const +{ + return strcmp16(mString, other) < 0; +} + +inline bool String16::operator<=(const char16_t* other) const +{ + return strcmp16(mString, other) <= 0; +} + +inline bool String16::operator==(const char16_t* other) const +{ + return strcmp16(mString, other) == 0; +} + +inline bool String16::operator!=(const char16_t* other) const +{ + return strcmp16(mString, other) != 0; +} + +inline bool String16::operator>=(const char16_t* other) const +{ + return strcmp16(mString, other) >= 0; +} + +inline bool String16::operator>(const char16_t* other) const +{ + return strcmp16(mString, other) > 0; +} + +inline String16::operator const char16_t*() const +{ + return mString; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STRING16_H diff --git a/include/utils/String8.h b/include/utils/String8.h new file mode 100644 index 000000000..c49faf6fe --- /dev/null +++ b/include/utils/String8.h @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_STRING8_H +#define ANDROID_STRING8_H + +#include + +// Need this for the char16_t type; String8.h should not +// be depedent on the String16 class. +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class TextOutput; + +//! This is a string holding UTF-8 characters. +class String8 +{ +public: + String8(); + String8(const String8& o); + explicit String8(const char* o); + explicit String8(const char* o, size_t numChars); + + explicit String8(const String16& o); + explicit String8(const char16_t* o); + explicit String8(const char16_t* o, size_t numChars); + + ~String8(); + + inline const char* string() const; + inline size_t size() const; + inline size_t length() const; + inline size_t bytes() const; + + inline const SharedBuffer* sharedBuffer() const; + + void setTo(const String8& other); + status_t setTo(const char* other); + status_t setTo(const char* other, size_t numChars); + status_t setTo(const char16_t* other, size_t numChars); + + status_t append(const String8& other); + status_t append(const char* other); + status_t append(const char* other, size_t numChars); + + inline String8& operator=(const String8& other); + inline String8& operator=(const char* other); + + inline String8& operator+=(const String8& other); + inline String8 operator+(const String8& other) const; + + inline String8& operator+=(const char* other); + inline String8 operator+(const char* other) const; + + inline int compare(const String8& other) const; + + inline bool operator<(const String8& other) const; + inline bool operator<=(const String8& other) const; + inline bool operator==(const String8& other) const; + inline bool operator!=(const String8& other) const; + inline bool operator>=(const String8& other) const; + inline bool operator>(const String8& other) const; + + inline bool operator<(const char* other) const; + inline bool operator<=(const char* other) const; + inline bool operator==(const char* other) const; + inline bool operator!=(const char* other) const; + inline bool operator>=(const char* other) const; + inline bool operator>(const char* other) const; + + inline operator const char*() const; + + char* lockBuffer(size_t size); + void unlockBuffer(); + status_t unlockBuffer(size_t size); + + // return the index of the first byte of other in this at or after + // start, or -1 if not found + ssize_t find(const char* other, size_t start = 0) const; + + void toLower(); + void toLower(size_t start, size_t numChars); + void toUpper(); + void toUpper(size_t start, size_t numChars); + + /* + * These methods operate on the string as if it were a path name. + */ + + /* + * Set the filename field to a specific value. + * + * Normalizes the filename, removing a trailing '/' if present. + */ + void setPathName(const char* name); + void setPathName(const char* name, size_t numChars); + + /* + * Get just the filename component. + * + * "/tmp/foo/bar.c" --> "bar.c" + */ + String8 getPathLeaf(void) const; + + /* + * Remove the last (file name) component, leaving just the directory + * name. + * + * "/tmp/foo/bar.c" --> "/tmp/foo" + * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX + * "bar.c" --> "" + */ + String8 getPathDir(void) const; + + /* + * Retrieve the front (root dir) component. Optionally also return the + * remaining components. + * + * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c") + * "/tmp" --> "tmp" (remain = "") + * "bar.c" --> "bar.c" (remain = "") + */ + String8 walkPath(String8* outRemains = NULL) const; + + /* + * Return the filename extension. This is the last '.' and up to + * four characters that follow it. The '.' is included in case we + * decide to expand our definition of what constitutes an extension. + * + * "/tmp/foo/bar.c" --> ".c" + * "/tmp" --> "" + * "/tmp/foo.bar/baz" --> "" + * "foo.jpeg" --> ".jpeg" + * "foo." --> "" + */ + String8 getPathExtension(void) const; + + /* + * Return the path without the extension. Rules for what constitutes + * an extension are described in the comment for getPathExtension(). + * + * "/tmp/foo/bar.c" --> "/tmp/foo/bar" + */ + String8 getBasePath(void) const; + + /* + * Add a component to the pathname. We guarantee that there is + * exactly one path separator between the old path and the new. + * If there is no existing name, we just copy the new name in. + * + * If leaf is a fully qualified path (i.e. starts with '/', it + * replaces whatever was there before. + */ + String8& appendPath(const char* leaf); + String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); } + + /* + * Like appendPath(), but does not affect this string. Returns a new one instead. + */ + String8 appendPathCopy(const char* leaf) const + { String8 p(*this); p.appendPath(leaf); return p; } + String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); } + + /* + * Converts all separators in this string to /, the default path separator. + * + * If the default OS separator is backslash, this converts all + * backslashes to slashes, in-place. Otherwise it does nothing. + * Returns self. + */ + String8& convertToResPath(); + +private: + status_t real_append(const char* other, size_t numChars); + char* find_extension(void) const; + + const char* mString; +}; + +TextOutput& operator<<(TextOutput& to, const String16& val); + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline int compare_type(const String8& lhs, const String8& rhs) +{ + return lhs.compare(rhs); +} + +inline int strictly_order_type(const String8& lhs, const String8& rhs) +{ + return compare_type(lhs, rhs) < 0; +} + +inline const char* String8::string() const +{ + return mString; +} + +inline size_t String8::length() const +{ + return SharedBuffer::sizeFromData(mString)-1; +} + +inline size_t String8::size() const +{ + return length(); +} + +inline size_t String8::bytes() const +{ + return SharedBuffer::sizeFromData(mString)-1; +} + +inline const SharedBuffer* String8::sharedBuffer() const +{ + return SharedBuffer::bufferFromData(mString); +} + +inline String8& String8::operator=(const String8& other) +{ + setTo(other); + return *this; +} + +inline String8& String8::operator=(const char* other) +{ + setTo(other); + return *this; +} + +inline String8& String8::operator+=(const String8& other) +{ + append(other); + return *this; +} + +inline String8 String8::operator+(const String8& other) const +{ + String8 tmp; + tmp += other; + return tmp; +} + +inline String8& String8::operator+=(const char* other) +{ + append(other); + return *this; +} + +inline String8 String8::operator+(const char* other) const +{ + String8 tmp; + tmp += other; + return tmp; +} + +inline int String8::compare(const String8& other) const +{ + return strcmp(mString, other.mString); +} + +inline bool String8::operator<(const String8& other) const +{ + return strcmp(mString, other.mString) < 0; +} + +inline bool String8::operator<=(const String8& other) const +{ + return strcmp(mString, other.mString) <= 0; +} + +inline bool String8::operator==(const String8& other) const +{ + return strcmp(mString, other.mString) == 0; +} + +inline bool String8::operator!=(const String8& other) const +{ + return strcmp(mString, other.mString) != 0; +} + +inline bool String8::operator>=(const String8& other) const +{ + return strcmp(mString, other.mString) >= 0; +} + +inline bool String8::operator>(const String8& other) const +{ + return strcmp(mString, other.mString) > 0; +} + +inline bool String8::operator<(const char* other) const +{ + return strcmp(mString, other) < 0; +} + +inline bool String8::operator<=(const char* other) const +{ + return strcmp(mString, other) <= 0; +} + +inline bool String8::operator==(const char* other) const +{ + return strcmp(mString, other) == 0; +} + +inline bool String8::operator!=(const char* other) const +{ + return strcmp(mString, other) != 0; +} + +inline bool String8::operator>=(const char* other) const +{ + return strcmp(mString, other) >= 0; +} + +inline bool String8::operator>(const char* other) const +{ + return strcmp(mString, other) > 0; +} + +inline String8::operator const char*() const +{ + return mString; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STRING8_H diff --git a/include/utils/SystemClock.h b/include/utils/SystemClock.h new file mode 100644 index 000000000..7c319be13 --- /dev/null +++ b/include/utils/SystemClock.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_UTILS_SYSTEMCLOCK_H +#define ANDROID_UTILS_SYSTEMCLOCK_H + +#include +#include + +namespace android { + +int setCurrentTimeMillis(int64_t millis); +int64_t uptimeMillis(); +int64_t elapsedRealtime(); + +}; // namespace android + +#endif // ANDROID_UTILS_SYSTEMCLOCK_H + diff --git a/include/utils/TextOutput.h b/include/utils/TextOutput.h new file mode 100644 index 000000000..d8d86ba82 --- /dev/null +++ b/include/utils/TextOutput.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2006 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. + */ + +#ifndef ANDROID_TEXTOUTPUT_H +#define ANDROID_TEXTOUTPUT_H + +#include + +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class TextOutput +{ +public: + TextOutput() { } + virtual ~TextOutput() { } + + virtual status_t print(const char* txt, size_t len) = 0; + virtual void moveIndent(int delta) = 0; + + class Bundle { + public: + inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); } + inline ~Bundle() { mTO.popBundle(); } + private: + TextOutput& mTO; + }; + + virtual void pushBundle() = 0; + virtual void popBundle() = 0; +}; + +// --------------------------------------------------------------------------- + +// Text output stream for printing to the log (via utils/Log.h). +extern TextOutput& alog; + +// Text output stream for printing to stdout. +extern TextOutput& aout; + +// Text output stream for printing to stderr. +extern TextOutput& aerr; + +typedef TextOutput& (*TextOutputManipFunc)(TextOutput&); + +TextOutput& endl(TextOutput& to); +TextOutput& indent(TextOutput& to); +TextOutput& dedent(TextOutput& to); + +TextOutput& operator<<(TextOutput& to, const char* str); +TextOutput& operator<<(TextOutput& to, char); // writes raw character +TextOutput& operator<<(TextOutput& to, bool); +TextOutput& operator<<(TextOutput& to, int); +TextOutput& operator<<(TextOutput& to, long); +TextOutput& operator<<(TextOutput& to, unsigned int); +TextOutput& operator<<(TextOutput& to, unsigned long); +TextOutput& operator<<(TextOutput& to, long long); +TextOutput& operator<<(TextOutput& to, unsigned long long); +TextOutput& operator<<(TextOutput& to, float); +TextOutput& operator<<(TextOutput& to, double); +TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func); +TextOutput& operator<<(TextOutput& to, const void*); + +class TypeCode +{ +public: + inline TypeCode(uint32_t code); + inline ~TypeCode(); + + inline uint32_t typeCode() const; + +private: + uint32_t mCode; +}; + +TextOutput& operator<<(TextOutput& to, const TypeCode& val); + +class HexDump +{ +public: + HexDump(const void *buf, size_t size, size_t bytesPerLine=16); + inline ~HexDump(); + + inline HexDump& setBytesPerLine(size_t bytesPerLine); + inline HexDump& setSingleLineCutoff(int32_t bytes); + inline HexDump& setAlignment(size_t alignment); + inline HexDump& setCArrayStyle(bool enabled); + + inline const void* buffer() const; + inline size_t size() const; + inline size_t bytesPerLine() const; + inline int32_t singleLineCutoff() const; + inline size_t alignment() const; + inline bool carrayStyle() const; + +private: + const void* mBuffer; + size_t mSize; + size_t mBytesPerLine; + int32_t mSingleLineCutoff; + size_t mAlignment; + bool mCArrayStyle; +}; + +TextOutput& operator<<(TextOutput& to, const HexDump& val); + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline TextOutput& endl(TextOutput& to) +{ + to.print("\n", 1); + return to; +} + +inline TextOutput& indent(TextOutput& to) +{ + to.moveIndent(1); + return to; +} + +inline TextOutput& dedent(TextOutput& to) +{ + to.moveIndent(-1); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, const char* str) +{ + to.print(str, strlen(str)); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, char c) +{ + to.print(&c, 1); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func) +{ + return (*func)(to); +} + +inline TypeCode::TypeCode(uint32_t code) : mCode(code) { } +inline TypeCode::~TypeCode() { } +inline uint32_t TypeCode::typeCode() const { return mCode; } + +inline HexDump::~HexDump() { } + +inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) { + mBytesPerLine = bytesPerLine; return *this; +} +inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) { + mSingleLineCutoff = bytes; return *this; +} +inline HexDump& HexDump::setAlignment(size_t alignment) { + mAlignment = alignment; return *this; +} +inline HexDump& HexDump::setCArrayStyle(bool enabled) { + mCArrayStyle = enabled; return *this; +} + +inline const void* HexDump::buffer() const { return mBuffer; } +inline size_t HexDump::size() const { return mSize; } +inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; } +inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; } +inline size_t HexDump::alignment() const { return mAlignment; } +inline bool HexDump::carrayStyle() const { return mCArrayStyle; } + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_TEXTOUTPUT_H diff --git a/include/utils/TimeUtils.h b/include/utils/TimeUtils.h new file mode 100644 index 000000000..b19e02126 --- /dev/null +++ b/include/utils/TimeUtils.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_TIME_H +#define ANDROID_TIME_H + +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +/* + * This class is the core implementation of the android.util.Time java + * class. It doesn't implement some of the methods that are implemented + * in Java. They could be done here, but it's not expected that this class + * will be used. If that assumption is incorrect, feel free to update this + * file. The reason to do it here is to not mix the implementation of this + * class and the jni glue code. + */ +class Time +{ +public: + struct tm t; + + // this object doesn't own this string + const char *timezone; + + enum { + SEC = 1, + MIN = 2, + HOUR = 3, + MDAY = 4, + MON = 5, + YEAR = 6, + WDAY = 7, + YDAY = 8 + }; + + static int compare(Time& a, Time& b); + + Time(); + + void switchTimezone(const char *timezone); + String8 format(const char *format, const struct strftime_locale *locale) const; + void format2445(short* buf, bool hasTime) const; + String8 toString() const; + void setToNow(); + int64_t toMillis(bool ignoreDst); + void set(int64_t millis); + + inline void set(int sec, int min, int hour, int mday, int mon, int year, + int isdst) + { + this->t.tm_sec = sec; + this->t.tm_min = min; + this->t.tm_hour = hour; + this->t.tm_mday = mday; + this->t.tm_mon = mon; + this->t.tm_year = year; + this->t.tm_isdst = isdst; +#ifdef HAVE_TM_GMTOFF + this->t.tm_gmtoff = 0; +#endif + this->t.tm_wday = 0; + this->t.tm_yday = 0; + } +}; + +}; // namespace android + +#endif // ANDROID_TIME_H diff --git a/include/utils/TimerProbe.h b/include/utils/TimerProbe.h new file mode 100644 index 000000000..f2e32b21a --- /dev/null +++ b/include/utils/TimerProbe.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_TIMER_PROBE_H +#define ANDROID_TIMER_PROBE_H + +#if 0 && defined(HAVE_POSIX_CLOCKS) +#define ENABLE_TIMER_PROBE 1 +#else +#define ENABLE_TIMER_PROBE 0 +#endif + +#if ENABLE_TIMER_PROBE + +#include +#include +#include + +#define TIMER_PROBE(tag) \ + static int _timer_slot_; \ + android::TimerProbe probe(tag, &_timer_slot_) +#define TIMER_PROBE_END() probe.end() +#else +#define TIMER_PROBE(tag) +#define TIMER_PROBE_END() +#endif + +#if ENABLE_TIMER_PROBE +namespace android { + +class TimerProbe { +public: + TimerProbe(const char tag[], int* slot); + void end(); + ~TimerProbe(); +private: + struct Bucket { + int mStart, mReal, mProcess, mThread, mCount; + const char* mTag; + int* mSlotPtr; + int mIndent; + }; + static Vector gBuckets; + static TimerProbe* gExecuteChain; + static int gIndent; + static timespec gRealBase; + TimerProbe* mNext; + static uint32_t ElapsedTime(const timespec& start, const timespec& end); + void print(const timespec& r, const timespec& p, const timespec& t) const; + timespec mRealStart, mPStart, mTStart; + const char* mTag; + int mIndent; + int mBucket; +}; + +}; // namespace android + +#endif +#endif diff --git a/include/utils/Timers.h b/include/utils/Timers.h new file mode 100644 index 000000000..96103995b --- /dev/null +++ b/include/utils/Timers.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Timer functions. +// +#ifndef _LIBS_UTILS_TIMERS_H +#define _LIBS_UTILS_TIMERS_H + +#include +#include +#include + +// ------------------------------------------------------------------ +// C API + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int64_t nsecs_t; // nano-seconds + +static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000000000; +} + +static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000000; +} + +static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000; +} + +static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs) +{ + return secs/1000000000; +} + +static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs) +{ + return secs/1000000; +} + +static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs) +{ + return secs/1000; +} + +static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);} +static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);} +static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);} +static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);} +static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);} +static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);} + +static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); } +static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); } +static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); } + +enum { + SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock + SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point + SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock + SYSTEM_TIME_THREAD = 3 // high-resolution per-thread clock +}; + +// return the system-time according to the specified clock +#ifdef __cplusplus +nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC); +#else +nsecs_t systemTime(int clock); +#endif // def __cplusplus + +// return the system-time according to the specified clock +int sleepForInterval(long interval, struct timeval* pNextTick); + +#ifdef __cplusplus +} // extern "C" +#endif + +// ------------------------------------------------------------------ +// C++ API + +#ifdef __cplusplus + +namespace android { +/* + * Time the duration of something. + * + * Includes some timeval manipulation functions. + */ +class DurationTimer { +public: + DurationTimer(void) {} + ~DurationTimer(void) {} + + // Start the timer. + void start(void); + // Stop the timer. + void stop(void); + // Get the duration in microseconds. + long long durationUsecs(void) const; + + // Subtract two timevals. Returns the difference (ptv1-ptv2) in + // microseconds. + static long long subtractTimevals(const struct timeval* ptv1, + const struct timeval* ptv2); + + // Add the specified amount of time to the timeval. + static void addToTimeval(struct timeval* ptv, long usec); + +private: + struct timeval mStartWhen; + struct timeval mStopWhen; +}; + +}; // android +#endif // def __cplusplus + +#endif // _LIBS_UTILS_TIMERS_H diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h new file mode 100644 index 000000000..c04c37fa9 --- /dev/null +++ b/include/utils/TypeHelpers.h @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_TYPE_HELPERS_H +#define ANDROID_TYPE_HELPERS_H + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +/* + * Types traits + */ + +template struct trait_trivial_ctor { enum { value = false }; }; +template struct trait_trivial_dtor { enum { value = false }; }; +template struct trait_trivial_copy { enum { value = false }; }; +template struct trait_trivial_assign{ enum { value = false }; }; + +template struct trait_pointer { enum { value = false }; }; +template struct trait_pointer { enum { value = true }; }; + +#define ANDROID_BASIC_TYPES_TRAITS( T ) \ + template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = true }; }; \ + template<> struct trait_trivial_assign< T >{ enum { value = true }; }; + +#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \ + template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \ + template<> struct trait_trivial_assign< T >{ enum { value = assign }; }; + +template +struct traits { + enum { + is_pointer = trait_pointer::value, + has_trivial_ctor = is_pointer || trait_trivial_ctor::value, + has_trivial_dtor = is_pointer || trait_trivial_dtor::value, + has_trivial_copy = is_pointer || trait_trivial_copy::value, + has_trivial_assign = is_pointer || trait_trivial_assign::value + }; +}; + +template +struct aggregate_traits { + enum { + is_pointer = false, + has_trivial_ctor = traits::has_trivial_ctor && traits::has_trivial_ctor, + has_trivial_dtor = traits::has_trivial_dtor && traits::has_trivial_dtor, + has_trivial_copy = traits::has_trivial_copy && traits::has_trivial_copy, + has_trivial_assign = traits::has_trivial_assign && traits::has_trivial_assign + }; +}; + +// --------------------------------------------------------------------------- + +/* + * basic types traits + */ + +ANDROID_BASIC_TYPES_TRAITS( void ); +ANDROID_BASIC_TYPES_TRAITS( bool ); +ANDROID_BASIC_TYPES_TRAITS( char ); +ANDROID_BASIC_TYPES_TRAITS( unsigned char ); +ANDROID_BASIC_TYPES_TRAITS( short ); +ANDROID_BASIC_TYPES_TRAITS( unsigned short ); +ANDROID_BASIC_TYPES_TRAITS( int ); +ANDROID_BASIC_TYPES_TRAITS( unsigned int ); +ANDROID_BASIC_TYPES_TRAITS( long ); +ANDROID_BASIC_TYPES_TRAITS( unsigned long ); +ANDROID_BASIC_TYPES_TRAITS( long long ); +ANDROID_BASIC_TYPES_TRAITS( unsigned long long ); +ANDROID_BASIC_TYPES_TRAITS( float ); +ANDROID_BASIC_TYPES_TRAITS( double ); + +// --------------------------------------------------------------------------- + + +/* + * compare and order types + */ + +template inline +int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { + return (lhs < rhs) ? 1 : 0; +} + +template inline +int compare_type(const TYPE& lhs, const TYPE& rhs) { + return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); +} + +/* + * create, destroy, copy and assign types... + */ + +template inline +void construct_type(TYPE* p, size_t n) { + if (!traits::has_trivial_ctor) { + while (n--) { + new(p++) TYPE; + } + } +} + +template inline +void destroy_type(TYPE* p, size_t n) { + if (!traits::has_trivial_dtor) { + while (n--) { + p->~TYPE(); + p++; + } + } +} + +template inline +void copy_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits::has_trivial_copy) { + while (n--) { + new(d) TYPE(*s); + d++, s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template inline +void assign_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits::has_trivial_assign) { + while (n--) { + *d++ = *s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template inline +void splat_type(TYPE* where, const TYPE* what, size_t n) { + if (!traits::has_trivial_copy) { + while (n--) { + new(where) TYPE(*what); + where++; + } + } else { + while (n--) { + *where++ = *what; + } + } +} + +template inline +void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { + d += n; + s += n; + while (n--) { + --d, --s; + if (!traits::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits::has_trivial_dtor) { + s->~TYPE(); + } + } + } else { + memmove(d,s,n*sizeof(TYPE)); + } +} + +template inline +void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { + while (n--) { + if (!traits::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits::has_trivial_dtor) { + s->~TYPE(); + } + d++, s++; + } + } else { + memmove(d,s,n*sizeof(TYPE)); + } +} +// --------------------------------------------------------------------------- + +/* + * a key/value pair + */ + +template +struct key_value_pair_t { + KEY key; + VALUE value; + key_value_pair_t() { } + key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } + key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } + key_value_pair_t(const KEY& k) : key(k) { } + inline bool operator < (const key_value_pair_t& o) const { + return strictly_order_type(key, o.key); + } +}; + +template<> +template +struct trait_trivial_ctor< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_ctor }; }; +template<> +template +struct trait_trivial_dtor< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_dtor }; }; +template<> +template +struct trait_trivial_copy< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_copy }; }; +template<> +template +struct trait_trivial_assign< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_assign};}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_TYPE_HELPERS_H diff --git a/include/utils/Vector.h b/include/utils/Vector.h new file mode 100644 index 000000000..be365d83e --- /dev/null +++ b/include/utils/Vector.h @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_VECTOR_H +#define ANDROID_VECTOR_H + +#include +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +/*! + * The main templated vector class ensuring type safety + * while making use of VectorImpl. + * This is the class users want to use. + */ + +template +class Vector : private VectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + Vector(); + Vector(const Vector& rhs); + virtual ~Vector(); + + /*! copy operator */ + const Vector& operator = (const Vector& rhs) const; + Vector& operator = (const Vector& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + //! read-write C-style access + TYPE* editArray(); + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! copy-on write support, grants write access to an item + TYPE& editItemAt(size_t index); + //! grants right acces to the top of the stack (last element) + TYPE& editTop(); + + /*! + * append/insert another vector + */ + + //! insert another vector at a given index + ssize_t insertVectorAt(const Vector& vector, size_t index); + + //! append another vector at the end of this one + ssize_t appendVector(const Vector& vector); + + + /*! + * add/insert/replace items + */ + + //! insert one or several items initialized with their default constructor + inline ssize_t insertAt(size_t index, size_t numItems = 1); + //! insert on onr several items initialized from a prototype item + ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); + //! pop the top of the stack (removes the last element). No-op if the stack's empty + inline void pop(); + //! pushes an item initialized with its default constructor + inline void push(); + //! pushes an item on the top of the stack + void push(const TYPE& item); + //! same as push() but returns the index the item was added at (or an error) + inline ssize_t add(); + //! same as push() but returns the index the item was added at (or an error) + ssize_t add(const TYPE& item); + //! replace an item with a new one initialized with its default constructor + inline ssize_t replaceAt(size_t index); + //! replace an item with a new one + ssize_t replaceAt(const TYPE& item, size_t index); + + /*! + * remove items + */ + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + + /*! + * sort (stable) the array + */ + + typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); + typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); + + inline status_t sort(compar_t cmp); + inline status_t sort(compar_r_t cmp, void* state); + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template inline +Vector::Vector() + : VectorImpl(sizeof(TYPE), + ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template inline +Vector::Vector(const Vector& rhs) + : VectorImpl(rhs) { +} + +template inline +Vector::~Vector() { + finish_vector(); +} + +template inline +Vector& Vector::operator = (const Vector& rhs) { + VectorImpl::operator = (rhs); + return *this; +} + +template inline +const Vector& Vector::operator = (const Vector& rhs) const { + VectorImpl::operator = (rhs); + return *this; +} + +template inline +const TYPE* Vector::array() const { + return static_cast(arrayImpl()); +} + +template inline +TYPE* Vector::editArray() { + return static_cast(editArrayImpl()); +} + + +template inline +const TYPE& Vector::operator[](size_t index) const { + LOG_FATAL_IF( index>=size(), + "itemAt: index %d is past size %d", (int)index, (int)size() ); + return *(array() + index); +} + +template inline +const TYPE& Vector::itemAt(size_t index) const { + return operator[](index); +} + +template inline +const TYPE& Vector::mirrorItemAt(ssize_t index) const { + LOG_FATAL_IF( (index>0 ? index : -index)>=size(), + "mirrorItemAt: index %d is past size %d", + (int)index, (int)size() ); + return *(array() + ((index<0) ? (size()-index) : index)); +} + +template inline +const TYPE& Vector::top() const { + return *(array() + size() - 1); +} + +template inline +TYPE& Vector::editItemAt(size_t index) { + return *( static_cast(editItemLocation(index)) ); +} + +template inline +TYPE& Vector::editTop() { + return *( static_cast(editItemLocation(size()-1)) ); +} + +template inline +ssize_t Vector::insertVectorAt(const Vector& vector, size_t index) { + return VectorImpl::insertVectorAt(reinterpret_cast(vector), index); +} + +template inline +ssize_t Vector::appendVector(const Vector& vector) { + return VectorImpl::appendVector(reinterpret_cast(vector)); +} + +template inline +ssize_t Vector::insertAt(const TYPE& item, size_t index, size_t numItems) { + return VectorImpl::insertAt(&item, index, numItems); +} + +template inline +void Vector::push(const TYPE& item) { + return VectorImpl::push(&item); +} + +template inline +ssize_t Vector::add(const TYPE& item) { + return VectorImpl::add(&item); +} + +template inline +ssize_t Vector::replaceAt(const TYPE& item, size_t index) { + return VectorImpl::replaceAt(&item, index); +} + +template inline +ssize_t Vector::insertAt(size_t index, size_t numItems) { + return VectorImpl::insertAt(index, numItems); +} + +template inline +void Vector::pop() { + VectorImpl::pop(); +} + +template inline +void Vector::push() { + VectorImpl::push(); +} + +template inline +ssize_t Vector::add() { + return VectorImpl::add(); +} + +template inline +ssize_t Vector::replaceAt(size_t index) { + return VectorImpl::replaceAt(index); +} + +template inline +ssize_t Vector::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +template inline +status_t Vector::sort(Vector::compar_t cmp) { + return VectorImpl::sort((VectorImpl::compar_t)cmp); +} + +template inline +status_t Vector::sort(Vector::compar_r_t cmp, void* state) { + return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state); +} + +// --------------------------------------------------------------------------- + +template +void Vector::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast(storage), num ); +} + +template +void Vector::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast(storage), num ); +} + +template +void Vector::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void Vector::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast(dest), reinterpret_cast(item), num ); +} + +template +void Vector::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void Vector::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h new file mode 100644 index 000000000..2525229be --- /dev/null +++ b/include/utils/VectorImpl.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_VECTOR_IMPL_H +#define ANDROID_VECTOR_IMPL_H + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// No user serviceable parts in here... +// --------------------------------------------------------------------------- + +namespace android { + +/*! + * Implementation of the guts of the vector<> class + * this ensures backward binary compatibility and + * reduces code size. + * For performance reasons, we expose mStorage and mCount + * so these fields are set in stone. + * + */ + +class VectorImpl +{ +public: + enum { // flags passed to the ctor + HAS_TRIVIAL_CTOR = 0x00000001, + HAS_TRIVIAL_DTOR = 0x00000002, + HAS_TRIVIAL_COPY = 0x00000004, + HAS_TRIVIAL_ASSIGN = 0x00000008 + }; + + VectorImpl(size_t itemSize, uint32_t flags); + VectorImpl(const VectorImpl& rhs); + virtual ~VectorImpl(); + + /*! must be called from subclasses destructor */ + void finish_vector(); + + VectorImpl& operator = (const VectorImpl& rhs); + + /*! C-style array access */ + inline const void* arrayImpl() const { return mStorage; } + void* editArrayImpl(); + + /*! vector stats */ + inline size_t size() const { return mCount; } + inline bool isEmpty() const { return mCount == 0; } + size_t capacity() const; + ssize_t setCapacity(size_t size); + + /*! append/insert another vector */ + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + + /*! add/insert/replace items */ + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + void pop(); + void push(); + void push(const void* item); + ssize_t add(); + ssize_t add(const void* item); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); + + /*! remove items */ + ssize_t removeItemsAt(size_t index, size_t count = 1); + void clear(); + + const void* itemLocation(size_t index) const; + void* editItemLocation(size_t index); + + typedef int (*compar_t)(const void* lhs, const void* rhs); + typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state); + status_t sort(compar_t cmp); + status_t sort(compar_r_t cmp, void* state); + +protected: + size_t itemSize() const; + void release_storage(); + + virtual void do_construct(void* storage, size_t num) const = 0; + virtual void do_destroy(void* storage, size_t num) const = 0; + virtual void do_copy(void* dest, const void* from, size_t num) const = 0; + virtual void do_splat(void* dest, const void* item, size_t num) const = 0; + virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; + virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; + + // take care of FBC... + virtual void reservedVectorImpl1(); + virtual void reservedVectorImpl2(); + virtual void reservedVectorImpl3(); + virtual void reservedVectorImpl4(); + virtual void reservedVectorImpl5(); + virtual void reservedVectorImpl6(); + virtual void reservedVectorImpl7(); + virtual void reservedVectorImpl8(); + +private: + void* _grow(size_t where, size_t amount); + void _shrink(size_t where, size_t amount); + + inline void _do_construct(void* storage, size_t num) const; + inline void _do_destroy(void* storage, size_t num) const; + inline void _do_copy(void* dest, const void* from, size_t num) const; + inline void _do_splat(void* dest, const void* item, size_t num) const; + inline void _do_move_forward(void* dest, const void* from, size_t num) const; + inline void _do_move_backward(void* dest, const void* from, size_t num) const; + + // These 2 fields are exposed in the inlines below, + // so they're set in stone. + void * mStorage; // base address of the vector + size_t mCount; // number of items + + const uint32_t mFlags; + const size_t mItemSize; +}; + + + +class SortedVectorImpl : public VectorImpl +{ +public: + SortedVectorImpl(size_t itemSize, uint32_t flags); + SortedVectorImpl(const VectorImpl& rhs); + virtual ~SortedVectorImpl(); + + SortedVectorImpl& operator = (const SortedVectorImpl& rhs); + + //! finds the index of an item + ssize_t indexOf(const void* item) const; + + //! finds where this item should be inserted + size_t orderOf(const void* item) const; + + //! add an item in the right place (or replaces it if there is one) + ssize_t add(const void* item); + + //! merges a vector into this one + ssize_t merge(const VectorImpl& vector); + ssize_t merge(const SortedVectorImpl& vector); + + //! removes an item + ssize_t remove(const void* item); + +protected: + virtual int do_compare(const void* lhs, const void* rhs) const = 0; + + // take care of FBC... + virtual void reservedSortedVectorImpl1(); + virtual void reservedSortedVectorImpl2(); + virtual void reservedSortedVectorImpl3(); + virtual void reservedSortedVectorImpl4(); + virtual void reservedSortedVectorImpl5(); + virtual void reservedSortedVectorImpl6(); + virtual void reservedSortedVectorImpl7(); + virtual void reservedSortedVectorImpl8(); + +private: + ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; + + // these are made private, because they can't be used on a SortedVector + // (they don't have an implementation either) + ssize_t add(); + void pop(); + void push(); + void push(const void* item); + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); +}; + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_IMPL_H diff --git a/include/utils/ZipEntry.h b/include/utils/ZipEntry.h new file mode 100644 index 000000000..e4698dfbb --- /dev/null +++ b/include/utils/ZipEntry.h @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Zip archive entries. +// +// The ZipEntry class is tightly meshed with the ZipFile class. +// +#ifndef __LIBS_ZIPENTRY_H +#define __LIBS_ZIPENTRY_H + +#include "Errors.h" + +#include +#include + +namespace android { + +class ZipFile; + +/* + * ZipEntry objects represent a single entry in a Zip archive. + * + * You can use one of these to get or set information about an entry, but + * there are no functions here for accessing the data itself. (We could + * tuck a pointer to the ZipFile in here for convenience, but that raises + * the likelihood of using ZipEntry objects after discarding the ZipFile.) + * + * File information is stored in two places: next to the file data (the Local + * File Header, and possibly a Data Descriptor), and at the end of the file + * (the Central Directory Entry). The two must be kept in sync. + */ +class ZipEntry { +public: + friend class ZipFile; + + ZipEntry(void) + : mDeleted(false), mMarked(false) + {} + ~ZipEntry(void) {} + + /* + * Returns "true" if the data is compressed. + */ + bool isCompressed(void) const { + return mCDE.mCompressionMethod != kCompressStored; + } + int getCompressionMethod(void) const { return mCDE.mCompressionMethod; } + + /* + * Return the uncompressed length. + */ + off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; } + + /* + * Return the compressed length. For uncompressed data, this returns + * the same thing as getUncompresesdLen(). + */ + off_t getCompressedLen(void) const { return mCDE.mCompressedSize; } + + /* + * Return the absolute file offset of the start of the compressed or + * uncompressed data. + */ + off_t getFileOffset(void) const { + return mCDE.mLocalHeaderRelOffset + + LocalFileHeader::kLFHLen + + mLFH.mFileNameLength + + mLFH.mExtraFieldLength; + } + + /* + * Return the data CRC. + */ + unsigned long getCRC32(void) const { return mCDE.mCRC32; } + + /* + * Return file modification time in UNIX seconds-since-epoch. + */ + time_t getModWhen(void) const; + + /* + * Return the archived file name. + */ + const char* getFileName(void) const { return (const char*) mCDE.mFileName; } + + /* + * Application-defined "mark". Can be useful when synchronizing the + * contents of an archive with contents on disk. + */ + bool getMarked(void) const { return mMarked; } + void setMarked(bool val) { mMarked = val; } + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short getShortLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long getLongLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + static inline void putShortLE(unsigned char* buf, short val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + } + static inline void putLongLE(unsigned char* buf, long val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + buf[2] = (unsigned char) (val >> 16); + buf[3] = (unsigned char) (val >> 24); + } + + /* defined for Zip archives */ + enum { + kCompressStored = 0, // no compression + // shrunk = 1, + // reduced 1 = 2, + // reduced 2 = 3, + // reduced 3 = 4, + // reduced 4 = 5, + // imploded = 6, + // tokenized = 7, + kCompressDeflated = 8, // standard deflate + // Deflate64 = 9, + // lib imploded = 10, + // reserved = 11, + // bzip2 = 12, + }; + + /* + * Deletion flag. If set, the entry will be removed on the next + * call to "flush". + */ + bool getDeleted(void) const { return mDeleted; } + +protected: + /* + * Initialize the structure from the file, which is pointing at + * our Central Directory entry. + */ + status_t initFromCDE(FILE* fp); + + /* + * Initialize the structure for a new file. We need the filename + * and comment so that we can properly size the LFH area. The + * filename is mandatory, the comment is optional. + */ + void initNew(const char* fileName, const char* comment); + + /* + * Initialize the structure with the contents of a ZipEntry from + * another file. + */ + status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry); + + /* + * Add some pad bytes to the LFH. We do this by adding or resizing + * the "extra" field. + */ + status_t addPadding(int padding); + + /* + * Set information about the data for this entry. + */ + void setDataInfo(long uncompLen, long compLen, unsigned long crc32, + int compressionMethod); + + /* + * Set the modification date. + */ + void setModWhen(time_t when); + + /* + * Return the offset of the local file header. + */ + off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; } + + /* + * Set the offset of the local file header, relative to the start of + * the current file. + */ + void setLFHOffset(off_t offset) { + mCDE.mLocalHeaderRelOffset = (long) offset; + } + + /* mark for deletion; used by ZipFile::remove() */ + void setDeleted(void) { mDeleted = true; } + +private: + /* these are private and not defined */ + ZipEntry(const ZipEntry& src); + ZipEntry& operator=(const ZipEntry& src); + + /* returns "true" if the CDE and the LFH agree */ + bool compareHeaders(void) const; + void copyCDEtoLFH(void); + + bool mDeleted; // set if entry is pending deletion + bool mMarked; // app-defined marker + + /* + * Every entry in the Zip archive starts off with one of these. + */ + class LocalFileHeader { + public: + LocalFileHeader(void) : + mVersionToExtract(0), + mGPBitFlag(0), + mCompressionMethod(0), + mLastModFileTime(0), + mLastModFileDate(0), + mCRC32(0), + mCompressedSize(0), + mUncompressedSize(0), + mFileNameLength(0), + mExtraFieldLength(0), + mFileName(NULL), + mExtraField(NULL) + {} + virtual ~LocalFileHeader(void) { + delete[] mFileName; + delete[] mExtraField; + } + + status_t read(FILE* fp); + status_t write(FILE* fp); + + // unsigned long mSignature; + unsigned short mVersionToExtract; + unsigned short mGPBitFlag; + unsigned short mCompressionMethod; + unsigned short mLastModFileTime; + unsigned short mLastModFileDate; + unsigned long mCRC32; + unsigned long mCompressedSize; + unsigned long mUncompressedSize; + unsigned short mFileNameLength; + unsigned short mExtraFieldLength; + unsigned char* mFileName; + unsigned char* mExtraField; + + enum { + kSignature = 0x04034b50, + kLFHLen = 30, // LocalFileHdr len, excl. var fields + }; + + void dump(void) const; + }; + + /* + * Every entry in the Zip archive has one of these in the "central + * directory" at the end of the file. + */ + class CentralDirEntry { + public: + CentralDirEntry(void) : + mVersionMadeBy(0), + mVersionToExtract(0), + mGPBitFlag(0), + mCompressionMethod(0), + mLastModFileTime(0), + mLastModFileDate(0), + mCRC32(0), + mCompressedSize(0), + mUncompressedSize(0), + mFileNameLength(0), + mExtraFieldLength(0), + mFileCommentLength(0), + mDiskNumberStart(0), + mInternalAttrs(0), + mExternalAttrs(0), + mLocalHeaderRelOffset(0), + mFileName(NULL), + mExtraField(NULL), + mFileComment(NULL) + {} + virtual ~CentralDirEntry(void) { + delete[] mFileName; + delete[] mExtraField; + delete[] mFileComment; + } + + status_t read(FILE* fp); + status_t write(FILE* fp); + + // unsigned long mSignature; + unsigned short mVersionMadeBy; + unsigned short mVersionToExtract; + unsigned short mGPBitFlag; + unsigned short mCompressionMethod; + unsigned short mLastModFileTime; + unsigned short mLastModFileDate; + unsigned long mCRC32; + unsigned long mCompressedSize; + unsigned long mUncompressedSize; + unsigned short mFileNameLength; + unsigned short mExtraFieldLength; + unsigned short mFileCommentLength; + unsigned short mDiskNumberStart; + unsigned short mInternalAttrs; + unsigned long mExternalAttrs; + unsigned long mLocalHeaderRelOffset; + unsigned char* mFileName; + unsigned char* mExtraField; + unsigned char* mFileComment; + + void dump(void) const; + + enum { + kSignature = 0x02014b50, + kCDELen = 46, // CentralDirEnt len, excl. var fields + }; + }; + + enum { + //kDataDescriptorSignature = 0x08074b50, // currently unused + kDataDescriptorLen = 16, // four 32-bit fields + + kDefaultVersion = 20, // need deflate, nothing much else + kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3 + kUsesDataDescr = 0x0008, // GPBitFlag bit 3 + }; + + LocalFileHeader mLFH; + CentralDirEntry mCDE; +}; + +}; // namespace android + +#endif // __LIBS_ZIPENTRY_H diff --git a/include/utils/ZipFile.h b/include/utils/ZipFile.h new file mode 100644 index 000000000..44df5bbaa --- /dev/null +++ b/include/utils/ZipFile.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// General-purpose Zip archive access. This class allows both reading and +// writing to Zip archives, including deletion of existing entries. +// +#ifndef __LIBS_ZIPFILE_H +#define __LIBS_ZIPFILE_H + +#include "ZipEntry.h" +#include "Vector.h" +#include "Errors.h" +#include + +namespace android { + +/* + * Manipulate a Zip archive. + * + * Some changes will not be visible in the until until "flush" is called. + * + * The correct way to update a file archive is to make all changes to a + * copy of the archive in a temporary file, and then unlink/rename over + * the original after everything completes. Because we're only interested + * in using this for packaging, we don't worry about such things. Crashing + * after making changes and before flush() completes could leave us with + * an unusable Zip archive. + */ +class ZipFile { +public: + ZipFile(void) + : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) + {} + ~ZipFile(void) { + if (!mReadOnly) + flush(); + if (mZipFp != NULL) + fclose(mZipFp); + discardEntries(); + } + + /* + * Open a new or existing archive. + */ + typedef enum { + kOpenReadOnly = 0x01, + kOpenReadWrite = 0x02, + kOpenCreate = 0x04, // create if it doesn't exist + kOpenTruncate = 0x08, // if it exists, empty it + }; + status_t open(const char* zipFileName, int flags); + + /* + * Add a file to the end of the archive. Specify whether you want the + * library to try to store it compressed. + * + * If "storageName" is specified, the archive will use that instead + * of "fileName". + * + * If there is already an entry with the same name, the call fails. + * Existing entries with the same name must be removed first. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const char* fileName, int compressionMethod, + ZipEntry** ppEntry) + { + return add(fileName, fileName, compressionMethod, ppEntry); + } + status_t add(const char* fileName, const char* storageName, + int compressionMethod, ZipEntry** ppEntry) + { + return addCommon(fileName, NULL, 0, storageName, + ZipEntry::kCompressStored, + compressionMethod, ppEntry); + } + + /* + * Add a file that is already compressed with gzip. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t addGzip(const char* fileName, const char* storageName, + ZipEntry** ppEntry) + { + return addCommon(fileName, NULL, 0, storageName, + ZipEntry::kCompressDeflated, + ZipEntry::kCompressDeflated, ppEntry); + } + + /* + * Add a file from an in-memory data buffer. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const void* data, size_t size, const char* storageName, + int compressionMethod, ZipEntry** ppEntry) + { + return addCommon(NULL, data, size, storageName, + ZipEntry::kCompressStored, + compressionMethod, ppEntry); + } + + /* + * Add an entry by copying it from another zip file. If "padding" is + * nonzero, the specified number of bytes will be added to the "extra" + * field in the header. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, + int padding, ZipEntry** ppEntry); + + /* + * Mark an entry as having been removed. It is not actually deleted + * from the archive or our internal data structures until flush() is + * called. + */ + status_t remove(ZipEntry* pEntry); + + /* + * Flush changes. If mNeedCDRewrite is set, this writes the central dir. + */ + status_t flush(void); + + /* + * Expand the data into the buffer provided. The buffer must hold + * at least bytes. Variation expands directly + * to a file. + * + * Returns "false" if an error was encountered in the compressed data. + */ + //bool uncompress(const ZipEntry* pEntry, void* buf) const; + //bool uncompress(const ZipEntry* pEntry, FILE* fp) const; + void* uncompress(const ZipEntry* pEntry); + + /* + * Get an entry, by name. Returns NULL if not found. + * + * Does not return entries pending deletion. + */ + ZipEntry* getEntryByName(const char* fileName) const; + + /* + * Get the Nth entry in the archive. + * + * This will return an entry that is pending deletion. + */ + int getNumEntries(void) const { return mEntries.size(); } + ZipEntry* getEntryByIndex(int idx) const; + +private: + /* these are private and not defined */ + ZipFile(const ZipFile& src); + ZipFile& operator=(const ZipFile& src); + + class EndOfCentralDir { + public: + EndOfCentralDir(void) : + mDiskNumber(0), + mDiskWithCentralDir(0), + mNumEntries(0), + mTotalNumEntries(0), + mCentralDirSize(0), + mCentralDirOffset(0), + mCommentLen(0), + mComment(NULL) + {} + virtual ~EndOfCentralDir(void) { + delete[] mComment; + } + + status_t readBuf(const unsigned char* buf, int len); + status_t write(FILE* fp); + + //unsigned long mSignature; + unsigned short mDiskNumber; + unsigned short mDiskWithCentralDir; + unsigned short mNumEntries; + unsigned short mTotalNumEntries; + unsigned long mCentralDirSize; + unsigned long mCentralDirOffset; // offset from first disk + unsigned short mCommentLen; + unsigned char* mComment; + + enum { + kSignature = 0x06054b50, + kEOCDLen = 22, // EndOfCentralDir len, excl. comment + + kMaxCommentLen = 65535, // longest possible in ushort + kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, + + }; + + void dump(void) const; + }; + + + /* read all entries in the central dir */ + status_t readCentralDir(void); + + /* crunch deleted entries out */ + status_t crunchArchive(void); + + /* clean up mEntries */ + void discardEntries(void); + + /* common handler for all "add" functions */ + status_t addCommon(const char* fileName, const void* data, size_t size, + const char* storageName, int sourceType, int compressionMethod, + ZipEntry** ppEntry); + + /* copy all of "srcFp" into "dstFp" */ + status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32); + /* copy all of "data" into "dstFp" */ + status_t copyDataToFp(FILE* dstFp, + const void* data, size_t size, unsigned long* pCRC32); + /* copy some of "srcFp" into "dstFp" */ + status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, + unsigned long* pCRC32); + /* like memmove(), but on parts of a single file */ + status_t filemove(FILE* fp, off_t dest, off_t src, size_t n); + /* compress all of "srcFp" into "dstFp", using Deflate */ + status_t compressFpToFp(FILE* dstFp, FILE* srcFp, + const void* data, size_t size, unsigned long* pCRC32); + + /* get modification date from a file descriptor */ + time_t getModTime(int fd); + + /* + * We use stdio FILE*, which gives us buffering but makes dealing + * with files >2GB awkward. Until we support Zip64, we're fine. + */ + FILE* mZipFp; // Zip file pointer + + /* one of these per file */ + EndOfCentralDir mEOCD; + + /* did we open this read-only? */ + bool mReadOnly; + + /* set this when we trash the central dir */ + bool mNeedCDRewrite; + + /* + * One ZipEntry per entry in the zip file. I'm using pointers instead + * of objects because it's easier than making operator= work for the + * classes and sub-classes. + */ + Vector mEntries; +}; + +}; // namespace android + +#endif // __LIBS_ZIPFILE_H diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h new file mode 100644 index 000000000..30e00368e --- /dev/null +++ b/include/utils/ZipFileCRO.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008 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. + */ + +// +// C API for ead-only access to Zip archives, with minimal heap allocation. +// +#ifndef __LIBS_ZIPFILECRO_H +#define __LIBS_ZIPFILECRO_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Trivial typedef to ensure that ZipFileCRO is not treated as a simple integer. + */ +typedef void* ZipFileCRO; + +/* + * Trivial typedef to ensure that ZipEntryCRO is not treated as a simple + * integer. We use NULL to indicate an invalid value. + */ +typedef void* ZipEntryCRO; + +extern ZipFileCRO ZipFileXRO_open(const char* path); + +extern void ZipFileCRO_destroy(ZipFileCRO zip); + +extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, + const char* fileName); + +extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, + int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); + +extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /*__LIBS_ZIPFILECRO_H*/ diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h new file mode 100644 index 000000000..51c4f2fb6 --- /dev/null +++ b/include/utils/ZipFileRO.h @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2007 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. + */ + +// +// Read-only access to Zip archives, with minimal heap allocation. +// +// This is similar to the more-complete ZipFile class, but no attempt +// has been made to make them interchangeable. This class operates under +// a very different set of assumptions and constraints. +// +#ifndef __LIBS_ZIPFILERO_H +#define __LIBS_ZIPFILERO_H + +#include "Errors.h" +#include "FileMap.h" + +#include +#include +#include + +namespace android { + +/* + * Trivial typedef to ensure that ZipEntryRO is not treated as a simple + * integer. We use NULL to indicate an invalid value. + */ +typedef void* ZipEntryRO; + +/* + * Open a Zip archive for reading. + * + * We want "open" and "find entry by name" to be fast operations, and we + * want to use as little memory as possible. We memory-map the file, + * and load a hash table with pointers to the filenames (which aren't + * null-terminated). The other fields are at a fixed offset from the + * filename, so we don't need to extract those (but we do need to byte-read + * and endian-swap them every time we want them). + * + * To speed comparisons when doing a lookup by name, we could make the mapping + * "private" (copy-on-write) and null-terminate the filenames after verifying + * the record structure. However, this requires a private mapping of + * every page that the Central Directory touches. Easier to tuck a copy + * of the string length into the hash table entry. + */ +class ZipFileRO { +public: + ZipFileRO() + : mFd(-1), mFileMap(NULL), mHashTableSize(-1), mHashTable(NULL) + {} + ~ZipFileRO() { + free(mHashTable); + if (mFileMap) + mFileMap->release(); + if (mFd >= 0) + close(mFd); + } + + /* + * Open an archive. + */ + status_t open(const char* zipFileName); + + /* + * Find an entry, by name. Returns the entry identifier, or NULL if + * not found. + * + * If two entries have the same name, one will be chosen at semi-random. + */ + ZipEntryRO findEntryByName(const char* fileName) const; + + /* + * Return the #of entries in the Zip archive. + */ + int getNumEntries(void) const { + return mNumEntries; + } + + /* + * Return the Nth entry. Zip file entries are not stored in sorted + * order, and updated entries may appear at the end, so anyone walking + * the archive needs to avoid making ordering assumptions. We take + * that further by returning the Nth non-empty entry in the hash table + * rather than the Nth entry in the archive. + * + * Valid values are [0..numEntries). + * + * [This is currently O(n). If it needs to be fast we can allocate an + * additional data structure or provide an iterator interface.] + */ + ZipEntryRO findEntryByIndex(int idx) const; + + /* + * Copy the filename into the supplied buffer. Returns 0 on success, + * -1 if "entry" is invalid, or the filename length if it didn't fit. The + * length, and the returned string, include the null-termination. + */ + int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const; + + /* + * Get the vital stats for an entry. Pass in NULL pointers for anything + * you don't need. + * + * "*pOffset" holds the Zip file offset of the entry's data. + * + * Returns "false" if "entry" is bogus or if the data in the Zip file + * appears to be bad. + */ + bool getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; + + /* + * Create a new FileMap object that maps a subset of the archive. For + * an uncompressed entry this effectively provides a pointer to the + * actual data, for a compressed entry this provides the input buffer + * for inflate(). + */ + FileMap* createEntryFileMap(ZipEntryRO entry) const; + + /* + * Uncompress the data into a buffer. Depending on the compression + * format, this is either an "inflate" operation or a memcpy. + * + * Use "uncompLen" from getEntryInfo() to determine the required + * buffer size. + * + * Returns "true" on success. + */ + bool uncompressEntry(ZipEntryRO entry, void* buffer) const; + + /* + * Uncompress the data to an open file descriptor. + */ + bool uncompressEntry(ZipEntryRO entry, int fd) const; + + /* Zip compression methods we support */ + enum { + kCompressStored = 0, // no compression + kCompressDeflated = 8, // standard deflate + }; + + /* + * Utility function: uncompress deflated data, buffer to buffer. + */ + static bool inflateBuffer(void* outBuf, const void* inBuf, + long uncompLen, long compLen); + + /* + * Utility function: uncompress deflated data, buffer to fd. + */ + static bool inflateBuffer(int fd, const void* inBuf, + long uncompLen, long compLen); + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short get2LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long get4LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + +private: + /* these are private and not defined */ + ZipFileRO(const ZipFileRO& src); + ZipFileRO& operator=(const ZipFileRO& src); + + /* parse the archive, prepping internal structures */ + bool parseZipArchive(void); + + /* add a new entry to the hash table */ + void addToHash(const char* str, int strLen, unsigned int hash); + + /* compute string hash code */ + static unsigned int computeHash(const char* str, int len); + + /* convert a ZipEntryRO back to a hash table index */ + int entryToIndex(const ZipEntryRO entry) const; + + /* + * One entry in the hash table. + */ + typedef struct HashEntry { + const char* name; + unsigned short nameLen; + //unsigned int hash; + } HashEntry; + + /* open Zip archive */ + int mFd; + + /* mapped file */ + FileMap* mFileMap; + + /* number of entries in the Zip archive */ + int mNumEntries; + + /* + * We know how many entries are in the Zip archive, so we have a + * fixed-size hash table. We probe for an empty slot. + */ + int mHashTableSize; + HashEntry* mHashTable; +}; + +}; // namespace android + +#endif /*__LIBS_ZIPFILERO_H*/ diff --git a/include/utils/ZipUtils.h b/include/utils/ZipUtils.h new file mode 100644 index 000000000..42c42b6c0 --- /dev/null +++ b/include/utils/ZipUtils.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2007 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. + */ + +// +// Miscellaneous zip/gzip utility functions. +// +#ifndef __LIBS_ZIPUTILS_H +#define __LIBS_ZIPUTILS_H + +#include + +namespace android { + +/* + * Container class for utility functions, primarily for namespace reasons. + */ +class ZipUtils { +public: + /* + * General utility function for uncompressing "deflate" data from a file + * to a buffer. + */ + static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, + long compressedLen); + static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, + long compressedLen); + + /* + * Someday we might want to make this generic and handle bzip2 ".bz2" + * files too. + * + * We could declare gzip to be a sub-class of zip that has exactly + * one always-compressed entry, but we currently want to treat Zip + * and gzip as distinct, so there's no value. + * + * The zlib library has some gzip utilities, but it has no interface + * for extracting the uncompressed length of the file (you do *not* + * want to gzseek to the end). + * + * Pass in a seeked file pointer for the gzip file. If this is a gzip + * file, we set our return values appropriately and return "true" with + * the file seeked to the start of the compressed data. + */ + static bool examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32); + +private: + ZipUtils() {} + ~ZipUtils() {} +}; + +}; // namespace android + +#endif /*__LIBS_ZIPUTILS_H*/ diff --git a/include/utils/ashmem.h b/include/utils/ashmem.h new file mode 100644 index 000000000..085477578 --- /dev/null +++ b/include/utils/ashmem.h @@ -0,0 +1,41 @@ +/* utils/ashmem.h + ** + ** Copyright 2008 The Android Open Source Project + ** + ** This file is dual licensed. It may be redistributed and/or modified + ** under the terms of the Apache 2.0 License OR version 2 of the GNU + ** General Public License. + */ + +#ifndef _UTILS_ASHMEM_H +#define _UTILS_ASHMEM_H + +#include +#include + +#define ASHMEM_NAME_LEN 256 + +#define ASHMEM_NAME_DEF "dev/ashmem" + +/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */ +#define ASHMEM_NOT_REAPED 0 +#define ASHMEM_WAS_REAPED 1 + +/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */ +#define ASHMEM_NOW_UNPINNED 0 +#define ASHMEM_NOW_PINNED 1 + +#define __ASHMEMIOC 0x77 + +#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN]) +#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN]) +#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t) +#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4) +#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long) +#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6) +#define ASHMEM_PIN _IO(__ASHMEMIOC, 7) +#define ASHMEM_UNPIN _IO(__ASHMEMIOC, 8) +#define ASHMEM_ISPINNED _IO(__ASHMEMIOC, 9) +#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10) + +#endif /* _UTILS_ASHMEM_H */ diff --git a/include/utils/executablepath.h b/include/utils/executablepath.h new file mode 100644 index 000000000..c979432ba --- /dev/null +++ b/include/utils/executablepath.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef _UTILS_EXECUTABLEPATH_H +#define _UTILS_EXECUTABLEPATH_H + +#include + +// returns the path to this executable +#if __cplusplus +extern "C" +#endif +void executablepath(char s[PATH_MAX]); + +#endif // _UTILS_EXECUTABLEPATH_H diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h new file mode 100644 index 000000000..dbd8672e0 --- /dev/null +++ b/include/utils/inet_address.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Internet address classes. Modeled after Java classes. +// +#ifndef _RUNTIME_INET_ADDRESS_H +#define _RUNTIME_INET_ADDRESS_H + +#ifdef HAVE_ANDROID_OS +#error DO NOT USE THIS FILE IN THE DEVICE BUILD +#endif + + +namespace android { + +/* + * This class holds Internet addresses. Perhaps more useful is its + * ability to look up addresses by name. + * + * Invoke one of the static factory methods to create a new object. + */ +class InetAddress { +public: + virtual ~InetAddress(void); + + // create from w.x.y.z or foo.bar.com notation + static InetAddress* getByName(const char* host); + + // copy-construction + InetAddress(const InetAddress& orig); + + const void* getAddress(void) const { return mAddress; } + int getAddressLength(void) const { return mLength; } + const char* getHostName(void) const { return mName; } + +private: + InetAddress(void); + // assignment (private) + InetAddress& operator=(const InetAddress& addr); + + // use a void* here so we don't have to expose actual socket headers + void* mAddress; // this is really a ptr to sockaddr_in + int mLength; + char* mName; +}; + + +/* + * Base class for socket addresses. + */ +class SocketAddress { +public: + SocketAddress() {} + virtual ~SocketAddress() {} +}; + + +/* + * Internet address class. This combines an InetAddress with a port. + */ +class InetSocketAddress : public SocketAddress { +public: + InetSocketAddress() : + mAddress(0), mPort(-1) + {} + ~InetSocketAddress(void) { + delete mAddress; + } + + // Create an address with a host wildcard (useful for servers). + bool create(int port); + // Create an address with the specified host and port. + bool create(const InetAddress* addr, int port); + // Create an address with the specified host and port. Does the + // hostname lookup. + bool create(const char* host, int port); + + const InetAddress* getAddress(void) const { return mAddress; } + const int getPort(void) const { return mPort; } + const char* getHostName(void) const { return mAddress->getHostName(); } + +private: + InetAddress* mAddress; + int mPort; +}; + +}; // namespace android + +#endif // _RUNTIME_INET_ADDRESS_H diff --git a/include/utils/misc.h b/include/utils/misc.h new file mode 100644 index 000000000..62e84b489 --- /dev/null +++ b/include/utils/misc.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Handy utility functions and portability code. +// +#ifndef _LIBS_UTILS_MISC_H +#define _LIBS_UTILS_MISC_H + +#include +#include "utils/Endian.h" + +namespace android { + +/* get #of elements in a static array */ +#ifndef NELEM +# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) +#endif + +/* + * Make a copy of the string, using "new[]" instead of "malloc". Free the + * string with delete[]. + * + * Returns NULL if "str" is NULL. + */ +char* strdupNew(const char* str); + +/* + * Concatenate an argument vector into a single string. If argc is >= 0 + * it will be used; if it's < 0 then the last element in the arg vector + * must be NULL. + * + * This inserts a space between each argument. + * + * This does not automatically add double quotes around arguments with + * spaces in them. This practice is necessary for Win32, because Win32's + * CreateProcess call is stupid. + * + * The caller should delete[] the returned string. + */ +char* concatArgv(int argc, const char* const argv[]); + +/* + * Count up the number of arguments in "argv". The count does not include + * the final NULL entry. + */ +int countArgv(const char* const argv[]); + +/* + * Some utility functions for working with files. These could be made + * part of a "File" class. + */ +typedef enum FileType { + kFileTypeUnknown = 0, + kFileTypeNonexistent, // i.e. ENOENT + kFileTypeRegular, + kFileTypeDirectory, + kFileTypeCharDev, + kFileTypeBlockDev, + kFileTypeFifo, + kFileTypeSymlink, + kFileTypeSocket, +} FileType; +/* get the file's type; follows symlinks */ +FileType getFileType(const char* fileName); +/* get the file's modification date; returns -1 w/errno set on failure */ +time_t getFileModDate(const char* fileName); + +/* + * Round up to the nearest power of 2. Handy for hash tables. + */ +unsigned int roundUpPower2(unsigned int val); + +void strreverse(char* begin, char* end); +void k_itoa(int value, char* str, int base); +char* itoa(int val, int base); + +}; // namespace android + +#endif // _LIBS_UTILS_MISC_H diff --git a/include/utils/ported.h b/include/utils/ported.h new file mode 100644 index 000000000..eb3be01e9 --- /dev/null +++ b/include/utils/ported.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Standard functions ported to the current platform. Note these are NOT +// in the "android" namespace. +// +#ifndef _LIBS_UTILS_PORTED_H +#define _LIBS_UTILS_PORTED_H + +#include // for timeval + +#ifdef __cplusplus +extern "C" { +#endif + +/* library replacement functions */ +#if defined(NEED_GETTIMEOFDAY) +int gettimeofday(struct timeval* tv, struct timezone* tz); +#endif +#if defined(NEED_USLEEP) +void usleep(unsigned long usec); +#endif +#if defined(NEED_PIPE) +int pipe(int filedes[2]); +#endif +#if defined(NEED_SETENV) +int setenv(const char* name, const char* value, int overwrite); +void unsetenv(const char* name); +char* getenv(const char* name); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // _LIBS_UTILS_PORTED_H diff --git a/include/utils/string_array.h b/include/utils/string_array.h new file mode 100644 index 000000000..064dda224 --- /dev/null +++ b/include/utils/string_array.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Sortable array of strings. STL-ish, but STL-free. +// +#ifndef _LIBS_UTILS_STRING_ARRAY_H +#define _LIBS_UTILS_STRING_ARRAY_H + +#include +#include + +namespace android { + +// +// An expanding array of strings. Add, get, sort, delete. +// +class StringArray { +public: + StringArray() + : mMax(0), mCurrent(0), mArray(NULL) + {} + virtual ~StringArray() { + for (int i = 0; i < mCurrent; i++) + delete[] mArray[i]; + delete[] mArray; + } + + // + // Add a string. A copy of the string is made. + // + bool push_back(const char* str) { + if (mCurrent >= mMax) { + char** tmp; + + if (mMax == 0) + mMax = 16; // initial storage + else + mMax *= 2; + + tmp = new char*[mMax]; + if (tmp == NULL) + return false; + + memcpy(tmp, mArray, mCurrent * sizeof(char*)); + delete[] mArray; + mArray = tmp; + } + + int len = strlen(str); + mArray[mCurrent] = new char[len+1]; + memcpy(mArray[mCurrent], str, len+1); + mCurrent++; + + return true; + } + + // + // Delete an entry. + // + void erase(int idx) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + if (idx < mCurrent-1) { + memmove(&mArray[idx], &mArray[idx+1], + (mCurrent-1 - idx) * sizeof(char*)); + } + mCurrent--; + } + + // + // Sort the array. + // + void sort(int (*compare)(const void*, const void*)) { + qsort(mArray, mCurrent, sizeof(char*), compare); + } + + // + // Pass this to the sort routine to do an ascending alphabetical sort. + // + static int cmpAscendingAlpha(const void* pstr1, const void* pstr2) { + return strcmp(*(const char**)pstr1, *(const char**)pstr2); + } + + // + // Get the #of items in the array. + // + inline int size(void) const { return mCurrent; } + + // + // Return entry N. + // [should use operator[] here] + // + const char* getEntry(int idx) const { + if (idx < 0 || idx >= mCurrent) + return NULL; + return mArray[idx]; + } + + // + // Set entry N to specified string. + // [should use operator[] here] + // + void setEntry(int idx, const char* str) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + int len = strlen(str); + mArray[idx] = new char[len+1]; + memcpy(mArray[idx], str, len+1); + } + +private: + int mMax; + int mCurrent; + char** mArray; +}; + +}; // namespace android + +#endif // _LIBS_UTILS_STRING_ARRAY_H diff --git a/include/utils/threads.h b/include/utils/threads.h new file mode 100644 index 000000000..7dca81004 --- /dev/null +++ b/include/utils/threads.h @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef _LIBS_UTILS_THREADS_H +#define _LIBS_UTILS_THREADS_H + +#include +#include +#include + +// ------------------------------------------------------------------ +// C API + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* android_thread_id_t; + +typedef int (*android_thread_func_t)(void*); + +enum { + /* + * *********************************************** + * ** Keep in sync with android.os.Process.java ** + * *********************************************** + * + * This maps directly to the "nice" priorites we use in Android. + * A thread priority should be chosen inverse-proportinally to + * the amount of work the thread is expected to do. The more work + * a thread will do, the less favorable priority it should get so that + * it doesn't starve the system. Threads not behaving properly might + * be "punished" by the kernel. + * Use the levels below when appropriate. Intermediate values are + * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below. + */ + ANDROID_PRIORITY_LOWEST = 19, + + /* use for background tasks */ + ANDROID_PRIORITY_BACKGROUND = 10, + + /* most threads run at normal priority */ + ANDROID_PRIORITY_NORMAL = 0, + + /* threads currently running a UI that the user is interacting with */ + ANDROID_PRIORITY_FOREGROUND = -2, + + /* the main UI thread has a slightly more favorable priority */ + ANDROID_PRIORITY_DISPLAY = -4, + + /* ui service treads might want to run at a urgent display (uncommon) */ + ANDROID_PRIORITY_URGENT_DISPLAY = -8, + + /* all normal audio threads */ + ANDROID_PRIORITY_AUDIO = -16, + + /* service audio threads (uncommon) */ + ANDROID_PRIORITY_URGENT_AUDIO = -19, + + /* should never be used in practice. regular process might not + * be allowed to use this level */ + ANDROID_PRIORITY_HIGHEST = -20, + + ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL, + ANDROID_PRIORITY_MORE_FAVORABLE = -1, + ANDROID_PRIORITY_LESS_FAVORABLE = +1, +}; + +// Create and run a new thread. +extern int androidCreateThread(android_thread_func_t, void *); + +// Create thread with lots of parameters +extern int androidCreateThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +// Get some sort of unique identifier for the current thread. +extern android_thread_id_t androidGetThreadId(); + +// Low-level thread creation -- never creates threads that can +// interact with the Java VM. +extern int androidCreateRawThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +// Used by the Java Runtime to control how threads are created, so that +// they can be proper and lovely Java threads. +typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +extern void androidSetCreateThreadFunc(android_create_thread_fn func); + +#ifdef __cplusplus +} +#endif + +// ------------------------------------------------------------------ +// C++ API + +#ifdef __cplusplus + +#include +#include +#include + +namespace android { + +typedef android_thread_id_t thread_id_t; + +typedef android_thread_func_t thread_func_t; + +enum { + PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST, + PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND, + PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL, + PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND, + PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY, + PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY, + PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO, + PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO, + PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST, + PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT, + PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE, + PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE, +}; + +// Create and run a new thread. +inline bool createThread(thread_func_t f, void *a) { + return androidCreateThread(f, a) ? true : false; +} + +// Create thread with lots of parameters +inline bool createThreadEtc(thread_func_t entryFunction, + void *userData, + const char* threadName = "android:unnamed_thread", + int32_t threadPriority = PRIORITY_DEFAULT, + size_t threadStackSize = 0, + thread_id_t *threadId = 0) +{ + return androidCreateThreadEtc(entryFunction, userData, threadName, + threadPriority, threadStackSize, threadId) ? true : false; +} + +// Get some sort of unique identifier for the current thread. +inline thread_id_t getThreadId() { + return androidGetThreadId(); +} + +/* + * Simple mutex class. The implementation is system-dependent. + * + * The mutex must be unlocked by the thread that locked it. They are not + * recursive, i.e. the same thread can't lock it multiple times. + */ +class Mutex { +public: + Mutex(); + Mutex(const char* name); + ~Mutex(); + + // lock or unlock the mutex + status_t lock(); + void unlock(); + + // lock if possible; returns 0 on success, error otherwise + status_t tryLock(); + + // Manages the mutex automatically. It'll be locked when Autolock is + // constructed and released when Autolock goes out of scope. + class Autolock { + public: + inline Autolock(Mutex& mutex) : mpMutex(&mutex) { mutex.lock(); } + inline Autolock(Mutex* mutex) : mpMutex(mutex) { mutex->lock(); } + inline ~Autolock() { mpMutex->unlock(); } + private: + Mutex* mpMutex; + }; + +private: + friend class Condition; + + // A mutex cannot be copied + Mutex(const Mutex&); + Mutex& operator = (const Mutex&); + void _init(); + + void* mState; +}; + +/* + * Automatic mutex. Declare one of these at the top of a function. + * When the function returns, it will go out of scope, and release the + * mutex. + */ + +typedef Mutex::Autolock AutoMutex; + + +/* + * Condition variable class. The implementation is system-dependent. + * + * Condition variables are paired up with mutexes. Lock the mutex, + * call wait(), then either re-wait() if things aren't quite what you want, + * or unlock the mutex and continue. All threads calling wait() must + * use the same mutex for a given Condition. + */ +class Condition { +public: + Condition(); + ~Condition(); + // Wait on the condition variable. Lock the mutex before calling. + status_t wait(Mutex& mutex); + // Wait on the condition variable until the given time. Lock the mutex + // before calling. + status_t wait(Mutex& mutex, nsecs_t abstime); + // same with relative timeout + status_t waitRelative(Mutex& mutex, nsecs_t reltime); + // Signal the condition variable, allowing one thread to continue. + void signal(); + // Signal the condition variable, allowing all threads to continue. + void broadcast(); + +private: + void* mState; +}; + + +/* + * Read/write lock. The resource can have multiple readers or one writer, + * but can't be read and written at the same time. + * + * The same thread should not call a lock function while it already has + * a lock. (Should be okay for multiple readers.) + */ +class ReadWriteLock { +public: + ReadWriteLock() + : mNumReaders(0), mNumWriters(0) + {} + ~ReadWriteLock() {} + + void lockForRead(); + bool tryLockForRead(); + void unlockForRead(); + + void lockForWrite(); + bool tryLockForWrite(); + void unlockForWrite(); + +private: + int mNumReaders; + int mNumWriters; + + Mutex mLock; + Condition mReadWaiter; + Condition mWriteWaiter; +#if defined(PRINT_RENDER_TIMES) + DurationTimer mDebugTimer; +#endif +}; + + +/* + * This is our spiffy thread object! + */ + +class Thread : virtual public RefBase +{ +public: + // Create a Thread object, but doesn't create or start the associated + // thread. See the run() method. + Thread(bool canCallJava = true); + virtual ~Thread(); + + // Start the thread in threadLoop() which needs to be implemented. + virtual status_t run( const char* name = 0, + int32_t priority = PRIORITY_DEFAULT, + size_t stack = 0); + + // Ask this object's thread to exit. This function is asynchronous, when the + // function returns the thread might still be running. Of course, this + // function can be called from a different thread. + virtual void requestExit(); + + // Good place to do one-time initializations + virtual status_t readyToRun(); + + // Call requestExit() and wait until this object's thread exits. + // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call + // this function from this object's thread. Will return WOULD_BLOCK in + // that case. + status_t requestExitAndWait(); + +protected: + // exitPending() returns true if requestExit() has been called. + bool exitPending() const; + +private: + // Derived class must implemtent threadLoop(). The thread starts its life + // here. There are two ways of using the Thread object: + // 1) loop: if threadLoop() returns true, it will be called again if + // requestExit() wasn't called. + // 2) once: if threadLoop() returns false, the thread will exit upon return. + virtual bool threadLoop() = 0; + +private: + Thread& operator=(const Thread&); + static int _threadLoop(void* user); + const bool mCanCallJava; + thread_id_t mThread; + Mutex mLock; + Condition mThreadExitedCondition; + status_t mStatus; + volatile bool mExitPending; + volatile bool mRunning; + sp mHoldSelf; +}; + + +}; // namespace android + +#endif // __cplusplus + +#endif // _LIBS_UTILS_THREADS_H diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp new file mode 100644 index 000000000..eb00f8cc9 --- /dev/null +++ b/libs/audioflinger/A2dpAudioInterface.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2008 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 + +#define LOG_NDEBUG 0 +#define LOG_TAG "A2dpAudioInterface" +#include +#include + +#include "A2dpAudioInterface.h" +#include "audio/liba2dp.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +A2dpAudioInterface::A2dpAudioInterface() : + mOutput(0) +{ +} + +A2dpAudioInterface::~A2dpAudioInterface() +{ + delete mOutput; +} + +status_t A2dpAudioInterface::initCheck() +{ + return 0; +} + +AudioStreamOut* A2dpAudioInterface::openOutputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status) +{ + LOGD("A2dpAudioInterface::openOutputStream %d, %d, %d\n", format, channelCount, sampleRate); + Mutex::Autolock lock(mLock); + status_t err = 0; + + // only one output stream allowed + if (mOutput) { + if (status) + *status = -1; + return NULL; + } + + // create new output stream + A2dpAudioStreamOut* out = new A2dpAudioStreamOut(); + if ((err = out->set(format, channelCount, sampleRate)) == NO_ERROR) { + mOutput = out; + } else { + delete out; + } + + if (status) + *status = err; + return mOutput; +} + +AudioStreamIn* A2dpAudioInterface::openInputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics) +{ + if (status) + *status = -1; + return NULL; +} + +status_t A2dpAudioInterface::setMicMute(bool state) +{ + return 0; +} + +status_t A2dpAudioInterface::getMicMute(bool* state) +{ + return 0; +} + +status_t A2dpAudioInterface::setParameter(const char *key, const char *value) +{ + LOGD("setParameter %s,%s\n", key, value); + + if (!key || !value) + return -EINVAL; + + if (strcmp(key, "a2dp_sink_address") == 0) { + return mOutput->setAddress(value); + } + if (strcmp(key, "bluetooth_enabled") == 0 && + strcmp(value, "false") == 0) { + return mOutput->close(); + } + + return 0; +} + +status_t A2dpAudioInterface::setVoiceVolume(float v) +{ + return 0; +} + +status_t A2dpAudioInterface::setMasterVolume(float v) +{ + return 0; +} + +status_t A2dpAudioInterface::doRouting() +{ + return 0; +} + +status_t A2dpAudioInterface::dump(int fd, const Vector& args) +{ + return 0; +} + +// ---------------------------------------------------------------------------- + +A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() : + mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL) +{ + // use any address by default + strncpy(mA2dpAddress, "00:00:00:00:00:00", sizeof(mA2dpAddress)); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::set( + int format, int channels, uint32_t rate) +{ + LOGD("A2dpAudioStreamOut::set %d, %d, %d\n", format, channels, rate); + + // fix up defaults + if (format == 0) format = AudioSystem::PCM_16_BIT; + if (channels == 0) channels = channelCount(); + if (rate == 0) rate = sampleRate(); + + // check values + if ((format != AudioSystem::PCM_16_BIT) || + (channels != channelCount()) || + (rate != sampleRate())) + return BAD_VALUE; + + return NO_ERROR; +} + +A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut() +{ + close(); +} + +ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes) +{ + status_t status = NO_INIT; + size_t remaining = bytes; + + if (!mData) { + status = a2dp_init(44100, 2, &mData); + if (status < 0) { + LOGE("a2dp_init failed err: %d\n", status); + mData = NULL; + goto Error; + } + a2dp_set_sink(mData, mA2dpAddress); + } + + while (remaining > 0) { + status = a2dp_write(mData, buffer, remaining); + if (status <= 0) { + LOGE("a2dp_write failed err: %d\n", status); + goto Error; + } + remaining -= status; + buffer = ((char *)buffer) + status; + } + + mStandby = false; + + return bytes; + +Error: + // Simulate audio output timing in case of error + usleep(bytes * 1000000 / frameSize() / sampleRate()); + + return status; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::standby() +{ + int result = 0; + + if (!mStandby) { + result = a2dp_stop(mData); + if (result == 0) + mStandby = true; + } + + return result; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address) +{ + if (strlen(address) < sizeof(mA2dpAddress)) + return -EINVAL; + + if (strcmp(address, mA2dpAddress)) { + strcpy(mA2dpAddress, address); + if (mData) + a2dp_set_sink(mData, mA2dpAddress); + } + + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::close() +{ + if (mData) { + a2dp_cleanup(mData); + mData = NULL; + } + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector& args) +{ + return NO_ERROR; +} + + +}; // namespace android diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h new file mode 100644 index 000000000..a56e8a0cd --- /dev/null +++ b/libs/audioflinger/A2dpAudioInterface.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef A2DP_AUDIO_HARDWARE_H +#define A2DP_AUDIO_HARDWARE_H + +#include +#include + +#include + +#include + + +namespace android { + +class A2dpAudioInterface : public AudioHardwareBase +{ + class A2dpAudioStreamOut; + +public: + A2dpAudioInterface(); + virtual ~A2dpAudioInterface(); + virtual status_t initCheck(); + + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + // Temporary interface, do not use + // TODO: Replace with a more generic key:value get/set mechanism + virtual status_t setParameter(const char *key, const char *value); + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + int format=0, + int channelCount=0, + uint32_t sampleRate=0, + status_t *status=0); + + virtual AudioStreamIn* openInputStream( + int format, + int channelCount, + uint32_t sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + +protected: + virtual status_t doRouting(); + virtual status_t dump(int fd, const Vector& args); + +private: + class A2dpAudioStreamOut : public AudioStreamOut { + public: + A2dpAudioStreamOut(); + virtual ~A2dpAudioStreamOut(); + status_t set(int format, + int channelCount, + uint32_t sampleRate); + virtual uint32_t sampleRate() const { return 44100; } + // SBC codec wants a multiple of 512 + virtual size_t bufferSize() const { return 512 * 20; } + virtual int channelCount() const { return 2; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return ((1000*bufferSize())/frameSize())/sampleRate() + 200; } + virtual status_t setVolume(float volume) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + status_t standby(); + status_t close(); + virtual status_t dump(int fd, const Vector& args); + + private: + friend class A2dpAudioInterface; + status_t setAddress(const char* address); + + private: + int mFd; + bool mStandby; + int mStartCount; + int mRetryCount; + char mA2dpAddress[20]; + void* mData; + }; + + Mutex mLock; + A2dpAudioStreamOut* mOutput; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // A2DP_AUDIO_HARDWARE_H diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk new file mode 100644 index 000000000..50d516b56 --- /dev/null +++ b/libs/audioflinger/Android.mk @@ -0,0 +1,56 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioHardwareGeneric.cpp \ + AudioHardwareStub.cpp \ + AudioDumpInterface.cpp \ + AudioHardwareInterface.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libmedia \ + libhardware_legacy + +ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) + LOCAL_CFLAGS += -DGENERIC_AUDIO +endif + +LOCAL_MODULE:= libaudiointerface + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioFlinger.cpp \ + AudioMixer.cpp.arm \ + AudioResampler.cpp.arm \ + AudioResamplerSinc.cpp.arm \ + AudioResamplerCubic.cpp.arm + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libmedia \ + libhardware_legacy + +ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) + LOCAL_STATIC_LIBRARIES += libaudiointerface +else + LOCAL_SHARED_LIBRARIES += libaudio +endif + +LOCAL_MODULE:= libaudioflinger + +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_SRC_FILES += A2dpAudioInterface.cpp + LOCAL_SHARED_LIBRARIES += liba2dp + LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP + LOCAL_C_INCLUDES += $(call include-path-for, bluez-libs) + LOCAL_C_INCLUDES += $(call include-path-for, bluez-utils) +endif + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/audioflinger/AudioBufferProvider.h b/libs/audioflinger/AudioBufferProvider.h new file mode 100644 index 000000000..1a467c744 --- /dev/null +++ b/libs/audioflinger/AudioBufferProvider.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_AUDIO_BUFFER_PROVIDER_H +#define ANDROID_AUDIO_BUFFER_PROVIDER_H + +#include +#include +#include + +namespace android { +// ---------------------------------------------------------------------------- + +class AudioBufferProvider +{ +public: + + struct Buffer { + union { + void* raw; + short* i16; + int8_t* i8; + }; + size_t frameCount; + }; + + virtual status_t getNextBuffer(Buffer* buffer) = 0; + virtual void releaseBuffer(Buffer* buffer) = 0; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_AUDIO_BUFFER_PROVIDER_H diff --git a/libs/audioflinger/AudioDumpInterface.cpp b/libs/audioflinger/AudioDumpInterface.cpp new file mode 100644 index 000000000..b4940cb1e --- /dev/null +++ b/libs/audioflinger/AudioDumpInterface.cpp @@ -0,0 +1,117 @@ +/* //device/servers/AudioFlinger/AudioDumpInterface.cpp +** +** Copyright 2008, 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. +*/ + +#define LOG_TAG "AudioFlingerDump" + +#include +#include +#include + +#include +#include + +#include "AudioDumpInterface.h" + +namespace android { + +bool gFirst = true; // true if first write after a standby + +// ---------------------------------------------------------------------------- + +AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) +{ + if(hw == 0) { + LOGE("Dump construct hw = 0"); + } + mFinalInterface = hw; + mStreamOut = 0; +} + + +AudioDumpInterface::~AudioDumpInterface() +{ + if(mFinalInterface) delete mFinalInterface; + if(mStreamOut) delete mStreamOut; +} + + +AudioStreamOut* AudioDumpInterface::openOutputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status) +{ + AudioStreamOut* outFinal = mFinalInterface->openOutputStream(format, channelCount, sampleRate, status); + + if(outFinal) { + mStreamOut = new AudioStreamOutDump(outFinal); + return mStreamOut; + } else { + LOGE("Dump outFinal=0"); + return 0; + } +} + +// ---------------------------------------------------------------------------- + +AudioStreamOutDump::AudioStreamOutDump( AudioStreamOut* finalStream) +{ + mFinalStream = finalStream; + mOutFile = 0; +} + + +AudioStreamOutDump::~AudioStreamOutDump() +{ + Close(); + delete mFinalStream; +} + +ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) +{ + ssize_t ret; + + ret = mFinalStream->write(buffer, bytes); + if(!mOutFile && gFirst) { + gFirst = false; + // check if dump file exist + mOutFile = fopen(FLINGER_DUMP_NAME, "r"); + if(mOutFile) { + fclose(mOutFile); + mOutFile = fopen(FLINGER_DUMP_NAME, "ab"); + } + } + if (mOutFile) { + fwrite(buffer, bytes, 1, mOutFile); + } + return ret; +} + +status_t AudioStreamOutDump::standby() +{ + Close(); + gFirst = true; + return mFinalStream->standby(); +} + + +void AudioStreamOutDump::Close(void) +{ + if(mOutFile) { + fclose(mOutFile); + mOutFile = 0; + } +} + +}; // namespace android diff --git a/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h new file mode 100644 index 000000000..9a941021e --- /dev/null +++ b/libs/audioflinger/AudioDumpInterface.h @@ -0,0 +1,97 @@ +/* //device/servers/AudioFlinger/AudioDumpInterface.h +** +** Copyright 2008, 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. +*/ + +#ifndef ANDROID_AUDIO_DUMP_INTERFACE_H +#define ANDROID_AUDIO_DUMP_INTERFACE_H + +#include +#include + +#include + +namespace android { + +#define FLINGER_DUMP_NAME "/data/FlingerOut.pcm" // name of file used for dump + +class AudioStreamOutDump : public AudioStreamOut { +public: + AudioStreamOutDump( AudioStreamOut* FinalStream); + ~AudioStreamOutDump(); + virtual ssize_t write(const void* buffer, size_t bytes); + + virtual uint32_t sampleRate() const { return mFinalStream->sampleRate(); } + virtual size_t bufferSize() const { return mFinalStream->bufferSize(); } + virtual int channelCount() const { return mFinalStream->channelCount(); } + virtual int format() const { return mFinalStream->format(); } + virtual uint32_t latency() const { return mFinalStream->latency(); } + virtual status_t setVolume(float volume) + { return mFinalStream->setVolume(volume); } + virtual status_t standby(); + virtual status_t dump(int fd, const Vector& args) { return mFinalStream->dump(fd, args); } + void Close(void); + +private: + AudioStreamOut *mFinalStream; + FILE *mOutFile; // output file +}; + + +class AudioDumpInterface : public AudioHardwareBase +{ + +public: + AudioDumpInterface(AudioHardwareInterface* hw); + virtual AudioStreamOut* openOutputStream( + int format=0, + int channelCount=0, + uint32_t sampleRate=0, + status_t *status=0); + virtual ~AudioDumpInterface(); + + virtual status_t initCheck() + {return mFinalInterface->initCheck();} + virtual status_t setVoiceVolume(float volume) + {return mFinalInterface->setVoiceVolume(volume);} + virtual status_t setMasterVolume(float volume) + {return mFinalInterface->setMasterVolume(volume);} + + // mic mute + virtual status_t setMicMute(bool state) + {return mFinalInterface->setMicMute(state);} + virtual status_t getMicMute(bool* state) + {return mFinalInterface->getMicMute(state);} + + virtual status_t setParameter(const char* key, const char* value) + {return mFinalInterface->setParameter(key, value);} + + virtual AudioStreamIn* openInputStream( int format, int channelCount, uint32_t sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics) + {return mFinalInterface->openInputStream( format, channelCount, sampleRate, status, acoustics);} + + virtual status_t dump(int fd, const Vector& args) { return mFinalInterface->dumpState(fd, args); } + +protected: + virtual status_t doRouting() {return mFinalInterface->setRouting(mMode, mRoutes[mMode]);} + + AudioHardwareInterface *mFinalInterface; + AudioStreamOutDump *mStreamOut; + +}; + +}; // namespace android + +#endif // ANDROID_AUDIO_DUMP_INTERFACE_H diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp new file mode 100644 index 000000000..92c40e90d --- /dev/null +++ b/libs/audioflinger/AudioFlinger.cpp @@ -0,0 +1,2474 @@ +/* //device/include/server/AudioFlinger/AudioFlinger.cpp +** +** Copyright 2007, 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. +*/ + + +#define LOG_TAG "AudioFlinger" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#include "AudioMixer.h" +#include "AudioFlinger.h" + +#ifdef WITH_A2DP +#include "A2dpAudioInterface.h" +#endif + +// ---------------------------------------------------------------------------- +// the sim build doesn't have gettid + +#ifndef HAVE_GETTID +# define gettid getpid +#endif + +// ---------------------------------------------------------------------------- + +namespace android { + +//static const nsecs_t kStandbyTimeInNsecs = seconds(3); +static const unsigned long kBufferRecoveryInUsecs = 2000; +static const unsigned long kMaxBufferRecoveryInUsecs = 20000; +static const float MAX_GAIN = 4096.0f; + +// retry counts for buffer fill timeout +// 50 * ~20msecs = 1 second +static const int8_t kMaxTrackRetries = 50; +static const int8_t kMaxTrackStartupRetries = 50; + +static const int kStartSleepTime = 30000; +static const int kStopSleepTime = 30000; + +// Maximum number of pending buffers allocated by OutputTrack::write() +static const uint8_t kMaxOutputTrackBuffers = 5; + + +#define AUDIOFLINGER_SECURITY_ENABLED 1 + +// ---------------------------------------------------------------------------- + +static bool recordingAllowed() { +#ifndef HAVE_ANDROID_OS + return true; +#endif +#if AUDIOFLINGER_SECURITY_ENABLED + if (getpid() == IPCThreadState::self()->getCallingPid()) return true; + bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO")); + if (!ok) LOGE("Request requires android.permission.RECORD_AUDIO"); + return ok; +#else + if (!checkCallingPermission(String16("android.permission.RECORD_AUDIO"))) + LOGW("WARNING: Need to add android.permission.RECORD_AUDIO to manifest"); + return true; +#endif +} + +static bool settingsAllowed() { +#ifndef HAVE_ANDROID_OS + return true; +#endif +#if AUDIOFLINGER_SECURITY_ENABLED + if (getpid() == IPCThreadState::self()->getCallingPid()) return true; + bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")); + if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS"); + return ok; +#else + if (!checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"))) + LOGW("WARNING: Need to add android.permission.MODIFY_AUDIO_SETTINGS to manifest"); + return true; +#endif +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::AudioFlinger() + : BnAudioFlinger(), + mAudioHardware(0), mA2dpAudioInterface(0), + mA2dpEnabled(false), mA2dpEnabledReq(false), + mForcedSpeakerCount(0), mForcedRoute(0), mRouteRestoreTime(0), mMusicMuteSaved(false) +{ + mHardwareStatus = AUDIO_HW_IDLE; + mAudioHardware = AudioHardwareInterface::create(); + mHardwareStatus = AUDIO_HW_INIT; + if (mAudioHardware->initCheck() == NO_ERROR) { + // open 16-bit output stream for s/w mixer + mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; + status_t status; + AudioStreamOut *hwOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); + mHardwareStatus = AUDIO_HW_IDLE; + if (hwOutput) { + mHardwareMixerThread = new MixerThread(this, hwOutput, AudioSystem::AUDIO_OUTPUT_HARDWARE); + } else { + LOGE("Failed to initialize hardware output stream, status: %d", status); + } + +#ifdef WITH_A2DP + // Create A2DP interface + mA2dpAudioInterface = new A2dpAudioInterface(); + AudioStreamOut *a2dpOutput = mA2dpAudioInterface->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); + if (a2dpOutput) { + mA2dpMixerThread = new MixerThread(this, a2dpOutput, AudioSystem::AUDIO_OUTPUT_A2DP); + if (hwOutput) { + uint32_t frameCount = ((a2dpOutput->bufferSize()/a2dpOutput->frameSize()) * hwOutput->sampleRate()) / a2dpOutput->sampleRate(); + MixerThread::OutputTrack *a2dpOutTrack = new MixerThread::OutputTrack(mA2dpMixerThread, + hwOutput->sampleRate(), + AudioSystem::PCM_16_BIT, + hwOutput->channelCount(), + frameCount); + mHardwareMixerThread->setOuputTrack(a2dpOutTrack); + } + } else { + LOGE("Failed to initialize A2DP output stream, status: %d", status); + } +#endif + + // FIXME - this should come from settings + setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); + setRouting(AudioSystem::MODE_RINGTONE, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); + setRouting(AudioSystem::MODE_IN_CALL, AudioSystem::ROUTE_EARPIECE, AudioSystem::ROUTE_ALL); + setMode(AudioSystem::MODE_NORMAL); + + setMasterVolume(1.0f); + setMasterMute(false); + + // Start record thread + mAudioRecordThread = new AudioRecordThread(mAudioHardware); + if (mAudioRecordThread != 0) { + mAudioRecordThread->run("AudioRecordThread", PRIORITY_URGENT_AUDIO); + } + } else { + LOGE("Couldn't even initialize the stubbed audio hardware!"); + } +} + +AudioFlinger::~AudioFlinger() +{ + if (mAudioRecordThread != 0) { + mAudioRecordThread->exit(); + mAudioRecordThread.clear(); + } + mHardwareMixerThread.clear(); + delete mAudioHardware; + // deleting mA2dpAudioInterface also deletes mA2dpOutput; +#ifdef WITH_A2DP + mA2dpMixerThread.clear(); + delete mA2dpAudioInterface; +#endif +} + + +#ifdef WITH_A2DP +void AudioFlinger::setA2dpEnabled(bool enable) +{ + LOGV_IF(enable, "set output to A2DP\n"); + LOGV_IF(!enable, "set output to hardware audio\n"); + + mA2dpEnabledReq = enable; + mA2dpMixerThread->wakeUp(); +} +#endif // WITH_A2DP + +bool AudioFlinger::streamForcedToSpeaker(int streamType) +{ + // NOTE that streams listed here must not be routed to A2DP by default: + // AudioSystem::routedToA2dpOutput(streamType) == false + return (streamType == AudioSystem::RING || + streamType == AudioSystem::ALARM || + streamType == AudioSystem::NOTIFICATION); +} + +status_t AudioFlinger::dumpClients(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + result.append("Clients:\n"); + for (size_t i = 0; i < mClients.size(); ++i) { + wp wClient = mClients.valueAt(i); + if (wClient != 0) { + sp client = wClient.promote(); + if (client != 0) { + snprintf(buffer, SIZE, " pid: %d\n", client->pid()); + result.append(buffer); + } + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + + +status_t AudioFlinger::dumpInternals(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Hardware status: %d\n", mHardwareStatus); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump AudioFlinger from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::dump(int fd, const Vector& args) +{ + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + dumpPermissionDenial(fd, args); + } else { + AutoMutex lock(&mLock); + + dumpClients(fd, args); + dumpInternals(fd, args); + mHardwareMixerThread->dump(fd, args); +#ifdef WITH_A2DP + mA2dpMixerThread->dump(fd, args); +#endif + + // dump record client + if (mAudioRecordThread != 0) mAudioRecordThread->dump(fd, args); + + if (mAudioHardware) { + mAudioHardware->dumpState(fd, args); + } + } + return NO_ERROR; +} + +// IAudioFlinger interface + + +sp AudioFlinger::createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp& sharedBuffer, + status_t *status) +{ + sp track; + sp trackHandle; + sp client; + wp wclient; + status_t lStatus; + + if (streamType >= AudioSystem::NUM_STREAM_TYPES) { + LOGE("invalid stream type"); + lStatus = BAD_VALUE; + goto Exit; + } + + { + Mutex::Autolock _l(mLock); + + wclient = mClients.valueFor(pid); + + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } +#ifdef WITH_A2DP + if (isA2dpEnabled() && AudioSystem::routedToA2dpOutput(streamType)) { + track = mA2dpMixerThread->createTrack(client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer, &lStatus); + } else +#endif + { + track = mHardwareMixerThread->createTrack(client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer, &lStatus); + } + if (track != NULL) { + trackHandle = new TrackHandle(track); + lStatus = NO_ERROR; + } + } + +Exit: + if(status) { + *status = lStatus; + } + return trackHandle; +} + +uint32_t AudioFlinger::sampleRate(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->sampleRate(); + } +#endif + return mHardwareMixerThread->sampleRate(); +} + +int AudioFlinger::channelCount(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->channelCount(); + } +#endif + return mHardwareMixerThread->channelCount(); +} + +int AudioFlinger::format(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->format(); + } +#endif + return mHardwareMixerThread->format(); +} + +size_t AudioFlinger::frameCount(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->frameCount(); + } +#endif + return mHardwareMixerThread->frameCount(); +} + +uint32_t AudioFlinger::latency(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->latency(); + } +#endif + return mHardwareMixerThread->latency(); +} + +status_t AudioFlinger::setMasterVolume(float value) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + // when hw supports master volume, don't scale in sw mixer + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + if (mAudioHardware->setMasterVolume(value) == NO_ERROR) { + value = 1.0f; + } + mHardwareStatus = AUDIO_HW_IDLE; + mHardwareMixerThread->setMasterVolume(value); +#ifdef WITH_A2DP + mA2dpMixerThread->setMasterVolume(value); +#endif + + return NO_ERROR; +} + +status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask) +{ + status_t err = NO_ERROR; + + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if ((mode < AudioSystem::MODE_CURRENT) || (mode >= AudioSystem::NUM_MODES)) { + LOGW("Illegal value: setRouting(%d, %u, %u)", mode, routes, mask); + return BAD_VALUE; + } + +#ifdef WITH_A2DP + LOGD("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), IPCThreadState::self()->getCallingPid()); + if (mode == AudioSystem::MODE_NORMAL && + (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) { + AutoMutex lock(&mLock); + + bool enableA2dp = false; + if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) { + enableA2dp = true; + } + setA2dpEnabled(enableA2dp); + LOGV("setOutput done\n"); + } +#endif + + // do nothing if only A2DP routing is affected + mask &= ~AudioSystem::ROUTE_BLUETOOTH_A2DP; + if (mask) { + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_GET_ROUTING; + uint32_t r; + err = mAudioHardware->getRouting(mode, &r); + if (err == NO_ERROR) { + r = (r & ~mask) | (routes & mask); + if (mode == AudioSystem::MODE_NORMAL || + (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { + mSavedRoute = r; + r |= mForcedRoute; + LOGV("setRouting mSavedRoute %08x mForcedRoute %08x\n", mSavedRoute, mForcedRoute); + } + mHardwareStatus = AUDIO_HW_SET_ROUTING; + err = mAudioHardware->setRouting(mode, r); + } + mHardwareStatus = AUDIO_HW_IDLE; + } + return err; +} + +uint32_t AudioFlinger::getRouting(int mode) const +{ + uint32_t routes = 0; + if ((mode >= AudioSystem::MODE_CURRENT) && (mode < AudioSystem::NUM_MODES)) { + if (mode == AudioSystem::MODE_NORMAL || + (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { + routes = mSavedRoute; + } else { + mHardwareStatus = AUDIO_HW_GET_ROUTING; + mAudioHardware->getRouting(mode, &routes); + mHardwareStatus = AUDIO_HW_IDLE; + } + } else { + LOGW("Illegal value: getRouting(%d)", mode); + } + return routes; +} + +status_t AudioFlinger::setMode(int mode) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) { + LOGW("Illegal value: setMode(%d)", mode); + return BAD_VALUE; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MODE; + status_t ret = mAudioHardware->setMode(mode); + mHardwareStatus = AUDIO_HW_IDLE; + return ret; +} + +int AudioFlinger::getMode() const +{ + int mode = AudioSystem::MODE_INVALID; + mHardwareStatus = AUDIO_HW_SET_MODE; + mAudioHardware->getMode(&mode); + mHardwareStatus = AUDIO_HW_IDLE; + return mode; +} + +status_t AudioFlinger::setMicMute(bool state) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MIC_MUTE; + status_t ret = mAudioHardware->setMicMute(state); + mHardwareStatus = AUDIO_HW_IDLE; + return ret; +} + +bool AudioFlinger::getMicMute() const +{ + bool state = AudioSystem::MODE_INVALID; + mHardwareStatus = AUDIO_HW_GET_MIC_MUTE; + mAudioHardware->getMicMute(&state); + mHardwareStatus = AUDIO_HW_IDLE; + return state; +} + +status_t AudioFlinger::setMasterMute(bool muted) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + mHardwareMixerThread->setMasterMute(muted); +#ifdef WITH_A2DP + mA2dpMixerThread->setMasterMute(muted); +#endif + return NO_ERROR; +} + +float AudioFlinger::masterVolume() const +{ + return mHardwareMixerThread->masterVolume(); +} + +bool AudioFlinger::masterMute() const +{ + return mHardwareMixerThread->masterMute(); +} + +status_t AudioFlinger::setStreamVolume(int stream, float value) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + + mHardwareMixerThread->setStreamVolume(stream, value); +#ifdef WITH_A2DP + mA2dpMixerThread->setStreamVolume(stream, value); +#endif + + status_t ret = NO_ERROR; + if (stream == AudioSystem::VOICE_CALL || + stream == AudioSystem::BLUETOOTH_SCO) { + + if (stream == AudioSystem::VOICE_CALL) { + value = (float)AudioSystem::logToLinear(value)/100.0f; + } else { // (type == AudioSystem::BLUETOOTH_SCO) + value = 1.0f; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_SET_VOICE_VOLUME; + ret = mAudioHardware->setVoiceVolume(value); + mHardwareStatus = AUDIO_HW_IDLE; + } + + return ret; +} + +status_t AudioFlinger::setStreamMute(int stream, bool muted) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + +#ifdef WITH_A2DP + mA2dpMixerThread->setStreamMute(stream, muted); +#endif + if (stream == AudioSystem::MUSIC) + { + AutoMutex lock(&mHardwareLock); + if (mForcedRoute != 0) + mMusicMuteSaved = muted; + else + mHardwareMixerThread->setStreamMute(stream, muted); + } else { + mHardwareMixerThread->setStreamMute(stream, muted); + } + + + + return NO_ERROR; +} + +float AudioFlinger::streamVolume(int stream) const +{ + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return 0.0f; + } + return mHardwareMixerThread->streamVolume(stream); +} + +bool AudioFlinger::streamMute(int stream) const +{ + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return true; + } + + if (stream == AudioSystem::MUSIC && mForcedRoute != 0) + { + return mMusicMuteSaved; + } + return mHardwareMixerThread->streamMute(stream); +} + +bool AudioFlinger::isMusicActive() const +{ + #ifdef WITH_A2DP + if (isA2dpEnabled()) { + return mA2dpMixerThread->isMusicActive(); + } + #endif + return mHardwareMixerThread->isMusicActive(); +} + +status_t AudioFlinger::setParameter(const char* key, const char* value) +{ + status_t result, result2; + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_SET_PARAMETER; + + LOGV("setParameter() key %s, value %s, tid %d, calling tid %d", key, value, gettid(), IPCThreadState::self()->getCallingPid()); + result = mAudioHardware->setParameter(key, value); + if (mA2dpAudioInterface) { + result2 = mA2dpAudioInterface->setParameter(key, value); + if (result2) + result = result2; + } + mHardwareStatus = AUDIO_HW_IDLE; + return result; +} + +size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); +} + +void AudioFlinger::registerClient(const sp& client) +{ + + LOGV("registerClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + + sp binder = client->asBinder(); + if (mNotificationClients.indexOf(binder) < 0) { + LOGV("Adding notification client %p", binder.get()); + binder->linkToDeath(this); + mNotificationClients.add(binder); + client->a2dpEnabledChanged(isA2dpEnabled()); + } +} + +void AudioFlinger::binderDied(const wp& who) { + + LOGV("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + + IBinder *binder = who.unsafe_get(); + + if (binder != NULL) { + int index = mNotificationClients.indexOf(binder); + if (index >= 0) { + LOGV("Removing notification client %p", binder); + mNotificationClients.removeAt(index); + } + } +} + +void AudioFlinger::handleOutputSwitch() +{ + if (mA2dpEnabled != mA2dpEnabledReq) + { + Mutex::Autolock _l(mLock); + + if (mA2dpEnabled != mA2dpEnabledReq) + { + mA2dpEnabled = mA2dpEnabledReq; + SortedVector < sp > tracks; + SortedVector < wp > activeTracks; + + // We hold mA2dpMixerThread mLock already + Mutex::Autolock _l(mHardwareMixerThread->mLock); + + // Transfer tracks playing on MUSIC stream from one mixer to the other + if (mA2dpEnabled) { + mHardwareMixerThread->getTracks(tracks, activeTracks); + mA2dpMixerThread->putTracks(tracks, activeTracks); + } else { + mA2dpMixerThread->getTracks(tracks, activeTracks); + mHardwareMixerThread->putTracks(tracks, activeTracks); + } + + // Notify AudioSystem of the A2DP activation/deactivation + size_t size = mNotificationClients.size(); + for (size_t i = 0; i < size; i++) { + sp binder = mNotificationClients.itemAt(i).promote(); + if (binder != NULL) { + LOGV("Notifying output change to client %p", binder.get()); + sp client = interface_cast (binder); + client->a2dpEnabledChanged(mA2dpEnabled); + } + } + + mHardwareMixerThread->wakeUp(); + } + } +} + +void AudioFlinger::removeClient(pid_t pid) +{ + LOGV("removeClient() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + mClients.removeItem(pid); +} + +void AudioFlinger::wakeUp() +{ + mHardwareMixerThread->wakeUp(); +#ifdef WITH_A2DP + mA2dpMixerThread->wakeUp(); +#endif // WITH_A2DP +} + +bool AudioFlinger::isA2dpEnabled() const +{ + return mA2dpEnabledReq; +} + +void AudioFlinger::handleForcedSpeakerRoute(int command) +{ + switch(command) { + case ACTIVE_TRACK_ADDED: + { + AutoMutex lock(mHardwareLock); + if (mForcedSpeakerCount++ == 0) { + mRouteRestoreTime = 0; + mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC); + if (mForcedRoute == 0 && !(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { + LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + mAudioHardware->setMasterVolume(0); + usleep(mHardwareMixerThread->latency()*1000); + mHardwareStatus = AUDIO_HW_SET_ROUTING; + mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareStatus = AUDIO_HW_IDLE; + // delay track start so that audio hardware has time to siwtch routes + usleep(kStartSleepTime); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + mAudioHardware->setMasterVolume(mHardwareMixerThread->masterVolume()); + mHardwareStatus = AUDIO_HW_IDLE; + } + mForcedRoute = AudioSystem::ROUTE_SPEAKER; + } + LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount); + } + break; + case ACTIVE_TRACK_REMOVED: + { + AutoMutex lock(mHardwareLock); + if (mForcedSpeakerCount > 0){ + if (--mForcedSpeakerCount == 0) { + mRouteRestoreTime = systemTime() + milliseconds(kStopSleepTime/1000); + } + LOGV("mForcedSpeakerCount decremented to %d", mForcedSpeakerCount); + } else { + LOGE("mForcedSpeakerCount is already zero"); + } + } + break; + case CHECK_ROUTE_RESTORE_TIME: + case FORCE_ROUTE_RESTORE: + if (mRouteRestoreTime) { + AutoMutex lock(mHardwareLock); + if (mRouteRestoreTime && + (systemTime() > mRouteRestoreTime || command == FORCE_ROUTE_RESTORE)) { + mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, mMusicMuteSaved); + mForcedRoute = 0; + if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { + mHardwareStatus = AUDIO_HW_SET_ROUTING; + mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute); + mHardwareStatus = AUDIO_HW_IDLE; + LOGV("Route forced to Speaker OFF %08x", mSavedRoute); + } + mRouteRestoreTime = 0; + } + } + break; + } +} + + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, AudioStreamOut* output, int outputType) + : Thread(false), + mAudioFlinger(audioFlinger), mAudioMixer(0), mOutput(output), mOutputType(outputType), + mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), mMixBuffer(0), + mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mStandby(false), + mInWrite(false) +{ + mSampleRate = output->sampleRate(); + mChannelCount = output->channelCount(); + + // FIXME - Current mixer implementation only supports stereo output + if (mChannelCount == 1) { + LOGE("Invalid audio hardware channel count"); + } + + mFormat = output->format(); + mFrameCount = output->bufferSize() / output->channelCount() / sizeof(int16_t); + mAudioMixer = new AudioMixer(mFrameCount, output->sampleRate()); + + // FIXME - Current mixer implementation only supports stereo output: Always + // Allocate a stereo buffer even if HW output is mono. + mMixBuffer = new int16_t[mFrameCount * 2]; + memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); +} + +AudioFlinger::MixerThread::~MixerThread() +{ + delete [] mMixBuffer; + delete mAudioMixer; +} + +status_t AudioFlinger::MixerThread::dump(int fd, const Vector& args) +{ + dumpInternals(fd, args); + dumpTracks(fd, args); + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Output %d mixer thread tracks\n", mOutputType); + result.append(buffer); + result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + for (size_t i = 0; i < mTracks.size(); ++i) { + wp wTrack = mTracks[i]; + if (wTrack != 0) { + sp track = wTrack.promote(); + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); + } + } + } + + snprintf(buffer, SIZE, "Output %d mixer thread active tracks\n", mOutputType); + result.append(buffer); + result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + for (size_t i = 0; i < mActiveTracks.size(); ++i) { + wp wTrack = mTracks[i]; + if (wTrack != 0) { + sp track = wTrack.promote(); + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); + } + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Output %d mixer thread internals\n", mOutputType); + result.append(buffer); + snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames()); + result.append(buffer); + snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); + result.append(buffer); + snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites); + result.append(buffer); + snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites); + result.append(buffer); + snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite); + result.append(buffer); + snprintf(buffer, SIZE, "standby: %d\n", mStandby); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// Thread virtuals +bool AudioFlinger::MixerThread::threadLoop() +{ + unsigned long sleepTime = kBufferRecoveryInUsecs; + int16_t* curBuf = mMixBuffer; + Vector< sp > tracksToRemove; + size_t enabledTracks = 0; + nsecs_t standbyTime = systemTime(); + size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t); + nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2; + +#ifdef WITH_A2DP + bool outputTrackActive = false; +#endif + + do { + enabledTracks = 0; + { // scope for the mLock + + Mutex::Autolock _l(mLock); + +#ifdef WITH_A2DP + if (mOutputType == AudioSystem::AUDIO_OUTPUT_A2DP) { + mAudioFlinger->handleOutputSwitch(); + } + if (mOutputTrack != NULL && !mAudioFlinger->isA2dpEnabled()) { + if (outputTrackActive) { + mOutputTrack->stop(); + outputTrackActive = false; + } + } +#endif + + const SortedVector< wp >& activeTracks = mActiveTracks; + + // put audio hardware into standby after short delay + if UNLIKELY(!activeTracks.size() && systemTime() > standbyTime) { + // wait until we have something to do... + LOGV("Audio hardware entering standby, output %d\n", mOutputType); +// mAudioFlinger->mHardwareStatus = AUDIO_HW_STANDBY; + if (!mStandby) { + mOutput->standby(); + mStandby = true; + } + +#ifdef WITH_A2DP + if (outputTrackActive) { + mOutputTrack->stop(); + outputTrackActive = false; + } +#endif + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + mAudioFlinger->handleForcedSpeakerRoute(FORCE_ROUTE_RESTORE); + } +// mHardwareStatus = AUDIO_HW_IDLE; + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + mWaitWorkCV.wait(mLock); + LOGV("Audio hardware exiting standby, output %d\n", mOutputType); + + if (mMasterMute == false) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + setMasterMute(true); + } + } + + standbyTime = systemTime() + kStandbyTimeInNsecs; + continue; + } + + // Forced route to speaker is handled by hardware mixer thread + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + mAudioFlinger->handleForcedSpeakerRoute(CHECK_ROUTE_RESTORE_TIME); + } + + // find out which tracks need to be processed + size_t count = activeTracks.size(); + for (size_t i=0 ; i t = activeTracks[i].promote(); + if (t == 0) continue; + + Track* const track = t.get(); + audio_track_cblk_t* cblk = track->cblk(); + + // The first time a track is added we wait + // for all its buffers to be filled before processing it + mAudioMixer->setActiveTrack(track->name()); + if (cblk->framesReady() && (track->isReady() || track->isStopped()) && + !track->isPaused()) + { + //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + + // compute volume for this track + int16_t left, right; + if (track->isMuted() || mMasterMute || track->isPausing()) { + left = right = 0; + if (track->isPausing()) { + LOGV("paused(%d)", track->name()); + track->setPaused(); + } + } else { + float typeVolume = mStreamTypes[track->type()].volume; + float v = mMasterVolume * typeVolume; + float v_clamped = v * cblk->volume[0]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + left = int16_t(v_clamped); + v_clamped = v * cblk->volume[1]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + right = int16_t(v_clamped); + } + + // XXX: these things DON'T need to be done each time + mAudioMixer->setBufferProvider(track); + mAudioMixer->enable(AudioMixer::MIXING); + + int param; + if ( track->mFillingUpStatus == Track::FS_FILLED) { + // no ramp for the first volume setting + track->mFillingUpStatus = Track::FS_ACTIVE; + if (track->mState == TrackBase::RESUMING) { + track->mState = TrackBase::ACTIVE; + param = AudioMixer::RAMP_VOLUME; + } else { + param = AudioMixer::VOLUME; + } + } else { + param = AudioMixer::RAMP_VOLUME; + } + mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); + mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::FORMAT, track->format()); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::CHANNEL_COUNT, track->channelCount()); + mAudioMixer->setParameter( + AudioMixer::RESAMPLE, + AudioMixer::SAMPLE_RATE, + int(cblk->sampleRate)); + + // reset retry count + track->mRetryCount = kMaxTrackRetries; + enabledTracks++; + } else { + //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); + if (track->isStopped()) { + track->reset(); + } + if (track->isTerminated() || track->isStopped() || track->isPaused()) { + // We have consumed all the buffers of this track. + // Remove it from the list of active tracks. + LOGV("remove(%d) from active list", track->name()); + tracksToRemove.add(track); + } else { + // No buffers for this track. Give it a few chances to + // fill a buffer, then remove it from active list. + if (--(track->mRetryCount) <= 0) { + LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); + tracksToRemove.add(track); + } + } + // LOGV("disable(%d)", track->name()); + mAudioMixer->disable(AudioMixer::MIXING); + } + } + + // remove all the tracks that need to be... + count = tracksToRemove.size(); + if (UNLIKELY(count)) { + for (size_t i=0 ; i& track = tracksToRemove[i]; + removeActiveTrack(track); + if (track->isTerminated()) { + mTracks.remove(track); + deleteTrackName(track->mName); + } + } + } + } + + if (LIKELY(enabledTracks)) { + // mix buffers... + mAudioMixer->process(curBuf); + +#ifdef WITH_A2DP + if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { + if (!outputTrackActive) { + LOGV("starting output track in mixer for output %d", mOutputType); + mOutputTrack->start(); + outputTrackActive = true; + } + mOutputTrack->write(curBuf, mFrameCount); + } +#endif + + // output audio to hardware + mLastWriteTime = systemTime(); + mInWrite = true; + mOutput->write(curBuf, mixBufferSize); + mNumWrites++; + mInWrite = false; + mStandby = false; + nsecs_t temp = systemTime(); + standbyTime = temp + kStandbyTimeInNsecs; + nsecs_t delta = temp - mLastWriteTime; + if (delta > maxPeriod) { + LOGW("write blocked for %llu msecs", ns2ms(delta)); + mNumDelayedWrites++; + } + sleepTime = kBufferRecoveryInUsecs; + } else { +#ifdef WITH_A2DP + if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { + if (outputTrackActive) { + mOutputTrack->write(curBuf, 0); + if (mOutputTrack->bufferQueueEmpty()) { + mOutputTrack->stop(); + outputTrackActive = false; + } else { + standbyTime = systemTime() + kStandbyTimeInNsecs; + } + } + } +#endif + // There was nothing to mix this round, which means all + // active tracks were late. Sleep a little bit to give + // them another chance. If we're too late, the audio + // hardware will zero-fill for us. + //LOGV("no buffers - usleep(%lu)", sleepTime); + usleep(sleepTime); + if (sleepTime < kMaxBufferRecoveryInUsecs) { + sleepTime += kBufferRecoveryInUsecs; + } + } + + // finally let go of all our tracks, without the lock held + // since we can't guarantee the destructors won't acquire that + // same lock. + tracksToRemove.clear(); + } while (true); + + return false; +} + +status_t AudioFlinger::MixerThread::readyToRun() +{ + if (mSampleRate == 0) { + LOGE("No working audio driver found."); + return NO_INIT; + } + LOGI("AudioFlinger's thread ready to run for output %d", mOutputType); + return NO_ERROR; +} + +void AudioFlinger::MixerThread::onFirstRef() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "Mixer Thread for output %d", mOutputType); + + run(buffer, ANDROID_PRIORITY_URGENT_AUDIO); +} + + +sp AudioFlinger::MixerThread::createTrack( + const sp& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp& sharedBuffer, + status_t *status) +{ + sp track; + status_t lStatus; + + // Resampler implementation limits input sampling rate to 2 x output sampling rate. + if (sampleRate > MAX_SAMPLE_RATE || sampleRate > mSampleRate*2) { + LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate); + lStatus = BAD_VALUE; + goto Exit; + } + + { + Mutex::Autolock _l(mLock); + + if (mSampleRate == 0) { + LOGE("Audio driver not initialized."); + lStatus = NO_INIT; + goto Exit; + } + + track = new Track(this, client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer); + if (track->getCblk() == NULL) { + track.clear(); + lStatus = NO_MEMORY; + goto Exit; + } + mTracks.add(track); + lStatus = NO_ERROR; + } + +Exit: + if(status) { + *status = lStatus; + } + return track; +} + +void AudioFlinger::MixerThread::getTracks( + SortedVector < sp >& tracks, + SortedVector < wp >& activeTracks) +{ + size_t size = mTracks.size(); + LOGV ("MixerThread::getTracks() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType, mTracks.size(), mActiveTracks.size()); + for (size_t i = 0; i < size; i++) { + sp t = mTracks[i]; + if (AudioSystem::routedToA2dpOutput(t->mStreamType)) { + tracks.add(t); + int j = mActiveTracks.indexOf(t); + if (j >= 0) { + t = mActiveTracks[j].promote(); + if (t != NULL) { + activeTracks.add(t); + } + } + } + } + + size = activeTracks.size(); + for (size_t i = 0; i < size; i++) { + removeActiveTrack(activeTracks[i]); + } + + size = tracks.size(); + for (size_t i = 0; i < size; i++) { + sp t = tracks[i]; + mTracks.remove(t); + deleteTrackName(t->name()); + } +} + +void AudioFlinger::MixerThread::putTracks( + SortedVector < sp >& tracks, + SortedVector < wp >& activeTracks) +{ + + LOGV ("MixerThread::putTracks() for output %d, tracks.size %d, activeTracks.size %d", mOutputType, tracks.size(), activeTracks.size()); + + size_t size = tracks.size(); + for (size_t i = 0; i < size ; i++) { + sp t = tracks[i]; + int name = getTrackName(); + + if (name < 0) return; + + t->mName = name; + t->mMixerThread = this; + mTracks.add(t); + + int j = activeTracks.indexOf(t); + if (j >= 0) { + addActiveTrack(t); + } + } +} + +uint32_t AudioFlinger::MixerThread::sampleRate() const +{ + return mSampleRate; +} + +int AudioFlinger::MixerThread::channelCount() const +{ + return mChannelCount; +} + +int AudioFlinger::MixerThread::format() const +{ + return mFormat; +} + +size_t AudioFlinger::MixerThread::frameCount() const +{ + return mFrameCount; +} + +uint32_t AudioFlinger::MixerThread::latency() const +{ + if (mOutput) { + return mOutput->latency(); + } + else { + return 0; + } +} + +status_t AudioFlinger::MixerThread::setMasterVolume(float value) +{ + mMasterVolume = value; + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::setMasterMute(bool muted) +{ + mMasterMute = muted; + return NO_ERROR; +} + +float AudioFlinger::MixerThread::masterVolume() const +{ + return mMasterVolume; +} + +bool AudioFlinger::MixerThread::masterMute() const +{ + return mMasterMute; +} + +status_t AudioFlinger::MixerThread::setStreamVolume(int stream, float value) +{ + mStreamTypes[stream].volume = value; + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::setStreamMute(int stream, bool muted) +{ + mStreamTypes[stream].mute = muted; + return NO_ERROR; +} + +float AudioFlinger::MixerThread::streamVolume(int stream) const +{ + return mStreamTypes[stream].volume; +} + +bool AudioFlinger::MixerThread::streamMute(int stream) const +{ + return mStreamTypes[stream].mute; +} + +bool AudioFlinger::MixerThread::isMusicActive() const +{ + size_t count = mActiveTracks.size(); + for (size_t i = 0 ; i < count ; ++i) { + sp t = mActiveTracks[i].promote(); + if (t == 0) continue; + Track* const track = t.get(); + if (t->mStreamType == AudioSystem::MUSIC) + return true; + } + return false; +} + +status_t AudioFlinger::MixerThread::addTrack(const sp& track) +{ + status_t status = ALREADY_EXISTS; + Mutex::Autolock _l(mLock); + + // here the track could be either new, or restarted + // in both cases "unstop" the track + if (track->isPaused()) { + track->mState = TrackBase::RESUMING; + LOGV("PAUSED => RESUMING (%d)", track->name()); + } else { + track->mState = TrackBase::ACTIVE; + LOGV("? => ACTIVE (%d)", track->name()); + } + // set retry count for buffer fill + track->mRetryCount = kMaxTrackStartupRetries; + if (mActiveTracks.indexOf(track) < 0) { + // the track is newly added, make sure it fills up all its + // buffers before playing. This is to ensure the client will + // effectively get the latency it requested. + track->mFillingUpStatus = Track::FS_FILLING; + track->mResetDone = false; + addActiveTrack(track); + status = NO_ERROR; + } + + LOGV("mWaitWorkCV.broadcast"); + mWaitWorkCV.broadcast(); + + return status; +} + +void AudioFlinger::MixerThread::removeTrack(wp track, int name) +{ + Mutex::Autolock _l(mLock); + sp t = track.promote(); + if (t!=NULL && (t->mState <= TrackBase::STOPPED)) { + remove_track_l(track, name); + } +} + +void AudioFlinger::MixerThread::remove_track_l(wp track, int name) +{ + sp t = track.promote(); + if (t!=NULL) { + t->reset(); + } + deleteTrackName(name); + removeActiveTrack(track); + mWaitWorkCV.broadcast(); +} + +void AudioFlinger::MixerThread::destroyTrack(const sp& track) +{ + // NOTE: We're acquiring a strong reference on the track before + // acquiring the lock, this is to make sure removing it from + // mTracks won't cause the destructor to be called while the lock is + // held (note that technically, 'track' could be a reference to an item + // in mTracks, which is why we need to do this). + sp keep(track); + Mutex::Autolock _l(mLock); + track->mState = TrackBase::TERMINATED; + if (mActiveTracks.indexOf(track) < 0) { + LOGV("remove track (%d) and delete from mixer", track->name()); + mTracks.remove(track); + deleteTrackName(keep->name()); + } +} + + +void AudioFlinger::MixerThread::addActiveTrack(const wp& t) +{ + mActiveTracks.add(t); + + // Force routing to speaker for certain stream types + // The forced routing to speaker is managed by hardware mixer + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + sp track = t.promote(); + if (track == NULL) return; + + if (streamForcedToSpeaker(track->type())) { + mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_ADDED); + } + } +} + +void AudioFlinger::MixerThread::removeActiveTrack(const wp& t) +{ + mActiveTracks.remove(t); + + // Force routing to speaker for certain stream types + // The forced routing to speaker is managed by hardware mixer + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + sp track = t.promote(); + if (track == NULL) return; + + if (streamForcedToSpeaker(track->type())) { + mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_REMOVED); + } + } +} + +int AudioFlinger::MixerThread::getTrackName() +{ + return mAudioMixer->getTrackName(); +} + +void AudioFlinger::MixerThread::deleteTrackName(int name) +{ + mAudioMixer->deleteTrackName(name); +} + +size_t AudioFlinger::MixerThread::getOutputFrameCount() +{ + return mOutput->bufferSize() / mOutput->channelCount() / sizeof(int16_t); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::TrackBase::TrackBase( + const sp& mixerThread, + const sp& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp& sharedBuffer) + : RefBase(), + mMixerThread(mixerThread), + mClient(client), + mStreamType(streamType), + mFrameCount(0), + mState(IDLE), + mClientTid(-1), + mFormat(format), + mFlags(flags & ~SYSTEM_FLAGS_MASK) +{ + mName = mixerThread->getTrackName(); + LOGV("TrackBase contructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + if (mName < 0) { + LOGE("no more track names availlable"); + return; + } + + LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); + + // LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); + size_t size = sizeof(audio_track_cblk_t); + size_t bufferSize = frameCount*channelCount*sizeof(int16_t); + if (sharedBuffer == 0) { + size += bufferSize; + } + + if (client != NULL) { + mCblkMemory = client->heap()->allocate(size); + if (mCblkMemory != 0) { + mCblk = static_cast(mCblkMemory->pointer()); + if (mCblk) { // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = channelCount; + if (sharedBuffer == 0) { + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + } else { + mBuffer = sharedBuffer->pointer(); + } + mBufferEnd = (uint8_t *)mBuffer + bufferSize; + } + } else { + LOGE("not enough memory for AudioTrack size=%u", size); + client->heap()->dump("AudioTrack"); + return; + } + } else { + mCblk = (audio_track_cblk_t *)(new uint8_t[size]); + if (mCblk) { // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = channelCount; + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + mBufferEnd = (uint8_t *)mBuffer + bufferSize; + } + } +} + +AudioFlinger::MixerThread::TrackBase::~TrackBase() +{ + if (mCblk) { + mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + } + mCblkMemory.clear(); // and free the shared memory + mClient.clear(); +} + +void AudioFlinger::MixerThread::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + buffer->raw = 0; + mFrameCount = buffer->frameCount; + step(); + buffer->frameCount = 0; +} + +bool AudioFlinger::MixerThread::TrackBase::step() { + bool result; + audio_track_cblk_t* cblk = this->cblk(); + + result = cblk->stepServer(mFrameCount); + if (!result) { + LOGV("stepServer failed acquiring cblk mutex"); + mFlags |= STEPSERVER_FAILED; + } + return result; +} + +void AudioFlinger::MixerThread::TrackBase::reset() { + audio_track_cblk_t* cblk = this->cblk(); + + cblk->user = 0; + cblk->server = 0; + cblk->userBase = 0; + cblk->serverBase = 0; + mFlags &= (uint32_t)(~SYSTEM_FLAGS_MASK); + LOGV("TrackBase::reset"); +} + +sp AudioFlinger::MixerThread::TrackBase::getCblk() const +{ + return mCblkMemory; +} + +int AudioFlinger::MixerThread::TrackBase::sampleRate() const { + return mCblk->sampleRate; +} + +int AudioFlinger::MixerThread::TrackBase::channelCount() const { + return mCblk->channels; +} + +void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { + audio_track_cblk_t* cblk = this->cblk(); + int16_t *bufferStart = (int16_t *)mBuffer + (offset-cblk->serverBase)*cblk->channels; + int16_t *bufferEnd = bufferStart + frames * cblk->channels; + + // Check validity of returned pointer in case the track control block would have been corrupted. + if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd) { + LOGW("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \ + server %d, serverBase %d, user %d, userBase %d", + bufferStart, bufferEnd, mBuffer, mBufferEnd, + cblk->server, cblk->serverBase, cblk->user, cblk->userBase); + return 0; + } + + return bufferStart; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::Track::Track( + const sp& mixerThread, + const sp& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp& sharedBuffer) + : TrackBase(mixerThread, client, streamType, sampleRate, format, channelCount, frameCount, 0, sharedBuffer) +{ + mVolume[0] = 1.0f; + mVolume[1] = 1.0f; + mMute = false; + mSharedBuffer = sharedBuffer; +} + +AudioFlinger::MixerThread::Track::~Track() +{ + wp weak(this); // never create a strong ref from the dtor + mState = TERMINATED; + mMixerThread->removeTrack(weak, mName); +} + +void AudioFlinger::MixerThread::Track::destroy() +{ + mMixerThread->destroyTrack(this); +} + +void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size) +{ + snprintf(buffer, size, " %5d %5d %3u %3u %3u %3u %1d %1d %1d %5u %5u %5u %04x %04x\n", + mName - AudioMixer::TRACK0, + (mClient == NULL) ? getpid() : mClient->pid(), + mStreamType, + mFormat, + mCblk->channels, + mFrameCount, + mState, + mMute, + mFillingUpStatus, + mCblk->sampleRate, + mCblk->volume[0], + mCblk->volume[1], + mCblk->server, + mCblk->user); +} + +status_t AudioFlinger::MixerThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + uint32_t framesReady; + uint32_t framesReq = buffer->frameCount; + + // Check if last stepServer failed, try to step now + if (mFlags & TrackBase::STEPSERVER_FAILED) { + if (!step()) goto getNextBuffer_exit; + LOGV("stepServer recovered"); + mFlags &= ~TrackBase::STEPSERVER_FAILED; + } + + framesReady = cblk->framesReady(); + + if (LIKELY(framesReady)) { + uint32_t s = cblk->server; + uint32_t bufferEnd = cblk->serverBase + cblk->frameCount; + + bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd; + if (framesReq > framesReady) { + framesReq = framesReady; + } + if (s + framesReq > bufferEnd) { + framesReq = bufferEnd - s; + } + + buffer->raw = getBuffer(s, framesReq); + if (buffer->raw == 0) goto getNextBuffer_exit; + + buffer->frameCount = framesReq; + return NO_ERROR; + } + +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; +} + +bool AudioFlinger::MixerThread::Track::isReady() const { + if (mFillingUpStatus != FS_FILLING) return true; + + if (mCblk->framesReady() >= mCblk->frameCount || + mCblk->forceReady) { + mFillingUpStatus = FS_FILLED; + mCblk->forceReady = 0; + LOGV("Track::isReady() track %d for output %d", mName, mMixerThread->mOutputType); + return true; + } + return false; +} + +status_t AudioFlinger::MixerThread::Track::start() +{ + LOGV("start(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); + mMixerThread->addTrack(this); + return NO_ERROR; +} + +void AudioFlinger::MixerThread::Track::stop() +{ + LOGV("stop(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); + Mutex::Autolock _l(mMixerThread->mLock); + if (mState > STOPPED) { + mState = STOPPED; + // If the track is not active (PAUSED and buffers full), flush buffers + if (mMixerThread->mActiveTracks.indexOf(this) < 0) { + reset(); + } + LOGV("(> STOPPED) => STOPPED (%d)", mName); + } +} + +void AudioFlinger::MixerThread::Track::pause() +{ + LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mMixerThread->mLock); + if (mState == ACTIVE || mState == RESUMING) { + mState = PAUSING; + LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName); + } +} + +void AudioFlinger::MixerThread::Track::flush() +{ + LOGV("flush(%d)", mName); + Mutex::Autolock _l(mMixerThread->mLock); + if (mState != STOPPED && mState != PAUSED && mState != PAUSING) { + return; + } + // No point remaining in PAUSED state after a flush => go to + // STOPPED state + mState = STOPPED; + + // NOTE: reset() will reset cblk->user and cblk->server with + // the risk that at the same time, the AudioMixer is trying to read + // data. In this case, getNextBuffer() would return a NULL pointer + // as audio buffer => the AudioMixer code MUST always test that pointer + // returned by getNextBuffer() is not NULL! + reset(); +} + +void AudioFlinger::MixerThread::Track::reset() +{ + // Do not reset twice to avoid discarding data written just after a flush and before + // the audioflinger thread detects the track is stopped. + if (!mResetDone) { + TrackBase::reset(); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + mCblk->forceReady = 0; + mFillingUpStatus = FS_FILLING; + mResetDone = true; + } +} + +void AudioFlinger::MixerThread::Track::mute(bool muted) +{ + mMute = muted; +} + +void AudioFlinger::MixerThread::Track::setVolume(float left, float right) +{ + mVolume[0] = left; + mVolume[1] = right; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::RecordTrack::RecordTrack( + const sp& mixerThread, + const sp& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags) + : TrackBase(mixerThread, client, streamType, sampleRate, format, + channelCount, frameCount, flags, 0), + mOverflow(false) +{ +} + +AudioFlinger::MixerThread::RecordTrack::~RecordTrack() +{ + mMixerThread->deleteTrackName(mName); +} + +status_t AudioFlinger::MixerThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + uint32_t framesAvail; + uint32_t framesReq = buffer->frameCount; + + // Check if last stepServer failed, try to step now + if (mFlags & TrackBase::STEPSERVER_FAILED) { + if (!step()) goto getNextBuffer_exit; + LOGV("stepServer recovered"); + mFlags &= ~TrackBase::STEPSERVER_FAILED; + } + + framesAvail = cblk->framesAvailable_l(); + + if (LIKELY(framesAvail)) { + uint32_t s = cblk->server; + uint32_t bufferEnd = cblk->serverBase + cblk->frameCount; + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + if (s + framesReq > bufferEnd) { + framesReq = bufferEnd - s; + } + + buffer->raw = getBuffer(s, framesReq); + if (buffer->raw == 0) goto getNextBuffer_exit; + + buffer->frameCount = framesReq; + return NO_ERROR; + } + +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; +} + +status_t AudioFlinger::MixerThread::RecordTrack::start() +{ + return mMixerThread->mAudioFlinger->startRecord(this); +} + +void AudioFlinger::MixerThread::RecordTrack::stop() +{ + mMixerThread->mAudioFlinger->stopRecord(this); + TrackBase::reset(); + // Force overerrun condition to avoid false overrun callback until first data is + // read from buffer + mCblk->flowControlFlag = 1; +} + + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::OutputTrack::OutputTrack( + const sp& mixerThread, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount) + : Track(mixerThread, NULL, AudioSystem::SYSTEM, sampleRate, format, channelCount, frameCount, NULL), + mOutputMixerThread(mixerThread) +{ + + mCblk->out = 1; + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->volume[0] = mCblk->volume[1] = 0x1000; + mOutBuffer.frameCount = 0; + mCblk->bufferTimeoutMs = 10; + + LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p", + mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd); + +} + +AudioFlinger::MixerThread::OutputTrack::~OutputTrack() +{ + stop(); +} + +status_t AudioFlinger::MixerThread::OutputTrack::start() +{ + status_t status = Track::start(); + + mRetryCount = 127; + return status; +} + +void AudioFlinger::MixerThread::OutputTrack::stop() +{ + Track::stop(); + clearBufferQueue(); + mOutBuffer.frameCount = 0; +} + +void AudioFlinger::MixerThread::OutputTrack::write(int16_t* data, uint32_t frames) +{ + Buffer *pInBuffer; + Buffer inBuffer; + uint32_t channels = mCblk->channels; + + inBuffer.frameCount = frames; + inBuffer.i16 = data; + + if (mCblk->user == 0) { + if (mOutputMixerThread->isMusicActive()) { + mCblk->forceReady = 1; + LOGV("OutputTrack::start() force ready"); + } else if (mCblk->frameCount > frames){ + if (mBufferQueue.size() < kMaxOutputTrackBuffers) { + uint32_t startFrames = (mCblk->frameCount - frames); + LOGV("OutputTrack::start() write %d frames", startFrames); + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[startFrames * channels]; + pInBuffer->frameCount = startFrames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + LOGW ("OutputTrack::write() no more buffers"); + } + } + } + + while (1) { + // First write pending buffers, then new data + if (mBufferQueue.size()) { + pInBuffer = mBufferQueue.itemAt(0); + } else { + pInBuffer = &inBuffer; + } + + if (pInBuffer->frameCount == 0) { + break; + } + + if (mOutBuffer.frameCount == 0) { + mOutBuffer.frameCount = pInBuffer->frameCount; + if (obtainBuffer(&mOutBuffer) == (status_t)AudioTrack::NO_MORE_BUFFERS) { + break; + } + } + + uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount; + memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t)); + mCblk->stepUser(outFrames); + pInBuffer->frameCount -= outFrames; + pInBuffer->i16 += outFrames * channels; + mOutBuffer.frameCount -= outFrames; + mOutBuffer.i16 += outFrames * channels; + + if (pInBuffer->frameCount == 0) { + if (mBufferQueue.size()) { + mBufferQueue.removeAt(0); + delete [] pInBuffer->mBuffer; + delete pInBuffer; + } else { + break; + } + } + } + + // If we could not write all frames, allocate a buffer and queue it for next time. + if (inBuffer.frameCount) { + if (mBufferQueue.size() < kMaxOutputTrackBuffers) { + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels]; + pInBuffer->frameCount = inBuffer.frameCount; + pInBuffer->i16 = pInBuffer->mBuffer; + memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + LOGW("OutputTrack::write() no more buffers"); + } + } + + // Calling write() with a 0 length buffer, means that no more data will be written: + // If no more buffers are pending, fill output track buffer to make sure it is started + // by output mixer. + if (frames == 0 && mBufferQueue.size() == 0 && mCblk->user < mCblk->frameCount) { + frames = mCblk->frameCount - mCblk->user; + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[frames * channels]; + pInBuffer->frameCount = frames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } + +} + +status_t AudioFlinger::MixerThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer) +{ + int active; + int timeout = 0; + status_t result; + audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = buffer->frameCount; + + LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server); + buffer->frameCount = 0; + + uint32_t framesAvail = cblk->framesAvailable(); + + if (framesAvail == 0) { + return AudioTrack::NO_MORE_BUFFERS; + } + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReq > bufferEnd) { + framesReq = bufferEnd - u; + } + + buffer->frameCount = framesReq; + buffer->raw = (void *)cblk->buffer(u); + return NO_ERROR; +} + + +void AudioFlinger::MixerThread::OutputTrack::clearBufferQueue() +{ + size_t size = mBufferQueue.size(); + Buffer *pBuffer; + + for (size_t i = 0; i < size; i++) { + pBuffer = mBufferQueue.itemAt(i); + delete [] pBuffer->mBuffer; + delete pBuffer; + } + mBufferQueue.clear(); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::Client::Client(const sp& audioFlinger, pid_t pid) + : RefBase(), + mAudioFlinger(audioFlinger), + mMemoryDealer(new MemoryDealer(1024*1024)), + mPid(pid) +{ + // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer +} + +AudioFlinger::Client::~Client() +{ + mAudioFlinger->removeClient(mPid); +} + +const sp& AudioFlinger::Client::heap() const +{ + return mMemoryDealer; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::TrackHandle::TrackHandle(const sp& track) + : BnAudioTrack(), + mTrack(track) +{ +} + +AudioFlinger::TrackHandle::~TrackHandle() { + // just stop the track on deletion, associated resources + // will be freed from the main thread once all pending buffers have + // been played. Unless it's not in the active track list, in which + // case we free everything now... + mTrack->destroy(); +} + +status_t AudioFlinger::TrackHandle::start() { + return mTrack->start(); +} + +void AudioFlinger::TrackHandle::stop() { + mTrack->stop(); +} + +void AudioFlinger::TrackHandle::flush() { + mTrack->flush(); +} + +void AudioFlinger::TrackHandle::mute(bool e) { + mTrack->mute(e); +} + +void AudioFlinger::TrackHandle::pause() { + mTrack->pause(); +} + +void AudioFlinger::TrackHandle::setVolume(float left, float right) { + mTrack->setVolume(left, right); +} + +sp AudioFlinger::TrackHandle::getCblk() const { + return mTrack->getCblk(); +} + +status_t AudioFlinger::TrackHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioTrack::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +sp AudioFlinger::openRecord( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + status_t *status) +{ + sp thread; + sp recordTrack; + sp recordHandle; + sp client; + wp wclient; + AudioStreamIn* input = 0; + int inFrameCount; + size_t inputBufferSize; + status_t lStatus; + + // check calling permissions + if (!recordingAllowed()) { + lStatus = PERMISSION_DENIED; + goto Exit; + } + + if (uint32_t(streamType) >= AudioRecord::NUM_STREAM_TYPES) { + LOGE("invalid stream type"); + lStatus = BAD_VALUE; + goto Exit; + } + + if (sampleRate > MAX_SAMPLE_RATE) { + LOGE("Sample rate out of range"); + lStatus = BAD_VALUE; + goto Exit; + } + + if (mAudioRecordThread == 0) { + LOGE("Audio record thread not started"); + lStatus = NO_INIT; + goto Exit; + } + + + // Check that audio input stream accepts requested audio parameters + inputBufferSize = mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); + if (inputBufferSize == 0) { + lStatus = BAD_VALUE; + LOGE("Bad audio input parameters: sampling rate %u, format %d, channels %d", sampleRate, format, channelCount); + goto Exit; + } + + // add client to list + { + Mutex::Autolock _l(mLock); + wclient = mClients.valueFor(pid); + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } + } + + // frameCount must be a multiple of input buffer size + inFrameCount = inputBufferSize/channelCount/sizeof(short); + frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount; + + // create new record track and pass to record thread + recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate, + format, channelCount, frameCount, flags); + if (recordTrack->getCblk() == NULL) { + recordTrack.clear(); + lStatus = NO_MEMORY; + goto Exit; + } + + // return to handle to client + recordHandle = new RecordHandle(recordTrack); + lStatus = NO_ERROR; + +Exit: + if (status) { + *status = lStatus; + } + return recordHandle; +} + +status_t AudioFlinger::startRecord(MixerThread::RecordTrack* recordTrack) { + if (mAudioRecordThread != 0) { + return mAudioRecordThread->start(recordTrack); + } + return NO_INIT; +} + +void AudioFlinger::stopRecord(MixerThread::RecordTrack* recordTrack) { + if (mAudioRecordThread != 0) { + mAudioRecordThread->stop(recordTrack); + } +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::RecordHandle::RecordHandle(const sp& recordTrack) + : BnAudioRecord(), + mRecordTrack(recordTrack) +{ +} + +AudioFlinger::RecordHandle::~RecordHandle() { + stop(); +} + +status_t AudioFlinger::RecordHandle::start() { + LOGV("RecordHandle::start()"); + return mRecordTrack->start(); +} + +void AudioFlinger::RecordHandle::stop() { + LOGV("RecordHandle::stop()"); + mRecordTrack->stop(); +} + +sp AudioFlinger::RecordHandle::getCblk() const { + return mRecordTrack->getCblk(); +} + +status_t AudioFlinger::RecordHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioRecord::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::AudioRecordThread::AudioRecordThread(AudioHardwareInterface* audioHardware) : + mAudioHardware(audioHardware), + mActive(false) +{ +} + +AudioFlinger::AudioRecordThread::~AudioRecordThread() +{ +} + +bool AudioFlinger::AudioRecordThread::threadLoop() +{ + LOGV("AudioRecordThread: start record loop"); + AudioBufferProvider::Buffer buffer; + int inBufferSize = 0; + int inFrameCount = 0; + AudioStreamIn* input = 0; + + mActive = 0; + + // start recording + while (!exitPending()) { + if (!mActive) { + mLock.lock(); + if (!mActive && !exitPending()) { + LOGV("AudioRecordThread: loop stopping"); + if (input) { + delete input; + input = 0; + } + mRecordTrack.clear(); + mStopped.signal(); + + mWaitWorkCV.wait(mLock); + + LOGV("AudioRecordThread: loop starting"); + if (mRecordTrack != 0) { + input = mAudioHardware->openInputStream(mRecordTrack->format(), + mRecordTrack->channelCount(), + mRecordTrack->sampleRate(), + &mStartStatus, + (AudioSystem::audio_in_acoustics)(mRecordTrack->mFlags >> 16)); + if (input != 0) { + inBufferSize = input->bufferSize(); + inFrameCount = inBufferSize/input->frameSize(); + } + } else { + mStartStatus = NO_INIT; + } + if (mStartStatus !=NO_ERROR) { + LOGW("record start failed, status %d", mStartStatus); + mActive = false; + mRecordTrack.clear(); + } + mWaitWorkCV.signal(); + } + mLock.unlock(); + } else if (mRecordTrack != 0) { + + buffer.frameCount = inFrameCount; + if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR)) { + LOGV("AudioRecordThread read: %d frames", buffer.frameCount); + ssize_t bytesRead = input->read(buffer.raw, inBufferSize); + if (bytesRead < 0) { + LOGE("Error reading audio input"); + sleep(1); + } + mRecordTrack->releaseBuffer(&buffer); + mRecordTrack->overflow(); + } + + // client isn't retrieving buffers fast enough + else { + if (!mRecordTrack->setOverflow()) + LOGW("AudioRecordThread: buffer overflow"); + // Release the processor for a while before asking for a new buffer. + // This will give the application more chance to read from the buffer and + // clear the overflow. + usleep(5000); + } + } + } + + + if (input) { + delete input; + } + mRecordTrack.clear(); + + return false; +} + +status_t AudioFlinger::AudioRecordThread::start(MixerThread::RecordTrack* recordTrack) +{ + LOGV("AudioRecordThread::start"); + AutoMutex lock(&mLock); + mActive = true; + // If starting the active track, just reset mActive in case a stop + // was pending and exit + if (recordTrack == mRecordTrack.get()) return NO_ERROR; + + if (mRecordTrack != 0) return -EBUSY; + + mRecordTrack = recordTrack; + + // signal thread to start + LOGV("Signal record thread"); + mWaitWorkCV.signal(); + mWaitWorkCV.wait(mLock); + LOGV("Record started, status %d", mStartStatus); + return mStartStatus; +} + +void AudioFlinger::AudioRecordThread::stop(MixerThread::RecordTrack* recordTrack) { + LOGV("AudioRecordThread::stop"); + AutoMutex lock(&mLock); + if (mActive && (recordTrack == mRecordTrack.get())) { + mActive = false; + mStopped.wait(mLock); + } +} + +void AudioFlinger::AudioRecordThread::exit() +{ + LOGV("AudioRecordThread::exit"); + { + AutoMutex lock(&mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +status_t AudioFlinger::AudioRecordThread::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + pid_t pid = 0; + + if (mRecordTrack != 0 && mRecordTrack->mClient != 0) { + snprintf(buffer, SIZE, "Record client pid: %d\n", mRecordTrack->mClient->pid()); + result.append(buffer); + } else { + result.append("No record client\n"); + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioFlinger::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- +void AudioFlinger::instantiate() { + defaultServiceManager()->addService( + String16("media.audio_flinger"), new AudioFlinger()); +} + +}; // namespace android diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h new file mode 100644 index 000000000..77f064b23 --- /dev/null +++ b/libs/audioflinger/AudioFlinger.h @@ -0,0 +1,637 @@ +/* //device/include/server/AudioFlinger/AudioFlinger.h +** +** Copyright 2007, 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. +*/ + +#ifndef ANDROID_AUDIO_FLINGER_H +#define ANDROID_AUDIO_FLINGER_H + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "AudioBufferProvider.h" + +namespace android { + +class audio_track_cblk_t; +class AudioMixer; +class AudioBuffer; + + +// ---------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + + +// ---------------------------------------------------------------------------- + +static const nsecs_t kStandbyTimeInNsecs = seconds(3); + +class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient +{ +public: + static void instantiate(); + + virtual status_t dump(int fd, const Vector& args); + + // IAudioFlinger interface + virtual sp createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp& sharedBuffer, + status_t *status); + + virtual uint32_t sampleRate(int output) const; + virtual int channelCount(int output) const; + virtual int format(int output) const; + virtual size_t frameCount(int output) const; + virtual uint32_t latency(int output) const; + + virtual status_t setMasterVolume(float value); + virtual status_t setMasterMute(bool muted); + + virtual float masterVolume() const; + virtual bool masterMute() const; + + virtual status_t setStreamVolume(int stream, float value); + virtual status_t setStreamMute(int stream, bool muted); + + virtual float streamVolume(int stream) const; + virtual bool streamMute(int stream) const; + + virtual status_t setRouting(int mode, uint32_t routes, uint32_t mask); + virtual uint32_t getRouting(int mode) const; + + virtual status_t setMode(int mode); + virtual int getMode() const; + + virtual status_t setMicMute(bool state); + virtual bool getMicMute() const; + + virtual bool isMusicActive() const; + + virtual bool isA2dpEnabled() const; + + virtual status_t setParameter(const char* key, const char* value); + + virtual void registerClient(const sp& client); + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + + virtual void wakeUp(); + + // IBinder::DeathRecipient + virtual void binderDied(const wp& who); + + enum hardware_call_state { + AUDIO_HW_IDLE = 0, + AUDIO_HW_INIT, + AUDIO_HW_OUTPUT_OPEN, + AUDIO_HW_OUTPUT_CLOSE, + AUDIO_HW_INPUT_OPEN, + AUDIO_HW_INPUT_CLOSE, + AUDIO_HW_STANDBY, + AUDIO_HW_SET_MASTER_VOLUME, + AUDIO_HW_GET_ROUTING, + AUDIO_HW_SET_ROUTING, + AUDIO_HW_GET_MODE, + AUDIO_HW_SET_MODE, + AUDIO_HW_GET_MIC_MUTE, + AUDIO_HW_SET_MIC_MUTE, + AUDIO_SET_VOICE_VOLUME, + AUDIO_SET_PARAMETER, + }; + + // record interface + virtual sp openRecord( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + status_t *status); + + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags); + +private: + AudioFlinger(); + virtual ~AudioFlinger(); + + void setOutput(int outputType); + void doSetOutput(int outputType); + +#ifdef WITH_A2DP + void setA2dpEnabled(bool enable); +#endif + static bool streamForcedToSpeaker(int streamType); + + // Management of forced route to speaker for certain track types. + enum force_speaker_command { + ACTIVE_TRACK_ADDED = 0, + ACTIVE_TRACK_REMOVED, + CHECK_ROUTE_RESTORE_TIME, + FORCE_ROUTE_RESTORE + }; + void handleForcedSpeakerRoute(int command); + + // Internal dump utilites. + status_t dumpPermissionDenial(int fd, const Vector& args); + status_t dumpClients(int fd, const Vector& args); + status_t dumpInternals(int fd, const Vector& args); + + // --- Client --- + class Client : public RefBase { + public: + Client(const sp& audioFlinger, pid_t pid); + virtual ~Client(); + const sp& heap() const; + pid_t pid() const { return mPid; } + private: + Client(const Client&); + Client& operator = (const Client&); + sp mAudioFlinger; + sp mMemoryDealer; + pid_t mPid; + }; + + + class TrackHandle; + class RecordHandle; + class AudioRecordThread; + + + // --- MixerThread --- + class MixerThread : public Thread { + public: + + // --- Track --- + + // base for record and playback + class TrackBase : public AudioBufferProvider, public RefBase { + + public: + enum track_state { + IDLE, + TERMINATED, + STOPPED, + RESUMING, + ACTIVE, + PAUSING, + PAUSED + }; + + enum track_flags { + STEPSERVER_FAILED = 0x01, // StepServer could not acquire cblk->lock mutex + SYSTEM_FLAGS_MASK = 0x0000ffffUL, + // The upper 16 bits are used for track-specific flags. + }; + + TrackBase(const sp& mixerThread, + const sp& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp& sharedBuffer); + ~TrackBase(); + + virtual status_t start() = 0; + virtual void stop() = 0; + sp getCblk() const; + + protected: + friend class MixerThread; + friend class RecordHandle; + friend class AudioRecordThread; + + TrackBase(const TrackBase&); + TrackBase& operator = (const TrackBase&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0; + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + + audio_track_cblk_t* cblk() const { + return mCblk; + } + + int type() const { + return mStreamType; + } + + int format() const { + return mFormat; + } + + int channelCount() const ; + + int sampleRate() const; + + void* getBuffer(uint32_t offset, uint32_t frames) const; + + int name() const { + return mName; + } + + bool isStopped() const { + return mState == STOPPED; + } + + bool isTerminated() const { + return mState == TERMINATED; + } + + bool step(); + void reset(); + + sp mMixerThread; + sp mClient; + sp mCblkMemory; + audio_track_cblk_t* mCblk; + int mStreamType; + void* mBuffer; + void* mBufferEnd; + uint32_t mFrameCount; + int mName; + // we don't really need a lock for these + int mState; + int mClientTid; + uint8_t mFormat; + uint32_t mFlags; + }; + + // playback track + class Track : public TrackBase { + public: + Track( const sp& mixerThread, + const sp& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp& sharedBuffer); + ~Track(); + + void dump(char* buffer, size_t size); + virtual status_t start(); + virtual void stop(); + void pause(); + + void flush(); + void destroy(); + void mute(bool); + void setVolume(float left, float right); + + protected: + friend class MixerThread; + friend class AudioFlinger; + friend class AudioFlinger::TrackHandle; + + Track(const Track&); + Track& operator = (const Track&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + + bool isMuted() const { + return (mMute || mMixerThread->mStreamTypes[mStreamType].mute); + } + + bool isPausing() const { + return mState == PAUSING; + } + + bool isPaused() const { + return mState == PAUSED; + } + + bool isReady() const; + + void setPaused() { mState = PAUSED; } + void reset(); + + // we don't really need a lock for these + float mVolume[2]; + volatile bool mMute; + // FILLED state is used for suppressing volume ramp at begin of playing + enum {FS_FILLING, FS_FILLED, FS_ACTIVE}; + mutable uint8_t mFillingUpStatus; + int8_t mRetryCount; + sp mSharedBuffer; + bool mResetDone; + }; // end of Track + + // record track + class RecordTrack : public TrackBase { + public: + RecordTrack(const sp& mixerThread, + const sp& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags); + ~RecordTrack(); + + virtual status_t start(); + virtual void stop(); + + bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } + bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; } + + private: + friend class AudioFlinger; + friend class AudioFlinger::RecordHandle; + friend class AudioFlinger::AudioRecordThread; + friend class MixerThread; + + RecordTrack(const Track&); + RecordTrack& operator = (const Track&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + + bool mOverflow; + }; + + // playback track + class OutputTrack : public Track { + public: + + class Buffer: public AudioBufferProvider::Buffer { + public: + int16_t *mBuffer; + }; + + OutputTrack( const sp& mixerThread, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount); + ~OutputTrack(); + + virtual status_t start(); + virtual void stop(); + void write(int16_t* data, uint32_t frames); + bool bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; } + + private: + + status_t obtainBuffer(AudioBufferProvider::Buffer* buffer); + void clearBufferQueue(); + + sp mOutputMixerThread; + Vector < Buffer* > mBufferQueue; + AudioBufferProvider::Buffer mOutBuffer; + uint32_t mFramesWritten; + + }; // end of OutputTrack + + MixerThread (const sp& audioFlinger, AudioStreamOut* output, int outputType); + virtual ~MixerThread(); + + virtual status_t dump(int fd, const Vector& args); + + // Thread virtuals + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + + virtual uint32_t sampleRate() const; + virtual int channelCount() const; + virtual int format() const; + virtual size_t frameCount() const; + virtual uint32_t latency() const; + + virtual status_t setMasterVolume(float value); + virtual status_t setMasterMute(bool muted); + + virtual float masterVolume() const; + virtual bool masterMute() const; + + virtual status_t setStreamVolume(int stream, float value); + virtual status_t setStreamMute(int stream, bool muted); + + virtual float streamVolume(int stream) const; + virtual bool streamMute(int stream) const; + + bool isMusicActive() const; + + + sp createTrack( + const sp& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp& sharedBuffer, + status_t *status); + + void wakeUp() { mWaitWorkCV.broadcast(); } + + void getTracks(SortedVector < sp >& tracks, + SortedVector < wp >& activeTracks); + void putTracks(SortedVector < sp >& tracks, + SortedVector < wp >& activeTracks); + void setOuputTrack(OutputTrack *track) { mOutputTrack = track; } + + struct stream_type_t { + stream_type_t() + : volume(1.0f), + mute(false) + { + } + float volume; + bool mute; + }; + + private: + + + friend class AudioFlinger; + friend class Track; + friend class TrackBase; + friend class RecordTrack; + + MixerThread(const Client&); + MixerThread& operator = (const MixerThread&); + + status_t addTrack(const sp& track); + void removeTrack(wp track, int name); + void remove_track_l(wp track, int name); + void destroyTrack(const sp& track); + int getTrackName(); + void deleteTrackName(int name); + void addActiveTrack(const wp& t); + void removeActiveTrack(const wp& t); + size_t getOutputFrameCount(); + + status_t dumpInternals(int fd, const Vector& args); + status_t dumpTracks(int fd, const Vector& args); + + sp mAudioFlinger; + mutable Mutex mLock; + mutable Condition mWaitWorkCV; + SortedVector< wp > mActiveTracks; + SortedVector< sp > mTracks; + stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES]; + AudioMixer* mAudioMixer; + AudioStreamOut* mOutput; + int mOutputType; + uint32_t mSampleRate; + size_t mFrameCount; + int mChannelCount; + int mFormat; + int16_t* mMixBuffer; + float mMasterVolume; + bool mMasterMute; + nsecs_t mLastWriteTime; + int mNumWrites; + int mNumDelayedWrites; + bool mStandby; + bool mInWrite; + sp mOutputTrack; + }; + + + friend class AudioBuffer; + + class TrackHandle : public android::BnAudioTrack { + public: + TrackHandle(const sp& track); + virtual ~TrackHandle(); + virtual status_t start(); + virtual void stop(); + virtual void flush(); + virtual void mute(bool); + virtual void pause(); + virtual void setVolume(float left, float right); + virtual sp getCblk() const; + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + private: + sp mTrack; + }; + + friend class Client; + friend class MixerThread::Track; + + + void removeClient(pid_t pid); + + + + class RecordHandle : public android::BnAudioRecord { + public: + RecordHandle(const sp& recordTrack); + virtual ~RecordHandle(); + virtual status_t start(); + virtual void stop(); + virtual sp getCblk() const; + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + private: + sp mRecordTrack; + }; + + // record thread + class AudioRecordThread : public Thread + { + public: + AudioRecordThread(AudioHardwareInterface* audioHardware); + virtual ~AudioRecordThread(); + virtual bool threadLoop(); + virtual status_t readyToRun() { return NO_ERROR; } + virtual void onFirstRef() {} + + status_t start(MixerThread::RecordTrack* recordTrack); + void stop(MixerThread::RecordTrack* recordTrack); + void exit(); + status_t dump(int fd, const Vector& args); + + private: + AudioRecordThread(); + AudioHardwareInterface *mAudioHardware; + sp mRecordTrack; + Mutex mLock; + Condition mWaitWorkCV; + Condition mStopped; + volatile bool mActive; + status_t mStartStatus; + }; + + friend class AudioRecordThread; + friend class MixerThread; + + status_t startRecord(MixerThread::RecordTrack* recordTrack); + void stopRecord(MixerThread::RecordTrack* recordTrack); + + void handleOutputSwitch(); + + mutable Mutex mHardwareLock; + mutable Mutex mLock; + DefaultKeyedVector< pid_t, wp > mClients; + + sp mA2dpMixerThread; + sp mHardwareMixerThread; + AudioHardwareInterface* mAudioHardware; + AudioHardwareInterface* mA2dpAudioInterface; + sp mAudioRecordThread; + bool mA2dpEnabled; + bool mA2dpEnabledReq; + mutable int mHardwareStatus; + SortedVector< wp > mNotificationClients; + int mForcedSpeakerCount; + uint32_t mSavedRoute; + uint32_t mForcedRoute; + nsecs_t mRouteRestoreTime; + bool mMusicMuteSaved; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_FLINGER_H diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp new file mode 100644 index 000000000..62beadabd --- /dev/null +++ b/libs/audioflinger/AudioHardwareGeneric.cpp @@ -0,0 +1,313 @@ +/* +** +** Copyright 2007, 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 +#include +#include +#include + +#define LOG_TAG "AudioHardware" +#include +#include + +#include "AudioHardwareGeneric.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static char const * const kAudioDeviceName = "/dev/eac"; + +// ---------------------------------------------------------------------------- + +AudioHardwareGeneric::AudioHardwareGeneric() + : mOutput(0), mInput(0), mFd(-1), mMicMute(false) +{ + mFd = ::open(kAudioDeviceName, O_RDWR); +} + +AudioHardwareGeneric::~AudioHardwareGeneric() +{ + if (mFd >= 0) ::close(mFd); + delete mOutput; + delete mInput; +} + +status_t AudioHardwareGeneric::initCheck() +{ + if (mFd >= 0) { + if (::access(kAudioDeviceName, O_RDWR) == NO_ERROR) + return NO_ERROR; + } + return NO_INIT; +} + +AudioStreamOut* AudioHardwareGeneric::openOutputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status) +{ + AutoMutex lock(mLock); + + // only one output stream allowed + if (mOutput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } + + // create new output stream + AudioStreamOutGeneric* out = new AudioStreamOutGeneric(); + status_t lStatus = out->set(this, mFd, format, channelCount, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { + mOutput = out; + } else { + delete out; + } + return mOutput; +} + +void AudioHardwareGeneric::closeOutputStream(AudioStreamOutGeneric* out) { + if (out == mOutput) mOutput = 0; +} + +AudioStreamIn* AudioHardwareGeneric::openInputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics) +{ + AutoMutex lock(mLock); + + // only one input stream allowed + if (mInput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } + + // create new output stream + AudioStreamInGeneric* in = new AudioStreamInGeneric(); + status_t lStatus = in->set(this, mFd, format, channelCount, sampleRate, acoustics); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { + mInput = in; + } else { + delete in; + } + return mInput; +} + +void AudioHardwareGeneric::closeInputStream(AudioStreamInGeneric* in) { + if (in == mInput) mInput = 0; +} + +status_t AudioHardwareGeneric::setVoiceVolume(float v) +{ + // Implement: set voice volume + return NO_ERROR; +} + +status_t AudioHardwareGeneric::setMasterVolume(float v) +{ + // Implement: set master volume + // return error - software mixer will handle it + return INVALID_OPERATION; +} + +status_t AudioHardwareGeneric::setMicMute(bool state) +{ + mMicMute = state; + return NO_ERROR; +} + +status_t AudioHardwareGeneric::getMicMute(bool* state) +{ + *state = mMicMute; + return NO_ERROR; +} + +status_t AudioHardwareGeneric::dumpInternals(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioHardwareGeneric::dumpInternals\n"); + snprintf(buffer, SIZE, "\tmFd: %d mMicMute: %s\n", mFd, mMicMute? "true": "false"); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioHardwareGeneric::dump(int fd, const Vector& args) +{ + dumpInternals(fd, args); + if (mInput) { + mInput->dump(fd, args); + } + if (mOutput) { + mOutput->dump(fd, args); + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamOutGeneric::set( + AudioHardwareGeneric *hw, + int fd, + int format, + int channels, + uint32_t rate) +{ + // fix up defaults + if (format == 0) format = AudioSystem::PCM_16_BIT; + if (channels == 0) channels = channelCount(); + if (rate == 0) rate = sampleRate(); + + // check values + if ((format != AudioSystem::PCM_16_BIT) || + (channels != channelCount()) || + (rate != sampleRate())) + return BAD_VALUE; + + mAudioHardware = hw; + mFd = fd; + return NO_ERROR; +} + +AudioStreamOutGeneric::~AudioStreamOutGeneric() +{ + if (mAudioHardware) + mAudioHardware->closeOutputStream(this); +} + +ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes) +{ + Mutex::Autolock _l(mLock); + return ssize_t(::write(mFd, buffer, bytes)); +} + +status_t AudioStreamOutGeneric::standby() +{ + // Implement: audio hardware to standby mode + return NO_ERROR; +} + +status_t AudioStreamOutGeneric::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamOutGeneric::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +// record functions +status_t AudioStreamInGeneric::set( + AudioHardwareGeneric *hw, + int fd, + int format, + int channels, + uint32_t rate, + AudioSystem::audio_in_acoustics acoustics) +{ + // FIXME: remove logging + LOGD("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, format, channels, rate); + // check values + if ((format != AudioSystem::PCM_16_BIT) || + (channels != channelCount()) || + (rate != sampleRate())) { + LOGE("Error opening input channel"); + return BAD_VALUE; + } + + mAudioHardware = hw; + mFd = fd; + return NO_ERROR; +} + +AudioStreamInGeneric::~AudioStreamInGeneric() +{ + // FIXME: remove logging + LOGD("AudioStreamInGeneric destructor"); + if (mAudioHardware) + mAudioHardware->closeInputStream(this); +} + +ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes) +{ + // FIXME: remove logging + LOGD("AudioStreamInGeneric::read(%p, %d) from fd %d", buffer, bytes, mFd); + AutoMutex lock(mLock); + if (mFd < 0) { + LOGE("Attempt to read from unopened device"); + return NO_INIT; + } + return ::read(mFd, buffer, bytes); +} + +status_t AudioStreamInGeneric::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamInGeneric::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/audioflinger/AudioHardwareGeneric.h b/libs/audioflinger/AudioHardwareGeneric.h new file mode 100644 index 000000000..c949aa12a --- /dev/null +++ b/libs/audioflinger/AudioHardwareGeneric.h @@ -0,0 +1,141 @@ +/* +** +** Copyright 2007, 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. +*/ + +#ifndef ANDROID_AUDIO_HARDWARE_GENERIC_H +#define ANDROID_AUDIO_HARDWARE_GENERIC_H + +#include +#include + +#include + +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioHardwareGeneric; + +class AudioStreamOutGeneric : public AudioStreamOut { +public: + AudioStreamOutGeneric() : mAudioHardware(0), mFd(-1) {} + virtual ~AudioStreamOutGeneric(); + + virtual status_t set( + AudioHardwareGeneric *hw, + int mFd, + int format, + int channelCount, + uint32_t sampleRate); + + virtual uint32_t sampleRate() const { return 44100; } + virtual size_t bufferSize() const { return 4096; } + virtual int channelCount() const { return 2; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 20; } + virtual status_t setVolume(float volume) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + virtual status_t standby(); + virtual status_t dump(int fd, const Vector& args); + +private: + AudioHardwareGeneric *mAudioHardware; + Mutex mLock; + int mFd; +}; + +class AudioStreamInGeneric : public AudioStreamIn { +public: + AudioStreamInGeneric() : mAudioHardware(0), mFd(-1) {} + virtual ~AudioStreamInGeneric(); + + virtual status_t set( + AudioHardwareGeneric *hw, + int mFd, + int format, + int channelCount, + uint32_t sampleRate, + AudioSystem::audio_in_acoustics acoustics); + + uint32_t sampleRate() const { return 8000; } + virtual size_t bufferSize() const { return 320; } + virtual int channelCount() const { return 1; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual status_t setGain(float gain) { return INVALID_OPERATION; } + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t dump(int fd, const Vector& args); + virtual status_t standby() { return NO_ERROR; } + +private: + AudioHardwareGeneric *mAudioHardware; + Mutex mLock; + int mFd; +}; + + +class AudioHardwareGeneric : public AudioHardwareBase +{ +public: + AudioHardwareGeneric(); + virtual ~AudioHardwareGeneric(); + virtual status_t initCheck(); + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + virtual status_t setParameter(const char* key, const char* value) + { return NO_ERROR; } + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + int format=0, + int channelCount=0, + uint32_t sampleRate=0, + status_t *status=0); + + virtual AudioStreamIn* openInputStream( + int format, + int channelCount, + uint32_t sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + + void closeOutputStream(AudioStreamOutGeneric* out); + void closeInputStream(AudioStreamInGeneric* in); +protected: + virtual status_t doRouting() { return NO_ERROR; } + virtual status_t dump(int fd, const Vector& args); + +private: + status_t dumpInternals(int fd, const Vector& args); + + Mutex mLock; + AudioStreamOutGeneric *mOutput; + AudioStreamInGeneric *mInput; + int mFd; + bool mMicMute; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_GENERIC_H diff --git a/libs/audioflinger/AudioHardwareInterface.cpp b/libs/audioflinger/AudioHardwareInterface.cpp new file mode 100644 index 000000000..ac76a19d8 --- /dev/null +++ b/libs/audioflinger/AudioHardwareInterface.cpp @@ -0,0 +1,247 @@ +/* +** +** Copyright 2007, 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 + +#define LOG_TAG "AudioHardwareInterface" +#include +#include + +#include "AudioHardwareStub.h" +#include "AudioHardwareGeneric.h" + +//#define DUMP_FLINGER_OUT // if defined allows recording samples in a file +#ifdef DUMP_FLINGER_OUT +#include "AudioDumpInterface.h" +#endif + + +// change to 1 to log routing calls +#define LOG_ROUTING_CALLS 0 + +namespace android { + +#if LOG_ROUTING_CALLS +static const char* routingModeStrings[] = +{ + "OUT OF RANGE", + "INVALID", + "CURRENT", + "NORMAL", + "RINGTONE", + "IN_CALL" +}; + +static const char* routeStrings[] = +{ + "EARPIECE ", + "SPEAKER ", + "BLUETOOTH ", + "HEADSET " + "BLUETOOTH_A2DP " +}; +static const char* routeNone = "NONE"; + +static const char* displayMode(int mode) +{ + if ((mode < -2) || (mode > 2)) + return routingModeStrings[0]; + return routingModeStrings[mode+3]; +} + +static const char* displayRoutes(uint32_t routes) +{ + static char routeStr[80]; + if (routes == 0) + return routeNone; + routeStr[0] = 0; + int bitMask = 1; + for (int i = 0; i < 4; ++i, bitMask <<= 1) { + if (routes & bitMask) { + strcat(routeStr, routeStrings[i]); + } + } + routeStr[strlen(routeStr)-1] = 0; + return routeStr; +} +#endif + +// ---------------------------------------------------------------------------- + +AudioHardwareInterface* AudioHardwareInterface::create() +{ + /* + * FIXME: This code needs to instantiate the correct audio device + * interface. For now - we use compile-time switches. + */ + AudioHardwareInterface* hw = 0; + char value[PROPERTY_VALUE_MAX]; + +#ifdef GENERIC_AUDIO + hw = new AudioHardwareGeneric(); +#else + // if running in emulation - use the emulator driver + if (property_get("ro.kernel.qemu", value, 0)) { + LOGD("Running in emulation - using generic audio driver"); + hw = new AudioHardwareGeneric(); + } + else { + LOGV("Creating Vendor Specific AudioHardware"); + hw = createAudioHardware(); + } +#endif + if (hw->initCheck() != NO_ERROR) { + LOGW("Using stubbed audio hardware. No sound will be produced."); + delete hw; + hw = new AudioHardwareStub(); + } + +#ifdef DUMP_FLINGER_OUT + // This code adds a record of buffers in a file to write calls made by AudioFlinger. + // It replaces the current AudioHardwareInterface object by an intermediate one which + // will record buffers in a file (after sending them to hardware) for testing purpose. + // This feature is enabled by defining symbol DUMP_FLINGER_OUT. + // The output file is FLINGER_DUMP_NAME. Pause are not recorded in the file. + + hw = new AudioDumpInterface(hw); // replace interface +#endif + return hw; +} + +AudioStreamOut::~AudioStreamOut() +{ +} + +AudioStreamIn::~AudioStreamIn() {} + +AudioHardwareBase::AudioHardwareBase() +{ + // force a routing update on initialization + memset(&mRoutes, 0, sizeof(mRoutes)); + mMode = 0; +} + +// generics for audio routing - the real work is done in doRouting +status_t AudioHardwareBase::setRouting(int mode, uint32_t routes) +{ +#if LOG_ROUTING_CALLS + LOGD("setRouting: mode=%s, routes=[%s]", displayMode(mode), displayRoutes(routes)); +#endif + if (mode == AudioSystem::MODE_CURRENT) + mode = mMode; + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) + return BAD_VALUE; + uint32_t old = mRoutes[mode]; + mRoutes[mode] = routes; + if ((mode != mMode) || (old == routes)) + return NO_ERROR; +#if LOG_ROUTING_CALLS + const char* oldRouteStr = strdup(displayRoutes(old)); + LOGD("doRouting: mode=%s, old route=[%s], new route=[%s]", + displayMode(mode), oldRouteStr, displayRoutes(routes)); + delete oldRouteStr; +#endif + return doRouting(); +} + +status_t AudioHardwareBase::getRouting(int mode, uint32_t* routes) +{ + if (mode == AudioSystem::MODE_CURRENT) + mode = mMode; + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) + return BAD_VALUE; + *routes = mRoutes[mode]; +#if LOG_ROUTING_CALLS + LOGD("getRouting: mode=%s, routes=[%s]", + displayMode(mode), displayRoutes(*routes)); +#endif + return NO_ERROR; +} + +status_t AudioHardwareBase::setMode(int mode) +{ +#if LOG_ROUTING_CALLS + LOGD("setMode(%s)", displayMode(mode)); +#endif + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) + return BAD_VALUE; + if (mMode == mode) + return NO_ERROR; +#if LOG_ROUTING_CALLS + LOGD("doRouting: old mode=%s, new mode=%s route=[%s]", + displayMode(mMode), displayMode(mode), displayRoutes(mRoutes[mode])); +#endif + mMode = mode; + return doRouting(); +} + +status_t AudioHardwareBase::getMode(int* mode) +{ + // Implement: set audio routing + *mode = mMode; + return NO_ERROR; +} + +status_t AudioHardwareBase::setParameter(const char* key, const char* value) +{ + // default implementation is to ignore + return NO_ERROR; +} + + +// default implementation +size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + if (sampleRate != 8000) { + LOGW("getInputBufferSize bad sampling rate: %d", sampleRate); + return 0; + } + if (format != AudioSystem::PCM_16_BIT) { + LOGW("getInputBufferSize bad format: %d", format); + return 0; + } + if (channelCount != 1) { + LOGW("getInputBufferSize bad channel count: %d", channelCount); + return 0; + } + + return 320; +} + +status_t AudioHardwareBase::dumpState(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioHardwareBase::dumpState\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tmMode: %d\n", mMode); + result.append(buffer); + for (int i = 0, n = AudioSystem::NUM_MODES; i < n; ++i) { + snprintf(buffer, SIZE, "\tmRoutes[%d]: %d\n", i, mRoutes[i]); + result.append(buffer); + } + ::write(fd, result.string(), result.size()); + dump(fd, args); // Dump the state of the concrete child. + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp new file mode 100644 index 000000000..b13cb1c07 --- /dev/null +++ b/libs/audioflinger/AudioHardwareStub.cpp @@ -0,0 +1,185 @@ +/* //device/servers/AudioFlinger/AudioHardwareStub.cpp +** +** Copyright 2007, 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 + +#include "AudioHardwareStub.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +AudioHardwareStub::AudioHardwareStub() : mMicMute(false) +{ +} + +AudioHardwareStub::~AudioHardwareStub() +{ +} + +status_t AudioHardwareStub::initCheck() +{ + return NO_ERROR; +} + +AudioStreamOut* AudioHardwareStub::openOutputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status) +{ + AudioStreamOutStub* out = new AudioStreamOutStub(); + status_t lStatus = out->set(format, channelCount, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) + return out; + delete out; + return 0; +} + +AudioStreamIn* AudioHardwareStub::openInputStream( + int format, int channelCount, uint32_t sampleRate, + status_t *status, AudioSystem::audio_in_acoustics acoustics) +{ + AudioStreamInStub* in = new AudioStreamInStub(); + status_t lStatus = in->set(format, channelCount, sampleRate, acoustics); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) + return in; + delete in; + return 0; +} + +status_t AudioHardwareStub::setVoiceVolume(float volume) +{ + return NO_ERROR; +} + +status_t AudioHardwareStub::setMasterVolume(float volume) +{ + return NO_ERROR; +} + +status_t AudioHardwareStub::dumpInternals(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioHardwareStub::dumpInternals\n"); + snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false"); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioHardwareStub::dump(int fd, const Vector& args) +{ + dumpInternals(fd, args); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamOutStub::set(int format, int channels, uint32_t rate) +{ + // fix up defaults + if (format == 0) format = AudioSystem::PCM_16_BIT; + if (channels == 0) channels = channelCount(); + if (rate == 0) rate = sampleRate(); + + if ((format == AudioSystem::PCM_16_BIT) && + (channels == channelCount()) && + (rate == sampleRate())) + return NO_ERROR; + return BAD_VALUE; +} + +ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes) +{ + // fake timing for audio output + usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate()); + return bytes; +} + +status_t AudioStreamOutStub::standby() +{ + return NO_ERROR; +} + +status_t AudioStreamOutStub::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamOutStub::dump\n"); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamInStub::set(int format, int channels, uint32_t rate, + AudioSystem::audio_in_acoustics acoustics) +{ + if ((format == AudioSystem::PCM_16_BIT) && + (channels == channelCount()) && + (rate == sampleRate())) + return NO_ERROR; + return BAD_VALUE; +} + +ssize_t AudioStreamInStub::read(void* buffer, ssize_t bytes) +{ + // fake timing for audio input + usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate()); + memset(buffer, 0, bytes); + return bytes; +} + +status_t AudioStreamInStub::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamInStub::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/audioflinger/AudioHardwareStub.h b/libs/audioflinger/AudioHardwareStub.h new file mode 100644 index 000000000..d40642465 --- /dev/null +++ b/libs/audioflinger/AudioHardwareStub.h @@ -0,0 +1,100 @@ +/* //device/servers/AudioFlinger/AudioHardwareStub.h +** +** Copyright 2007, 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. +*/ + +#ifndef ANDROID_AUDIO_HARDWARE_STUB_H +#define ANDROID_AUDIO_HARDWARE_STUB_H + +#include +#include + +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioStreamOutStub : public AudioStreamOut { +public: + virtual status_t set(int format, int channelCount, uint32_t sampleRate); + virtual uint32_t sampleRate() const { return 44100; } + virtual size_t bufferSize() const { return 4096; } + virtual int channelCount() const { return 2; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 0; } + virtual status_t setVolume(float volume) { return NO_ERROR; } + virtual ssize_t write(const void* buffer, size_t bytes); + virtual status_t standby(); + virtual status_t dump(int fd, const Vector& args); +}; + +class AudioStreamInStub : public AudioStreamIn { +public: + virtual status_t set(int format, int channelCount, uint32_t sampleRate, AudioSystem::audio_in_acoustics acoustics); + virtual uint32_t sampleRate() const { return 8000; } + virtual size_t bufferSize() const { return 320; } + virtual int channelCount() const { return 1; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual status_t setGain(float gain) { return NO_ERROR; } + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t dump(int fd, const Vector& args); + virtual status_t standby() { return NO_ERROR; } +}; + +class AudioHardwareStub : public AudioHardwareBase +{ +public: + AudioHardwareStub(); + virtual ~AudioHardwareStub(); + virtual status_t initCheck(); + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; } + virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO_ERROR; } + + virtual status_t setParameter(const char* key, const char* value) + { return NO_ERROR; } + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + int format=0, + int channelCount=0, + uint32_t sampleRate=0, + status_t *status=0); + + virtual AudioStreamIn* openInputStream( + int format, + int channelCount, + uint32_t sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + +protected: + virtual status_t doRouting() { return NO_ERROR; } + virtual status_t dump(int fd, const Vector& args); + + bool mMicMute; +private: + status_t dumpInternals(int fd, const Vector& args); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_STUB_H diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp new file mode 100644 index 000000000..b03467f6c --- /dev/null +++ b/libs/audioflinger/AudioMixer.cpp @@ -0,0 +1,913 @@ +/* //device/include/server/AudioFlinger/AudioMixer.cpp +** +** Copyright 2007, 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. +*/ + +#define LOG_TAG "AudioMixer" + +#include +#include +#include +#include + +#include +#include + +#include "AudioMixer.h" + +namespace android { +// ---------------------------------------------------------------------------- + +static inline int16_t clamp16(int32_t sample) +{ + if ((sample>>15) ^ (sample>>31)) + sample = 0x7FFF ^ (sample>>31); + return sample; +} + +// ---------------------------------------------------------------------------- + +AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate) + : mActiveTrack(0), mTrackNames(0), mSampleRate(sampleRate) +{ + mState.enabledTracks= 0; + mState.needsChanged = 0; + mState.frameCount = frameCount; + mState.outputTemp = 0; + mState.resampleTemp = 0; + mState.hook = process__nop; + track_t* t = mState.tracks; + for (int i=0 ; i<32 ; i++) { + t->needs = 0; + t->volume[0] = UNITY_GAIN; + t->volume[1] = UNITY_GAIN; + t->volumeInc[0] = 0; + t->volumeInc[1] = 0; + t->channelCount = 2; + t->enabled = 0; + t->format = 16; + t->buffer.raw = 0; + t->bufferProvider = 0; + t->hook = 0; + t->resampler = 0; + t->sampleRate = mSampleRate; + t->in = 0; + t++; + } +} + + AudioMixer::~AudioMixer() + { + track_t* t = mState.tracks; + for (int i=0 ; i<32 ; i++) { + delete t->resampler; + t++; + } + delete [] mState.outputTemp; + delete [] mState.resampleTemp; + } + + int AudioMixer::getTrackName() + { + uint32_t names = mTrackNames; + uint32_t mask = 1; + int n = 0; + while (names & mask) { + mask <<= 1; + n++; + } + if (mask) { + LOGV("add track (%d)", n); + mTrackNames |= mask; + return TRACK0 + n; + } + return -1; + } + + void AudioMixer::invalidateState(uint32_t mask) + { + if (mask) { + mState.needsChanged |= mask; + mState.hook = process__validate; + } + } + + void AudioMixer::deleteTrackName(int name) + { + name -= TRACK0; + if (uint32_t(name) < MAX_NUM_TRACKS) { + LOGV("deleteTrackName(%d)", name); + track_t& track(mState.tracks[ name ]); + if (track.enabled != 0) { + track.enabled = 0; + invalidateState(1<= MAX_NUM_TRACKS) { + return BAD_VALUE; + } + mActiveTrack = track - TRACK0; + return NO_ERROR; +} + +status_t AudioMixer::setParameter(int target, int name, int value) +{ + switch (target) { + case TRACK: + if (name == CHANNEL_COUNT) { + if ((uint32_t(value) <= MAX_NUM_CHANNELS) && (value)) { + if (mState.tracks[ mActiveTrack ].channelCount != value) { + mState.tracks[ mActiveTrack ].channelCount = value; + LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", value); + invalidateState(1< 0) { + track_t& track = mState.tracks[ mActiveTrack ]; + if (track.setResampler(uint32_t(value), mSampleRate)) { + LOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)", + uint32_t(value)); + invalidateState(1<0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) || + ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) { + volumeInc[i] = 0; + prevVolume[i] = volume[i]<<16; + } + } +} + + +status_t AudioMixer::setBufferProvider(AudioBufferProvider* buffer) +{ + mState.tracks[ mActiveTrack ].bufferProvider = buffer; + return NO_ERROR; +} + + + +void AudioMixer::process(void* output) +{ + mState.hook(&mState, output); +} + + +void AudioMixer::process__validate(state_t* state, void* output) +{ + LOGW_IF(!state->needsChanged, + "in process__validate() but nothing's invalid"); + + uint32_t changed = state->needsChanged; + state->needsChanged = 0; // clear the validation flag + + // recompute which tracks are enabled / disabled + uint32_t enabled = 0; + uint32_t disabled = 0; + while (changed) { + const int i = 31 - __builtin_clz(changed); + const uint32_t mask = 1<tracks[i]; + (t.enabled ? enabled : disabled) |= mask; + } + state->enabledTracks &= ~disabled; + state->enabledTracks |= enabled; + + // compute everything we need... + int countActiveTracks = 0; + int all16BitsStereoNoResample = 1; + int resampling = 0; + int volumeRamp = 0; + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<tracks[i]; + uint32_t n = 0; + n |= NEEDS_CHANNEL_1 + t.channelCount - 1; + n |= NEEDS_FORMAT_16; + n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED; + + if (t.volumeInc[0]|t.volumeInc[1]) { + volumeRamp = 1; + } else if (!t.doesResample() && t.volumeRL == 0) { + n |= NEEDS_MUTE_ENABLED; + } + t.needs = n; + + if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) { + t.hook = track__nop; + } else { + if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { + all16BitsStereoNoResample = 0; + resampling = 1; + t.hook = track__genericResample; + } else { + if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){ + t.hook = track__16BitsMono; + all16BitsStereoNoResample = 0; + } + if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_2){ + t.hook = track__16BitsStereo; + } + } + } + } + + // select the processing hooks + state->hook = process__nop; + if (countActiveTracks) { + if (resampling) { + if (!state->outputTemp) { + state->outputTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; + } + if (!state->resampleTemp) { + state->resampleTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; + } + state->hook = process__genericResampling; + } else { + if (state->outputTemp) { + delete [] state->outputTemp; + state->outputTemp = 0; + } + if (state->resampleTemp) { + delete [] state->resampleTemp; + state->resampleTemp = 0; + } + state->hook = process__genericNoResampling; + if (all16BitsStereoNoResample && !volumeRamp) { + if (countActiveTracks == 1) { + state->hook = process__OneTrack16BitsStereoNoResampling; + } + } + } + } + + LOGV("mixer configuration change: %d activeTracks (%08x) " + "all16BitsStereoNoResample=%d, resampling=%d, volumeRamp=%d", + countActiveTracks, state->enabledTracks, + all16BitsStereoNoResample, resampling, volumeRamp); + + state->hook(state, output); + + // Now that the volume ramp has been done, set optimal state and + // track hooks for subsequent mixer process + if (countActiveTracks) { + int allMuted = 1; + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<tracks[i]; + if (!t.doesResample() && t.volumeRL == 0) + { + t.needs |= NEEDS_MUTE_ENABLED; + t.hook = track__nop; + } else { + allMuted = 0; + } + } + if (allMuted) { + state->hook = process__nop; + } else if (!resampling && all16BitsStereoNoResample) { + if (countActiveTracks == 1) { + state->hook = process__OneTrack16BitsStereoNoResampling; + } + } + } +} + +static inline +int32_t mulAdd(int16_t in, int16_t v, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + asm( "smlabb %[out], %[in], %[v], %[a] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v), [a]"r"(a) + : ); + return out; +#else + return a + in * int32_t(v); +#endif +} + +static inline +int32_t mul(int16_t in, int16_t v) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + asm( "smulbb %[out], %[in], %[v] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v) + : ); + return out; +#else + return in * int32_t(v); +#endif +} + +static inline +int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smlabb %[out], %[inRL], %[vRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a) + : ); + } else { + asm( "smlatt %[out], %[inRL], %[vRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a) + : ); + } + return out; +#else + if (left) { + return a + int16_t(inRL&0xFFFF) * int16_t(vRL&0xFFFF); + } else { + return a + int16_t(inRL>>16) * int16_t(vRL>>16); + } +#endif +} + +static inline +int32_t mulRL(int left, uint32_t inRL, uint32_t vRL) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smulbb %[out], %[inRL], %[vRL] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL) + : ); + } else { + asm( "smultt %[out], %[inRL], %[vRL] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL) + : ); + } + return out; +#else + if (left) { + return int16_t(inRL&0xFFFF) * int16_t(vRL&0xFFFF); + } else { + return int16_t(inRL>>16) * int16_t(vRL>>16); + } +#endif +} + + +void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp) +{ + t->resampler->setSampleRate(t->sampleRate); + + // ramp gain - resample to temp buffer and scale/mix in 2nd step + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN); + memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); + t->resampler->resample(temp, outFrameCount, t->bufferProvider); + volumeRampStereo(t, out, outFrameCount, temp); + } + + // constant gain + else { + t->resampler->setVolume(t->volume[0], t->volume[1]); + t->resampler->resample(out, outFrameCount, t->bufferProvider); + } +} + +void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp) +{ +} + +void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +{ + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + //LOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + // ramp volume + do { + *out++ += (vl >> 16) * (*temp++ >> 12); + *out++ += (vr >> 16) * (*temp++ >> 12); + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(); +} + +void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +{ + int16_t const *in = static_cast(t->in); + + // ramp gain + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + do { + *out++ += (vl >> 16) * (int32_t) *in++; + *out++ += (vr >> 16) * (int32_t) *in++; + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(); + } + + // constant gain + else { + const uint32_t vrl = t->volumeRL; + do { + uint32_t rl = *reinterpret_cast(in); + in += 2; + out[0] = mulAddRL(1, rl, vrl, out[0]); + out[1] = mulAddRL(0, rl, vrl, out[1]); + out += 2; + } while (--frameCount); + } + t->in = in; +} + +void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +{ + int16_t const *in = static_cast(t->in); + + // ramp gain + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + do { + int32_t l = *in++; + *out++ += (vl >> 16) * l; + *out++ += (vr >> 16) * l; + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(); + } + // constant gain + else { + const int16_t vl = t->volume[0]; + const int16_t vr = t->volume[1]; + do { + int16_t l = *in++; + out[0] = mulAdd(l, vl, out[0]); + out[1] = mulAdd(l, vr, out[1]); + out += 2; + } while (--frameCount); + } + t->in = in; +} + +inline +void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c) +{ + for (size_t i=0 ; i> 12; + int32_t nr = r >> 12; + l = clamp16(nl); + r = clamp16(nr); + *out++ = (r<<16) | (l & 0xFFFF); + } +} + +// no-op case +void AudioMixer::process__nop(state_t* state, void* output) +{ + // this assumes output 16 bits stereo, no resampling + memset(output, 0, state->frameCount*4); + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<tracks[i]; + size_t outFrames = state->frameCount; + while (outFrames) { + t.buffer.frameCount = outFrames; + t.bufferProvider->getNextBuffer(&t.buffer); + if (!t.buffer.raw) break; + outFrames -= t.buffer.frameCount; + t.bufferProvider->releaseBuffer(&t.buffer); + } + } +} + +// generic code without resampling +void AudioMixer::process__genericNoResampling(state_t* state, void* output) +{ + int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32))); + + // acquire each track's buffer + uint32_t enabledTracks = state->enabledTracks; + uint32_t en = enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<tracks[i]; + t.buffer.frameCount = state->frameCount; + t.bufferProvider->getNextBuffer(&t.buffer); + t.frameCount = t.buffer.frameCount; + t.in = t.buffer.raw; + // t.in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (t.in == NULL) + enabledTracks &= ~(1<(output); + size_t numFrames = state->frameCount; + do { + memset(outTemp, 0, sizeof(outTemp)); + + en = enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<tracks[i]; + size_t outFrames = BLOCKSIZE; + + while (outFrames) { + size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount; + if (inFrames) { + (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp); + t.frameCount -= inFrames; + outFrames -= inFrames; + } + if (t.frameCount == 0 && outFrames) { + t.bufferProvider->releaseBuffer(&t.buffer); + t.buffer.frameCount = numFrames - (BLOCKSIZE - outFrames); + t.bufferProvider->getNextBuffer(&t.buffer); + t.in = t.buffer.raw; + if (t.in == NULL) { + enabledTracks &= ~(1<tracks[i]; + t.bufferProvider->releaseBuffer(&t.buffer); + } +} + +// generic code with resampling +void AudioMixer::process__genericResampling(state_t* state, void* output) +{ + int32_t* const outTemp = state->outputTemp; + const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount; + memset(outTemp, 0, size); + + int32_t* out = static_cast(output); + size_t numFrames = state->frameCount; + + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<tracks[i]; + + // this is a little goofy, on the resampling case we don't + // acquire/release the buffers because it's done by + // the resampler. + if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { + (t.hook)(&t, outTemp, numFrames, state->resampleTemp); + } else { + + size_t outFrames = numFrames; + + while (outFrames) { + t.buffer.frameCount = outFrames; + t.bufferProvider->getNextBuffer(&t.buffer); + t.in = t.buffer.raw; + // t.in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (t.in == NULL) break; + + (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp); + outFrames -= t.buffer.frameCount; + t.bufferProvider->releaseBuffer(&t.buffer); + } + } + } + + ditherAndClamp(out, outTemp, numFrames); +} + +// one track, 16 bits stereo without resampling is the most common case +void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output) +{ + const int i = 31 - __builtin_clz(state->enabledTracks); + const track_t& t = state->tracks[i]; + + AudioBufferProvider::Buffer& b(t.buffer); + + int32_t* out = static_cast(output); + size_t numFrames = state->frameCount; + + const int16_t vl = t.volume[0]; + const int16_t vr = t.volume[1]; + const uint32_t vrl = t.volumeRL; + while (numFrames) { + b.frameCount = numFrames; + t.bufferProvider->getNextBuffer(&b); + int16_t const *in = b.i16; + + // in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (in == NULL) { + memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t)); + return; + } + size_t outFrames = b.frameCount; + + if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) { + // volume is boosted, so we might need to clamp even though + // we process only one track. + do { + uint32_t rl = *reinterpret_cast(in); + in += 2; + int32_t l = mulRL(1, rl, vrl) >> 12; + int32_t r = mulRL(0, rl, vrl) >> 12; + // clamping... + l = clamp16(l); + r = clamp16(r); + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + } else { + do { + uint32_t rl = *reinterpret_cast(in); + in += 2; + int32_t l = mulRL(1, rl, vrl) >> 12; + int32_t r = mulRL(0, rl, vrl) >> 12; + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + } + numFrames -= b.frameCount; + t.bufferProvider->releaseBuffer(&b); + } +} + +// 2 tracks is also a common case +void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output) +{ + int i; + uint32_t en = state->enabledTracks; + + i = 31 - __builtin_clz(en); + const track_t& t0 = state->tracks[i]; + AudioBufferProvider::Buffer& b0(t0.buffer); + + en &= ~(1<tracks[i]; + AudioBufferProvider::Buffer& b1(t1.buffer); + + int16_t const *in0; + const int16_t vl0 = t0.volume[0]; + const int16_t vr0 = t0.volume[1]; + size_t frameCount0 = 0; + + int16_t const *in1; + const int16_t vl1 = t1.volume[0]; + const int16_t vr1 = t1.volume[1]; + size_t frameCount1 = 0; + + int32_t* out = static_cast(output); + size_t numFrames = state->frameCount; + int16_t const *buff = NULL; + + + while (numFrames) { + + if (frameCount0 == 0) { + b0.frameCount = numFrames; + t0.bufferProvider->getNextBuffer(&b0); + if (b0.i16 == NULL) { + if (buff == NULL) { + buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; + } + in0 = buff; + b0.frameCount = numFrames; + } else { + in0 = b0.i16; + } + frameCount0 = b0.frameCount; + } + if (frameCount1 == 0) { + b1.frameCount = numFrames; + t1.bufferProvider->getNextBuffer(&b1); + if (b1.i16 == NULL) { + if (buff == NULL) { + buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; + } + in1 = buff; + b1.frameCount = numFrames; + } else { + in1 = b1.i16; + } + frameCount1 = b1.frameCount; + } + + size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1; + + numFrames -= outFrames; + frameCount0 -= outFrames; + frameCount1 -= outFrames; + + do { + int32_t l0 = *in0++; + int32_t r0 = *in0++; + l0 = mul(l0, vl0); + r0 = mul(r0, vr0); + int32_t l = *in1++; + int32_t r = *in1++; + l = mulAdd(l, vl1, l0) >> 12; + r = mulAdd(r, vr1, r0) >> 12; + // clamping... + l = clamp16(l); + r = clamp16(r); + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + + if (frameCount0 == 0) { + t0.bufferProvider->releaseBuffer(&b0); + } + if (frameCount1 == 0) { + t1.bufferProvider->releaseBuffer(&b1); + } + } + + if (buff != NULL) { + delete [] buff; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h new file mode 100644 index 000000000..72ca28a17 --- /dev/null +++ b/libs/audioflinger/AudioMixer.h @@ -0,0 +1,192 @@ +/* //device/include/server/AudioFlinger/AudioMixer.h +** +** Copyright 2007, 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. +*/ + +#ifndef ANDROID_AUDIO_MIXER_H +#define ANDROID_AUDIO_MIXER_H + +#include +#include + +#include "AudioBufferProvider.h" +#include "AudioResampler.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// ---------------------------------------------------------------------------- + +class AudioMixer +{ +public: + AudioMixer(size_t frameCount, uint32_t sampleRate); + + ~AudioMixer(); + + static const uint32_t MAX_NUM_TRACKS = 32; + static const uint32_t MAX_NUM_CHANNELS = 2; + + static const uint16_t UNITY_GAIN = 0x1000; + + enum { // names + + // track units (32 units) + TRACK0 = 0x1000, + + // enable/disable + MIXING = 0x2000, + + // setParameter targets + TRACK = 0x3000, + RESAMPLE = 0x3001, + RAMP_VOLUME = 0x3002, // ramp to new volume + VOLUME = 0x3003, // don't ramp + + // set Parameter names + // for target TRACK + CHANNEL_COUNT = 0x4000, + FORMAT = 0x4001, + // for TARGET RESAMPLE + SAMPLE_RATE = 0x4100, + // for TARGET VOLUME (8 channels max) + VOLUME0 = 0x4200, + VOLUME1 = 0x4201, + }; + + + int getTrackName(); + void deleteTrackName(int name); + + status_t enable(int name); + status_t disable(int name); + + status_t setActiveTrack(int track); + status_t setParameter(int target, int name, int value); + + status_t setBufferProvider(AudioBufferProvider* bufferProvider); + void process(void* output); + + uint32_t trackNames() const { return mTrackNames; } + +private: + + enum { + NEEDS_CHANNEL_COUNT__MASK = 0x00000003, + NEEDS_FORMAT__MASK = 0x000000F0, + NEEDS_MUTE__MASK = 0x00000100, + NEEDS_RESAMPLE__MASK = 0x00001000, + }; + + enum { + NEEDS_CHANNEL_1 = 0x00000000, + NEEDS_CHANNEL_2 = 0x00000001, + + NEEDS_FORMAT_16 = 0x00000010, + + NEEDS_MUTE_DISABLED = 0x00000000, + NEEDS_MUTE_ENABLED = 0x00000100, + + NEEDS_RESAMPLE_DISABLED = 0x00000000, + NEEDS_RESAMPLE_ENABLED = 0x00001000, + }; + + static inline int32_t applyVolume(int32_t in, int32_t v) { + return in * v; + } + + + struct state_t; + + typedef void (*mix_t)(state_t* state, void* output); + + static const int BLOCKSIZE = 16; // 4 cache lines + + struct track_t { + uint32_t needs; + + union { + int16_t volume[2]; // [0]3.12 fixed point + int32_t volumeRL; + }; + + int32_t prevVolume[2]; + + int32_t volumeInc[2]; + + uint16_t frameCount; + + uint8_t channelCount : 4; + uint8_t enabled : 1; + uint8_t reserved0 : 3; + uint8_t format; + + AudioBufferProvider* bufferProvider; + mutable AudioBufferProvider::Buffer buffer; + + void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp); + void const* in; // current location in buffer + + AudioResampler* resampler; + uint32_t sampleRate; + + bool setResampler(uint32_t sampleRate, uint32_t devSampleRate); + bool doesResample() const; + void adjustVolumeRamp(); + }; + + // pad to 32-bytes to fill cache line + struct state_t { + uint32_t enabledTracks; + uint32_t needsChanged; + size_t frameCount; + mix_t hook; + int32_t *outputTemp; + int32_t *resampleTemp; + int32_t reserved[2]; + track_t tracks[32]; __attribute__((aligned(32))); + }; + + int mActiveTrack; + uint32_t mTrackNames; + const uint32_t mSampleRate; + + state_t mState __attribute__((aligned(32))); + + void invalidateState(uint32_t mask); + + static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp); + static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c); + + static void process__validate(state_t* state, void* output); + static void process__nop(state_t* state, void* output); + static void process__genericNoResampling(state_t* state, void* output); + static void process__genericResampling(state_t* state, void* output); + static void process__OneTrack16BitsStereoNoResampling(state_t* state, void* output); + static void process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_AUDIO_MIXER_H diff --git a/libs/audioflinger/AudioResampler.cpp b/libs/audioflinger/AudioResampler.cpp new file mode 100644 index 000000000..5dabacbb7 --- /dev/null +++ b/libs/audioflinger/AudioResampler.cpp @@ -0,0 +1,595 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "AudioResampler" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include "AudioResampler.h" +#include "AudioResamplerSinc.h" +#include "AudioResamplerCubic.h" + +namespace android { + +#ifdef __ARM_ARCH_5E__ // optimized asm option + #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 +#endif // __ARM_ARCH_5E__ +// ---------------------------------------------------------------------------- + +class AudioResamplerOrder1 : public AudioResampler { +public: + AudioResamplerOrder1(int bitDepth, int inChannelCount, int32_t sampleRate) : + AudioResampler(bitDepth, inChannelCount, sampleRate), mX0L(0), mX0R(0) { + } + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +private: + // number of bits used in interpolation multiply - 15 bits avoids overflow + static const int kNumInterpBits = 15; + + // bits to shift the phase fraction down to avoid overflow + static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits; + + void init() {} + void resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + void resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + void AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement); + void AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement); +#endif // ASM_ARM_RESAMP1 + + static inline int32_t Interp(int32_t x0, int32_t x1, uint32_t f) { + return x0 + (((x1 - x0) * (int32_t)(f >> kPreInterpShift)) >> kNumInterpBits); + } + static inline void Advance(size_t* index, uint32_t* frac, uint32_t inc) { + *frac += inc; + *index += (size_t)(*frac >> kNumPhaseBits); + *frac &= kPhaseMask; + } + int mX0L; + int mX0R; +}; + +// ---------------------------------------------------------------------------- +AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, + int32_t sampleRate, int quality) { + + // can only create low quality resample now + AudioResampler* resampler; + + char value[PROPERTY_VALUE_MAX]; + if (property_get("af.resampler.quality", value, 0)) { + quality = atoi(value); + LOGD("forcing AudioResampler quality to %d", quality); + } + + if (quality == DEFAULT) + quality = LOW_QUALITY; + + switch (quality) { + default: + case LOW_QUALITY: + LOGV("Create linear Resampler"); + resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate); + break; + case MED_QUALITY: + LOGV("Create cubic Resampler"); + resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate); + break; + case HIGH_QUALITY: + LOGV("Create sinc Resampler"); + resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate); + break; + } + + // initialize resampler + resampler->init(); + return resampler; +} + +AudioResampler::AudioResampler(int bitDepth, int inChannelCount, + int32_t sampleRate) : + mBitDepth(bitDepth), mChannelCount(inChannelCount), + mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0), + mPhaseFraction(0) { + // sanity check on format + if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) { + LOGE("Unsupported sample format, %d bits, %d channels", bitDepth, + inChannelCount); + // LOG_ASSERT(0); + } + + // initialize common members + mVolume[0] = mVolume[1] = 0; + mBuffer.frameCount = 0; + + // save format for quick lookup + if (inChannelCount == 1) { + mFormat = MONO_16_BIT; + } else { + mFormat = STEREO_16_BIT; + } +} + +AudioResampler::~AudioResampler() { +} + +void AudioResampler::setSampleRate(int32_t inSampleRate) { + mInSampleRate = inSampleRate; + mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate); +} + +void AudioResampler::setVolume(int16_t left, int16_t right) { + // TODO: Implement anti-zipper filter + mVolume[0] = left; + mVolume[1] = right; +} + +// ---------------------------------------------------------------------------- + +void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + // should never happen, but we overflow if it does + // LOG_ASSERT(outFrameCount < 32767); + + // select the appropriate resampler + switch (mChannelCount) { + case 1: + resampleMono16(out, outFrameCount, provider); + break; + case 2: + resampleStereo16(out, outFrameCount, provider); + break; + } +} + +void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // LOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n", + // outFrameCount, inputIndex, phaseFraction, phaseIncrement); + + while (outputIndex < outputSampleCount) { + + // buffer is empty, fetch a new one + while (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) { + goto resampleStereo16_exit; + } + + // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount); + if (mBuffer.frameCount > inputIndex) break; + + inputIndex -= mBuffer.frameCount; + mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; + mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; + provider->releaseBuffer(&mBuffer); + // mBuffer.frameCount == 0 now so we reload a new buffer + } + + int16_t *in = mBuffer.i16; + + // handle boundary case + while (inputIndex == 0) { + // LOGE("boundary case\n"); + out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction); + out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction); + Advance(&inputIndex, &phaseFraction, phaseIncrement); + if (outputIndex == outputSampleCount) + break; + } + + // process input samples + // LOGE("general case\n"); + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + if (inputIndex + 2 < mBuffer.frameCount) { + int32_t* maxOutPt; + int32_t maxInIdx; + + maxOutPt = out + (outputSampleCount - 2); // 2 because 2 frames per loop + maxInIdx = mBuffer.frameCount - 2; + AsmStereo16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr, + phaseFraction, phaseIncrement); + } +#endif // ASM_ARM_RESAMP1 + + while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) { + out[outputIndex++] += vl * Interp(in[inputIndex*2-2], + in[inputIndex*2], phaseFraction); + out[outputIndex++] += vr * Interp(in[inputIndex*2-1], + in[inputIndex*2+1], phaseFraction); + Advance(&inputIndex, &phaseFraction, phaseIncrement); + } + + // LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + + // if done with buffer, save samples + if (inputIndex >= mBuffer.frameCount) { + inputIndex -= mBuffer.frameCount; + + // LOGE("buffer done, new input index %d", inputIndex); + + mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; + mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; + provider->releaseBuffer(&mBuffer); + + // verify that the releaseBuffer resets the buffer frameCount + // LOG_ASSERT(mBuffer.frameCount == 0); + } + } + + // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + +resampleStereo16_exit: + // save state + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // LOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n", + // outFrameCount, inputIndex, phaseFraction, phaseIncrement); + while (outputIndex < outputSampleCount) { + // buffer is empty, fetch a new one + while (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) { + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; + goto resampleMono16_exit; + } + // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount); + if (mBuffer.frameCount > inputIndex) break; + + inputIndex -= mBuffer.frameCount; + mX0L = mBuffer.i16[mBuffer.frameCount-1]; + provider->releaseBuffer(&mBuffer); + // mBuffer.frameCount == 0 now so we reload a new buffer + } + int16_t *in = mBuffer.i16; + + // handle boundary case + while (inputIndex == 0) { + // LOGE("boundary case\n"); + int32_t sample = Interp(mX0L, in[0], phaseFraction); + out[outputIndex++] += vl * sample; + out[outputIndex++] += vr * sample; + Advance(&inputIndex, &phaseFraction, phaseIncrement); + if (outputIndex == outputSampleCount) + break; + } + + // process input samples + // LOGE("general case\n"); + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + if (inputIndex + 2 < mBuffer.frameCount) { + int32_t* maxOutPt; + int32_t maxInIdx; + + maxOutPt = out + (outputSampleCount - 2); + maxInIdx = (int32_t)mBuffer.frameCount - 2; + AsmMono16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr, + phaseFraction, phaseIncrement); + } +#endif // ASM_ARM_RESAMP1 + + while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) { + int32_t sample = Interp(in[inputIndex-1], in[inputIndex], + phaseFraction); + out[outputIndex++] += vl * sample; + out[outputIndex++] += vr * sample; + Advance(&inputIndex, &phaseFraction, phaseIncrement); + } + + + // LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + + // if done with buffer, save samples + if (inputIndex >= mBuffer.frameCount) { + inputIndex -= mBuffer.frameCount; + + // LOGE("buffer done, new input index %d", inputIndex); + + mX0L = mBuffer.i16[mBuffer.frameCount-1]; + provider->releaseBuffer(&mBuffer); + + // verify that the releaseBuffer resets the buffer frameCount + // LOG_ASSERT(mBuffer.frameCount == 0); + } + } + + // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + +resampleMono16_exit: + // save state + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + +/******************************************************************* +* +* AsmMono16Loop +* asm optimized monotonic loop version; one loop is 2 frames +* Input: +* in : pointer on input samples +* maxOutPt : pointer on first not filled +* maxInIdx : index on first not used +* outputIndex : pointer on current output index +* out : pointer on output buffer +* inputIndex : pointer on current input index +* vl, vr : left and right gain +* phaseFraction : pointer on current phase fraction +* phaseIncrement +* Ouput: +* outputIndex : +* out : updated buffer +* inputIndex : index of next to use +* phaseFraction : phase fraction for next interpolation +* +*******************************************************************/ +void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement) +{ +#define MO_PARAM5 "36" // offset of parameter 5 (outputIndex) + + asm( + "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" + // get parameters + " ldr r6, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction + " ldr r6, [r6]\n" // phaseFraction + " ldr r7, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex + " ldr r7, [r7]\n" // inputIndex + " ldr r8, [sp, #" MO_PARAM5 " + 4]\n" // out + " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex + " ldr r0, [r0]\n" // outputIndex + " add r8, r0, asl #2\n" // curOut + " ldr r9, [sp, #" MO_PARAM5 " + 24]\n" // phaseIncrement + " ldr r10, [sp, #" MO_PARAM5 " + 12]\n" // vl + " ldr r11, [sp, #" MO_PARAM5 " + 16]\n" // vr + + // r0 pin, x0, Samp + + // r1 in + // r2 maxOutPt + // r3 maxInIdx + + // r4 x1, i1, i3, Out1 + // r5 out0 + + // r6 frac + // r7 inputIndex + // r8 curOut + + // r9 inc + // r10 vl + // r11 vr + + // r12 + // r13 sp + // r14 + + // the following loop works on 2 frames + + ".Y4L01:\n" + " cmp r8, r2\n" // curOut - maxCurOut + " bcs .Y4L02\n" + +#define MO_ONE_FRAME \ + " add r0, r1, r7, asl #1\n" /* in + inputIndex */\ + " ldrsh r4, [r0]\n" /* in[inputIndex] */\ + " ldr r5, [r8]\n" /* out[outputIndex] */\ + " ldrsh r0, [r0, #-2]\n" /* in[inputIndex-1] */\ + " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\ + " sub r4, r4, r0\n" /* in[inputIndex] - in[inputIndex-1] */\ + " mov r4, r4, lsl #2\n" /* <<2 */\ + " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\ + " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\ + " add r0, r0, r4\n" /* x0 - (..) */\ + " mla r5, r0, r10, r5\n" /* vl*interp + out[] */\ + " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\ + " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\ + " mla r4, r0, r11, r4\n" /* vr*interp + out[] */\ + " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */\ + " str r4, [r8], #4\n" /* out[outputIndex++] = ... */ + + MO_ONE_FRAME // frame 1 + MO_ONE_FRAME // frame 2 + + " cmp r7, r3\n" // inputIndex - maxInIdx + " bcc .Y4L01\n" + ".Y4L02:\n" + + " bic r6, r6, #0xC0000000\n" // phaseFraction & ... + // save modified values + " ldr r0, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction + " str r6, [r0]\n" // phaseFraction + " ldr r0, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex + " str r7, [r0]\n" // inputIndex + " ldr r0, [sp, #" MO_PARAM5 " + 4]\n" // out + " sub r8, r0\n" // curOut - out + " asr r8, #2\n" // new outputIndex + " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex + " str r8, [r0]\n" // save outputIndex + + " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n" + ); +} + +/******************************************************************* +* +* AsmStereo16Loop +* asm optimized stereo loop version; one loop is 2 frames +* Input: +* in : pointer on input samples +* maxOutPt : pointer on first not filled +* maxInIdx : index on first not used +* outputIndex : pointer on current output index +* out : pointer on output buffer +* inputIndex : pointer on current input index +* vl, vr : left and right gain +* phaseFraction : pointer on current phase fraction +* phaseIncrement +* Ouput: +* outputIndex : +* out : updated buffer +* inputIndex : index of next to use +* phaseFraction : phase fraction for next interpolation +* +*******************************************************************/ +void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement) +{ +#define ST_PARAM5 "40" // offset of parameter 5 (outputIndex) + asm( + "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n" + // get parameters + " ldr r6, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction + " ldr r6, [r6]\n" // phaseFraction + " ldr r7, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex + " ldr r7, [r7]\n" // inputIndex + " ldr r8, [sp, #" ST_PARAM5 " + 4]\n" // out + " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex + " ldr r0, [r0]\n" // outputIndex + " add r8, r0, asl #2\n" // curOut + " ldr r9, [sp, #" ST_PARAM5 " + 24]\n" // phaseIncrement + " ldr r10, [sp, #" ST_PARAM5 " + 12]\n" // vl + " ldr r11, [sp, #" ST_PARAM5 " + 16]\n" // vr + + // r0 pin, x0, Samp + + // r1 in + // r2 maxOutPt + // r3 maxInIdx + + // r4 x1, i1, i3, out1 + // r5 out0 + + // r6 frac + // r7 inputIndex + // r8 curOut + + // r9 inc + // r10 vl + // r11 vr + + // r12 temporary + // r13 sp + // r14 + + ".Y5L01:\n" + " cmp r8, r2\n" // curOut - maxCurOut + " bcs .Y5L02\n" + +#define ST_ONE_FRAME \ + " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\ +\ + " add r0, r1, r7, asl #2\n" /* in + 2*inputIndex */\ +\ + " ldrsh r4, [r0]\n" /* in[2*inputIndex] */\ + " ldr r5, [r8]\n" /* out[outputIndex] */\ + " ldrsh r12, [r0, #-4]\n" /* in[2*inputIndex-2] */\ + " sub r4, r4, r12\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\ + " mov r4, r4, lsl #2\n" /* <<2 */\ + " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\ + " add r12, r12, r4\n" /* x0 - (..) */\ + " mla r5, r12, r10, r5\n" /* vl*interp + out[] */\ + " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\ + " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\ +\ + " ldrsh r12, [r0, #+2]\n" /* in[2*inputIndex+1] */\ + " ldrsh r0, [r0, #-2]\n" /* in[2*inputIndex-1] */\ + " sub r12, r12, r0\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\ + " mov r12, r12, lsl #2\n" /* <<2 */\ + " smulwt r12, r12, r6\n" /* (x1-x0)*.. */\ + " add r12, r0, r12\n" /* x0 - (..) */\ + " mla r4, r12, r11, r4\n" /* vr*interp + out[] */\ + " str r4, [r8], #4\n" /* out[outputIndex++] = ... */\ +\ + " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\ + " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */ + + ST_ONE_FRAME // frame 1 + ST_ONE_FRAME // frame 1 + + " cmp r7, r3\n" // inputIndex - maxInIdx + " bcc .Y5L01\n" + ".Y5L02:\n" + + " bic r6, r6, #0xC0000000\n" // phaseFraction & ... + // save modified values + " ldr r0, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction + " str r6, [r0]\n" // phaseFraction + " ldr r0, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex + " str r7, [r0]\n" // inputIndex + " ldr r0, [sp, #" ST_PARAM5 " + 4]\n" // out + " sub r8, r0\n" // curOut - out + " asr r8, #2\n" // new outputIndex + " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex + " str r8, [r0]\n" // save outputIndex + + " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}\n" + ); +} + +#endif // ASM_ARM_RESAMP1 + + +// ---------------------------------------------------------------------------- +} +; // namespace android + diff --git a/libs/audioflinger/AudioResampler.h b/libs/audioflinger/AudioResampler.h new file mode 100644 index 000000000..39656c032 --- /dev/null +++ b/libs/audioflinger/AudioResampler.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_AUDIO_RESAMPLER_H +#define ANDROID_AUDIO_RESAMPLER_H + +#include +#include + +#include "AudioBufferProvider.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class AudioResampler { +public: + // Determines quality of SRC. + // LOW_QUALITY: linear interpolator (1st order) + // MED_QUALITY: cubic interpolator (3rd order) + // HIGH_QUALITY: fixed multi-tap FIR (e.g. 48KHz->44.1KHz) + // NOTE: high quality SRC will only be supported for + // certain fixed rate conversions. Sample rate cannot be + // changed dynamically. + enum src_quality { + DEFAULT=0, + LOW_QUALITY=1, + MED_QUALITY=2, + HIGH_QUALITY=3 + }; + + static AudioResampler* create(int bitDepth, int inChannelCount, + int32_t sampleRate, int quality=DEFAULT); + + virtual ~AudioResampler(); + + virtual void init() = 0; + virtual void setSampleRate(int32_t inSampleRate); + virtual void setVolume(int16_t left, int16_t right); + + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) = 0; + +protected: + // number of bits for phase fraction - 30 bits allows nearly 2x downsampling + static const int kNumPhaseBits = 30; + + // phase mask for fraction + static const uint32_t kPhaseMask = (1LU< +#include +#include +#include + +#include "AudioResampler.h" +#include "AudioResamplerCubic.h" + +#define LOG_TAG "AudioSRC" + +namespace android { +// ---------------------------------------------------------------------------- + +void AudioResamplerCubic::init() { + memset(&left, 0, sizeof(state)); + memset(&right, 0, sizeof(state)); +} + +void AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + // should never happen, but we overflow if it does + // LOG_ASSERT(outFrameCount < 32767); + + // select the appropriate resampler + switch (mChannelCount) { + case 1: + resampleMono16(out, outFrameCount, provider); + break; + case 2: + resampleStereo16(out, outFrameCount, provider); + break; + } +} + +void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // fetch first buffer + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + return; + // LOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); + } + int16_t *in = mBuffer.i16; + + while (outputIndex < outputSampleCount) { + int32_t sample; + int32_t x; + + // calculate output sample + x = phaseFraction >> kPreInterpShift; + out[outputIndex++] += vl * interp(&left, x); + out[outputIndex++] += vr * interp(&right, x); + // out[outputIndex++] += vr * in[inputIndex*2]; + + // increment phase + phaseFraction += phaseIncrement; + uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits); + phaseFraction &= kPhaseMask; + + // time to fetch another sample + while (indexIncrement--) { + + inputIndex++; + if (inputIndex == mBuffer.frameCount) { + inputIndex = 0; + provider->releaseBuffer(&mBuffer); + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + goto save_state; // ugly, but efficient + in = mBuffer.i16; + // LOGW("New buffer: offset=%p, frames=%d\n", mBuffer.raw, mBuffer.frameCount); + } + + // advance sample state + advance(&left, in[inputIndex*2]); + advance(&right, in[inputIndex*2+1]); + } + } + +save_state: + // LOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction); + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // fetch first buffer + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + return; + // LOGW("New buffer: offset=%p, frames=%d\n", mBuffer.raw, mBuffer.frameCount); + } + int16_t *in = mBuffer.i16; + + while (outputIndex < outputSampleCount) { + int32_t sample; + int32_t x; + + // calculate output sample + x = phaseFraction >> kPreInterpShift; + sample = interp(&left, x); + out[outputIndex++] += vl * sample; + out[outputIndex++] += vr * sample; + + // increment phase + phaseFraction += phaseIncrement; + uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits); + phaseFraction &= kPhaseMask; + + // time to fetch another sample + while (indexIncrement--) { + + inputIndex++; + if (inputIndex == mBuffer.frameCount) { + inputIndex = 0; + provider->releaseBuffer(&mBuffer); + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + goto save_state; // ugly, but efficient + // LOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); + in = mBuffer.i16; + } + + // advance sample state + advance(&left, in[inputIndex]); + } + } + +save_state: + // LOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction); + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +// ---------------------------------------------------------------------------- +} +; // namespace android + diff --git a/libs/audioflinger/AudioResamplerCubic.h b/libs/audioflinger/AudioResamplerCubic.h new file mode 100644 index 000000000..b72b62a50 --- /dev/null +++ b/libs/audioflinger/AudioResamplerCubic.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_AUDIO_RESAMPLER_CUBIC_H +#define ANDROID_AUDIO_RESAMPLER_CUBIC_H + +#include +#include +#include + +#include "AudioResampler.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class AudioResamplerCubic : public AudioResampler { +public: + AudioResamplerCubic(int bitDepth, int inChannelCount, int32_t sampleRate) : + AudioResampler(bitDepth, inChannelCount, sampleRate) { + } + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +private: + // number of bits used in interpolation multiply - 14 bits avoids overflow + static const int kNumInterpBits = 14; + + // bits to shift the phase fraction down to avoid overflow + static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits; + typedef struct { + int32_t a, b, c, y0, y1, y2, y3; + } state; + void init(); + void resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + void resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + static inline int32_t interp(state* p, int32_t x) { + return (((((p->a * x >> 14) + p->b) * x >> 14) + p->c) * x >> 14) + p->y1; + } + static inline void advance(state* p, int16_t in) { + p->y0 = p->y1; + p->y1 = p->y2; + p->y2 = p->y3; + p->y3 = in; + p->a = (3 * (p->y1 - p->y2) - p->y0 + p->y3) >> 1; + p->b = (p->y2 << 1) + p->y0 - (((5 * p->y1 + p->y3)) >> 1); + p->c = (p->y2 - p->y0) >> 1; + } + state left, right; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_CUBIC_H*/ diff --git a/libs/audioflinger/AudioResamplerSinc.cpp b/libs/audioflinger/AudioResamplerSinc.cpp new file mode 100644 index 000000000..9e5e25478 --- /dev/null +++ b/libs/audioflinger/AudioResamplerSinc.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2007 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 "AudioResamplerSinc.h" + +namespace android { +// ---------------------------------------------------------------------------- + + +/* + * These coeficients are computed with the "fir" utility found in + * tools/resampler_tools + * TODO: A good optimization would be to transpose this matrix, to take + * better advantage of the data-cache. + */ +const int32_t AudioResamplerSinc::mFirCoefsUp[] = { + 0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621, + 0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9, + 0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9, + 0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798, + 0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636, + 0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2, + 0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070, + 0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, + 0x00000000 // this one is needed for lerping the last coefficient +}; + +/* + * These coefficients are optimized for 48KHz -> 44.1KHz (stop-band at 22.050KHz) + * It's possible to use the above coefficient for any down-sampling + * at the expense of a slower processing loop (we can interpolate + * these coefficient from the above by "Stretching" them in time). + */ +const int32_t AudioResamplerSinc::mFirCoefsDown[] = { + 0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540, + 0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4, + 0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa, + 0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066, + 0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf, + 0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d, + 0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a, + 0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000, + 0x00000000 // this one is needed for lerping the last coefficient +}; + +// ---------------------------------------------------------------------------- + +static inline +int32_t mulRL(int left, int32_t in, uint32_t vRL) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smultb %[out], %[in], %[vRL] \n" + : [out]"=r"(out) + : [in]"%r"(in), [vRL]"r"(vRL) + : ); + } else { + asm( "smultt %[out], %[in], %[vRL] \n" + : [out]"=r"(out) + : [in]"%r"(in), [vRL]"r"(vRL) + : ); + } + return out; +#else + if (left) { + return int16_t(in>>16) * int16_t(vRL&0xFFFF); + } else { + return int16_t(in>>16) * int16_t(vRL>>16); + } +#endif +} + +static inline +int32_t mulAdd(int16_t in, int32_t v, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + asm( "smlawb %[out], %[v], %[in], %[a] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v), [a]"r"(a) + : ); + return out; +#else + return a + in * (v>>16); + // improved precision + // return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16); +#endif +} + +static inline +int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smlawb %[out], %[v], %[inRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) + : ); + } else { + asm( "smlawt %[out], %[v], %[inRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) + : ); + } + return out; +#else + if (left) { + return a + (int16_t(inRL&0xFFFF) * (v>>16)); + //improved precision + // return a + (int16_t(inRL&0xFFFF) * (v>>16)) + ((int16_t(inRL&0xFFFF) * (v & 0xffff)) >> 16); + } else { + return a + (int16_t(inRL>>16) * (v>>16)); + } +#endif +} + +// ---------------------------------------------------------------------------- + +AudioResamplerSinc::AudioResamplerSinc(int bitDepth, + int inChannelCount, int32_t sampleRate) + : AudioResampler(bitDepth, inChannelCount, sampleRate), + mState(0) +{ + /* + * Layout of the state buffer for 32 tap: + * + * "present" sample beginning of 2nd buffer + * v v + * 0 01 2 23 3 + * 0 F0 0 F0 F + * [pppppppppppppppInnnnnnnnnnnnnnnnpppppppppppppppInnnnnnnnnnnnnnnn] + * ^ ^ head + * + * p = past samples, convoluted with the (p)ositive side of sinc() + * n = future samples, convoluted with the (n)egative side of sinc() + * r = extra space for implementing the ring buffer + * + */ + + const size_t numCoefs = 2*halfNumCoefs; + const size_t stateSize = numCoefs * inChannelCount * 2; + mState = new int16_t[stateSize]; + memset(mState, 0, sizeof(int16_t)*stateSize); + mImpulse = mState + (halfNumCoefs-1)*inChannelCount; + mRingFull = mImpulse + (numCoefs+1)*inChannelCount; +} + +AudioResamplerSinc::~AudioResamplerSinc() +{ + delete [] mState; +} + +void AudioResamplerSinc::init() { +} + +void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) +{ + mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown; + + // select the appropriate resampler + switch (mChannelCount) { + case 1: + resample<1>(out, outFrameCount, provider); + break; + case 2: + resample<2>(out, outFrameCount, provider); + break; + } +} + + +template +void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) +{ + int16_t* impulse = mImpulse; + uint32_t vRL = mVolumeRL; + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + AudioBufferProvider::Buffer& buffer(mBuffer); + while (outputIndex < outputSampleCount) { + // buffer is empty, fetch a new one + while (buffer.frameCount == 0) { + buffer.frameCount = inFrameCount; + provider->getNextBuffer(&buffer); + if (buffer.raw == NULL) { + goto resample_exit; + } + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + if (phaseIndex == 1) { + // read one frame + read(impulse, phaseFraction, buffer.i16, inputIndex); + } else if (phaseIndex == 2) { + // read 2 frames + read(impulse, phaseFraction, buffer.i16, inputIndex); + inputIndex++; + if (inputIndex >= mBuffer.frameCount) { + inputIndex -= mBuffer.frameCount; + provider->releaseBuffer(&buffer); + } else { + read(impulse, phaseFraction, buffer.i16, inputIndex); + } + } + } + int16_t *in = buffer.i16; + const size_t frameCount = buffer.frameCount; + + // Always read-in the first samples from the input buffer + int16_t* head = impulse + halfNumCoefs*CHANNELS; + head[0] = in[inputIndex*CHANNELS + 0]; + if (CHANNELS == 2) + head[1] = in[inputIndex*CHANNELS + 1]; + + // handle boundary case + int32_t l, r; + while (outputIndex < outputSampleCount) { + filterCoefficient(l, r, phaseFraction, impulse); + out[outputIndex++] += 2 * mulRL(1, l, vRL); + out[outputIndex++] += 2 * mulRL(0, r, vRL); + + phaseFraction += phaseIncrement; + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + if (phaseIndex == 1) { + inputIndex++; + if (inputIndex >= frameCount) + break; // need a new buffer + read(impulse, phaseFraction, in, inputIndex); + } else if(phaseIndex == 2) { // maximum value + inputIndex++; + if (inputIndex >= frameCount) + break; // 0 frame available, 2 frames needed + // read first frame + read(impulse, phaseFraction, in, inputIndex); + inputIndex++; + if (inputIndex >= frameCount) + break; // 0 frame available, 1 frame needed + // read second frame + read(impulse, phaseFraction, in, inputIndex); + } + } + + // if done with buffer, save samples + if (inputIndex >= frameCount) { + inputIndex -= frameCount; + provider->releaseBuffer(&buffer); + } + } + +resample_exit: + mImpulse = impulse; + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +template +/*** +* read() +* +* This function reads only one frame from input buffer and writes it in +* state buffer +* +**/ +void AudioResamplerSinc::read( + int16_t*& impulse, uint32_t& phaseFraction, + int16_t const* in, size_t inputIndex) +{ + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + impulse += CHANNELS; + phaseFraction -= 1LU<= mRingFull) { + const size_t stateSize = (halfNumCoefs*2)*CHANNELS; + memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize); + impulse -= stateSize; + } + int16_t* head = impulse + halfNumCoefs*CHANNELS; + head[0] = in[inputIndex*CHANNELS + 0]; + if (CHANNELS == 2) + head[1] = in[inputIndex*CHANNELS + 1]; +} + +template +void AudioResamplerSinc::filterCoefficient( + int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples) +{ + // compute the index of the coefficient on the positive side and + // negative side + uint32_t indexP = (phase & cMask) >> cShift; + uint16_t lerpP = (phase & pMask) >> pShift; + uint32_t indexN = (-phase & cMask) >> cShift; + uint16_t lerpN = (-phase & pMask) >> pShift; + if ((indexP == 0) && (lerpP == 0)) { + indexN = cMask >> cShift; + lerpN = pMask >> pShift; + } + + l = 0; + r = 0; + int32_t const* coefs = mFirCoefs; + int16_t const *sP = samples; + int16_t const *sN = samples+CHANNELS; + for (unsigned int i=0 ; i(l, r, coefs+indexP, lerpP, sP); + interpolate(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<(l, r, coefs+indexP, lerpP, sP); + interpolate(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<(l, r, coefs+indexP, lerpP, sP); + interpolate(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<(l, r, coefs+indexP, lerpP, sP); + interpolate(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1< +void AudioResamplerSinc::interpolate( + int32_t& l, int32_t& r, + int32_t const* coefs, int16_t lerp, int16_t const* samples) +{ + int32_t c0 = coefs[0]; + int32_t c1 = coefs[1]; + int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0); + if (CHANNELS == 2) { + uint32_t rl = *reinterpret_cast(samples); + l = mulAddRL(1, rl, sinc, l); + r = mulAddRL(0, rl, sinc, r); + } else { + r = l = mulAdd(samples[0], sinc, l); + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/libs/audioflinger/AudioResamplerSinc.h b/libs/audioflinger/AudioResamplerSinc.h new file mode 100644 index 000000000..e6cb90b80 --- /dev/null +++ b/libs/audioflinger/AudioResamplerSinc.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_AUDIO_RESAMPLER_SINC_H +#define ANDROID_AUDIO_RESAMPLER_SINC_H + +#include +#include +#include + +#include "AudioResampler.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioResamplerSinc : public AudioResampler { +public: + AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate); + + ~AudioResamplerSinc(); + + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +private: + void init(); + + template + void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + + template + inline void filterCoefficient( + int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples); + + template + inline void interpolate( + int32_t& l, int32_t& r, + int32_t const* coefs, int16_t lerp, int16_t const* samples); + + template + inline void read(int16_t*& impulse, uint32_t& phaseFraction, + int16_t const* in, size_t inputIndex); + + int16_t *mState; + int16_t *mImpulse; + int16_t *mRingFull; + + int32_t const * mFirCoefs; + static const int32_t mFirCoefsDown[]; + static const int32_t mFirCoefsUp[]; + + // ---------------------------------------------------------------------------- + static const int32_t RESAMPLE_FIR_NUM_COEF = 8; + static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 4; + + // we have 16 coefs samples per zero-crossing + static const int coefsBits = RESAMPLE_FIR_LERP_INT_BITS; // 4 + static const int cShift = kNumPhaseBits - coefsBits; // 26 + static const uint32_t cMask = ((1< +#include +#include + +namespace android { + +class Barrier +{ +public: + inline Barrier() : state(CLOSED) { } + inline ~Barrier() { } + void open() { + // gcc memory barrier, this makes sure all memory writes + // have been issued by gcc. On an SMP system we'd need a real + // h/w barrier. + asm volatile ("":::"memory"); + Mutex::Autolock _l(lock); + state = OPENED; + cv.broadcast(); + } + void close() { + Mutex::Autolock _l(lock); + state = CLOSED; + } + void wait() const { + Mutex::Autolock _l(lock); + while (state == CLOSED) { + cv.wait(lock); + } + } +private: + enum { OPENED, CLOSED }; + mutable Mutex lock; + mutable Condition cv; + volatile int state; +}; + +}; // namespace android + +#endif // ANDROID_BARRIER_H diff --git a/libs/surfaceflinger/BlurFilter.cpp b/libs/surfaceflinger/BlurFilter.cpp new file mode 100644 index 000000000..5dc0ba057 --- /dev/null +++ b/libs/surfaceflinger/BlurFilter.cpp @@ -0,0 +1,326 @@ +/* +** +** Copyright 2006, 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 + +#include + +#include "clz.h" + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { + +#if BYTE_ORDER == LITTLE_ENDIAN +inline uint32_t BLUR_RGBA_TO_HOST(uint32_t v) { + return v; +} +inline uint32_t BLUR_HOST_TO_RGBA(uint32_t v) { + return v; +} +#else +inline uint32_t BLUR_RGBA_TO_HOST(uint32_t v) { + return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00); +} +inline uint32_t BLUR_HOST_TO_RGBA(uint32_t v) { + return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00); +} +#endif + +const int BLUR_DITHER_BITS = 6; // dither weights stored on 6 bits +const int BLUR_DITHER_ORDER_SHIFT= 3; +const int BLUR_DITHER_ORDER = (1< +struct BlurColor565 +{ + typedef uint16_t type; + int r, g, b; + inline BlurColor565() { } + inline BlurColor565(uint16_t v) { + r = v >> 11; + g = (v >> 5) & 0x3E; + b = v & 0x1F; + } + inline void clear() { r=g=b=0; } + inline uint16_t to(int shift, int last, int dither) const { + int R = r; + int G = g; + int B = b; + if (UNLIKELY(last)) { + if (FACTOR>0) { + int L = (R+G+B)>>1; + R += (((L>>1) - R) * FACTOR) >> 8; + G += (((L ) - G) * FACTOR) >> 8; + B += (((L>>1) - B) * FACTOR) >> 8; + } + R += (dither << shift) >> BLUR_DITHER_BITS; + G += (dither << shift) >> BLUR_DITHER_BITS; + B += (dither << shift) >> BLUR_DITHER_BITS; + } + R >>= shift; + G >>= shift; + B >>= shift; + return (R<<11) | (G<<5) | B; + } + inline BlurColor565& operator += (const BlurColor565& rhs) { + r += rhs.r; + g += rhs.g; + b += rhs.b; + return *this; + } + inline BlurColor565& operator -= (const BlurColor565& rhs) { + r -= rhs.r; + g -= rhs.g; + b -= rhs.b; + return *this; + } +}; + +struct BlurGray565 +{ + typedef uint16_t type; + int l; + inline BlurGray565() { } + inline BlurGray565(uint16_t v) { + int r = v >> 11; + int g = (v >> 5) & 0x3F; + int b = v & 0x1F; + l = (r + g + b + 1)>>1; + } + inline void clear() { l=0; } + inline uint16_t to(int shift, int last, int dither) const { + int L = l; + if (UNLIKELY(last)) { + L += (dither << shift) >> BLUR_DITHER_BITS; + } + L >>= shift; + return ((L>>1)<<11) | (L<<5) | (L>>1); + } + inline BlurGray565& operator += (const BlurGray565& rhs) { + l += rhs.l; + return *this; + } + inline BlurGray565& operator -= (const BlurGray565& rhs) { + l -= rhs.l; + return *this; + } +}; + +struct BlurGray8888 +{ + typedef uint32_t type; + int l, a; + inline BlurGray8888() { } + inline BlurGray8888(uint32_t v) { + v = BLUR_RGBA_TO_HOST(v); + int r = v & 0xFF; + int g = (v >> 8) & 0xFF; + int b = (v >> 16) & 0xFF; + a = v >> 24; + l = r + g + g + b; + } + inline void clear() { l=a=0; } + inline uint32_t to(int shift, int last, int dither) const { + int L = l; + int A = a; + if (UNLIKELY(last)) { + L += (dither << (shift+2)) >> BLUR_DITHER_BITS; + A += (dither << shift) >> BLUR_DITHER_BITS; + } + L >>= (shift+2); + A >>= shift; + return BLUR_HOST_TO_RGBA((A<<24) | (L<<16) | (L<<8) | L); + } + inline BlurGray8888& operator += (const BlurGray8888& rhs) { + l += rhs.l; + a += rhs.a; + return *this; + } + inline BlurGray8888& operator -= (const BlurGray8888& rhs) { + l -= rhs.l; + a -= rhs.a; + return *this; + } +}; + + +template +static status_t blurFilter( + GGLSurface const* dst, + GGLSurface const* src, + int kernelSizeUser, + int repeat) +{ + typedef typename PIXEL::type TYPE; + + const int shift = 31 - clz(kernelSizeUser); + const int areaShift = shift*2; + const int kernelSize = 1<width; + const int h = src->height; + const uint8_t* ditherMatrix = gDitherMatrix; + + // we need a temporary buffer to store one line of blurred columns + // as well as kernelSize lines of source pixels organized as a ring buffer. + void* const temporary_buffer = malloc( + (w + kernelSize) * sizeof(PIXEL) + + (src->stride * kernelSize) * sizeof(TYPE)); + if (!temporary_buffer) + return NO_MEMORY; + + PIXEL* const sums = (PIXEL*)temporary_buffer; + TYPE* const scratch = (TYPE*)(sums + w + kernelSize); + + // Apply the blur 'repeat' times, this is used to approximate + // gaussian blurs. 3 times gives good results. + for (int k=0 ; kstride*kernelHalfSize, + src->data, + src->stride*kernelHalfSize*sizeof(TYPE)); + + // sum half of each column, because we assume the first half is + // zeros (black/transparent). + for (int y=0 ; ydata + y*src->stride; + for (int x=0 ; xdata + y*dst->stride; + + // compute the dither matrix line + uint8_t const * ditherY = ditherMatrix + + (y & BLUR_DITHER_MASK)*BLUR_DITHER_ORDER; + + // Horizontal blur pass on the columns sums + int count, dither, x=0; + PIXEL const * out= sums; + PIXEL const * in = sums; + current.clear(); + + count = kernelHalfSize; + do { + current += *in; + in++; + } while (--count); + + count = kernelHalfSize; + do { + current += *in; + dither = *(ditherY + ((x++)&BLUR_DITHER_MASK)); + *fb++ = current.to(areaShift, k==repeat-1, dither); + in++; + } while (--count); + + count = w-kernelSize; + do { + current += *in; + current -= *out; + dither = *(ditherY + ((x++)&BLUR_DITHER_MASK)); + *fb++ = current.to(areaShift, k==repeat-1, dither); + in++, out++; + } while (--count); + + count = kernelHalfSize; + do { + current -= *out; + dither = *(ditherY + ((x++)&BLUR_DITHER_MASK)); + *fb++ = current.to(areaShift, k==repeat-1, dither); + out++; + } while (--count); + + // vertical blur pass, subtract the oldest line from each columns + // and add a new line. Subtract or add zeros at the top + // and bottom edges. + TYPE* const tail = scratch + (y & mask) * src->stride; + if (y >= kernelHalfSize) { + for (int x=0 ; xdata + (y+kernelHalfSize)*src->stride, + src->stride*sizeof(TYPE)); + for (int x=0 ; x >( + GGLSurface const* dst, + GGLSurface const* src, + int kernelSizeUser, + int repeat); + +status_t blurFilter( + GGLSurface const* image, + int kernelSizeUser, + int repeat) +{ + return blurFilter< BlurColor565<0x80> >(image, image, kernelSizeUser, repeat); +} + +} // namespace android + +//err = blur< BlurColor565<0x80> >(dst, src, kernelSizeUser, repeat); +//err = blur(dst, src, kernelSizeUser, repeat); +//err = blur(dst, src, kernelSizeUser, repeat); diff --git a/libs/surfaceflinger/BlurFilter.h b/libs/surfaceflinger/BlurFilter.h new file mode 100644 index 000000000..294db43cb --- /dev/null +++ b/libs/surfaceflinger/BlurFilter.h @@ -0,0 +1,35 @@ +/* +** +** Copyright 2006, 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. +*/ + +#ifndef ANDROID_BLUR_FILTER_H +#define ANDROID_BLUR_FILTER_H + +#include +#include + +#include + +namespace android { + +status_t blurFilter( + GGLSurface const* image, + int kernelSizeUser, + int repeat); + +} // namespace android + +#endif // ANDROID_BLUR_FILTER_H diff --git a/libs/surfaceflinger/BootAnimation.cpp b/libs/surfaceflinger/BootAnimation.cpp new file mode 100644 index 000000000..2b30336b7 --- /dev/null +++ b/libs/surfaceflinger/BootAnimation.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "BootAnimation" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "BootAnimation.h" + +namespace android { + +// --------------------------------------------------------------------------- + +BootAnimation::BootAnimation(const sp& composer) : + Thread(false) { + mSession = SurfaceComposerClient::clientForConnection( + composer->createConnection()->asBinder()); +} + +BootAnimation::~BootAnimation() { +} + +void BootAnimation::onFirstRef() { + run("BootAnimation", PRIORITY_DISPLAY); +} + +const sp& BootAnimation::session() const { + return mSession; +} + +status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, + const char* name) { + Asset* asset = assets.open(name, Asset::ACCESS_BUFFER); + if (!asset) + return NO_INIT; + SkBitmap bitmap; + SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), + &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode); + asset->close(); + delete asset; + + // ensure we can call getPixels(). No need to call unlock, since the + // bitmap will go out of scope when we return from this method. + bitmap.lockPixels(); + + const int w = bitmap.width(); + const int h = bitmap.height(); + const void* p = bitmap.getPixels(); + + GLint crop[4] = { 0, h, w, -h }; + texture->w = w; + texture->h = h; + + glGenTextures(1, &texture->name); + glBindTexture(GL_TEXTURE_2D, texture->name); + + switch (bitmap.getConfig()) { + case SkBitmap::kA8_Config: + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, + GL_UNSIGNED_BYTE, p); + break; + case SkBitmap::kARGB_4444_Config: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_SHORT_4_4_4_4, p); + break; + case SkBitmap::kARGB_8888_Config: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, p); + break; + case SkBitmap::kRGB_565_Config: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, + GL_UNSIGNED_SHORT_5_6_5, p); + break; + default: + break; + } + + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + return NO_ERROR; +} + +status_t BootAnimation::readyToRun() { + mAssets.addDefaultAssets(); + + DisplayInfo dinfo; + status_t status = session()->getDisplayInfo(0, &dinfo); + if (status) + return -1; + + // create the native surface + sp s = session()->createSurface(getpid(), 0, dinfo.w, dinfo.h, + PIXEL_FORMAT_RGB_565); + session()->openTransaction(); + s->setLayer(0x40000000); + session()->closeTransaction(); + + // initialize opengl and egl + const EGLint attribs[] = { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 0, EGL_NONE }; + EGLint w, h, dummy; + EGLint numConfigs; + EGLConfig config; + EGLSurface surface; + EGLContext context; + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglChooseConfig(display, attribs, &config, 1, &numConfigs); + + mNativeWindowSurface = new EGLNativeWindowSurface(s); + surface = eglCreateWindowSurface(display, config, + mNativeWindowSurface.get(), NULL); + + context = eglCreateContext(display, config, NULL, NULL); + eglQuerySurface(display, surface, EGL_WIDTH, &w); + eglQuerySurface(display, surface, EGL_HEIGHT, &h); + eglMakeCurrent(display, surface, surface, context); + mDisplay = display; + mContext = context; + mSurface = surface; + mWidth = w; + mHeight = h; + mFlingerSurface = s; + + // initialize GL + glShadeModel(GL_FLAT); + glEnable(GL_DITHER); + glEnable(GL_TEXTURE_2D); + glEnable(GL_SCISSOR_TEST); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + return NO_ERROR; +} + +void BootAnimation::requestExit() { + mBarrier.open(); + Thread::requestExit(); +} + +bool BootAnimation::threadLoop() { + bool r = android(); + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(mDisplay, mContext); + eglDestroySurface(mDisplay, mSurface); + mNativeWindowSurface.clear(); + return r; +} + +bool BootAnimation::android() { + initTexture(&mAndroid[0], mAssets, "images/android_320x480.png"); + initTexture(&mAndroid[1], mAssets, "images/boot_robot.png"); + initTexture(&mAndroid[2], mAssets, "images/boot_robot_glow.png"); + + // erase screen + glDisable(GL_SCISSOR_TEST); + glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); + + // clear screen + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mDisplay, mSurface); + + // wait ~1s + usleep(800000); + + // fade in + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + const int steps = 8; + for (int i = 1; i < steps; i++) { + float fade = i / float(steps); + glColor4f(1, 1, 1, fade * fade); + glClear(GL_COLOR_BUFFER_BIT); + glDrawTexiOES(0, 0, 0, mAndroid[0].w, mAndroid[0].h); + eglSwapBuffers(mDisplay, mSurface); + } + + // draw last frame + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDisable(GL_BLEND); + glDrawTexiOES(0, 0, 0, mAndroid[0].w, mAndroid[0].h); + eglSwapBuffers(mDisplay, mSurface); + + // update rect for the robot + const int x = mWidth - mAndroid[1].w - 33; + const int y = (mHeight - mAndroid[1].h) / 2 - 1; + const Rect updateRect(x, y, x + mAndroid[1].w, y + mAndroid[1].h); + + // draw and update only what we need + mNativeWindowSurface->setSwapRectangle(updateRect.left, + updateRect.top, updateRect.width(), updateRect.height()); + + glEnable(GL_SCISSOR_TEST); + glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), + updateRect.height()); + + const nsecs_t startTime = systemTime(); + do { + // glow speed and shape + nsecs_t time = systemTime() - startTime; + float t = ((4.0f / (360.0f * us2ns(16667))) * time); + t = t - floorf(t); + const float fade = 0.5f + 0.5f * sinf(t * 2 * M_PI); + + // fade the glow in and out + glDisable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, mAndroid[2].name); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4f(fade, fade, fade, fade); + glDrawTexiOES(updateRect.left, mHeight - updateRect.bottom, 0, + updateRect.width(), updateRect.height()); + + // draw the robot + glEnable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, mAndroid[1].name); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDrawTexiOES(updateRect.left, mHeight - updateRect.bottom, 0, + updateRect.width(), updateRect.height()); + + // make sure sleep a lot to not take too much CPU away from + // the boot process. With this "glow" animation there is no + // visible difference. + usleep(16667 * 4); + + eglSwapBuffers(mDisplay, mSurface); + } while (!exitPending()); + + glDeleteTextures(1, &mAndroid[0].name); + glDeleteTextures(1, &mAndroid[1].name); + glDeleteTextures(1, &mAndroid[2].name); + return false; +} + +bool BootAnimation::cylon() { + // initialize the textures... + initTexture(&mLeftTrail, mAssets, "images/cylon_left.png"); + initTexture(&mRightTrail, mAssets, "images/cylon_right.png"); + initTexture(&mBrightSpot, mAssets, "images/cylon_dot.png"); + + int w = mWidth; + int h = mHeight; + + const Point c(w / 2, h / 2); + const GLint amplitude = 60; + const int scx = c.x - amplitude - mBrightSpot.w / 2; + const int scy = c.y - mBrightSpot.h / 2; + const int scw = amplitude * 2 + mBrightSpot.w; + const int sch = mBrightSpot.h; + const Rect updateRect(scx, h - scy - sch, scx + scw, h - scy); + + // erase screen + glDisable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT); + + eglSwapBuffers(mDisplay, mSurface); + + glClear(GL_COLOR_BUFFER_BIT); + + mNativeWindowSurface->setSwapRectangle(updateRect.left, + updateRect.top, updateRect.width(), updateRect.height()); + + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + // clear the screen to white + Point p; + float t = 0; + float alpha = 1.0f; + const nsecs_t startTime = systemTime(); + nsecs_t fadeTime = 0; + + do { + // Set scissor in interesting area + glScissor(scx, scy, scw, sch); + + // erase screen + glClear(GL_COLOR_BUFFER_BIT); + + // compute wave + const float a = (t * 2 * M_PI) - M_PI / 2; + const float sn = sinf(a); + const float cs = cosf(a); + GLint x = GLint(amplitude * sn); + float derivative = cs; + + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (derivative > 0) { + // vanishing trail... + p.x = (-amplitude + c.x) - mBrightSpot.w / 2; + p.y = c.y - mLeftTrail.h / 2; + float fade = 2.0f * (0.5f - t); + //fade *= fade; + glColor4f(fade, fade, fade, fade); + glBindTexture(GL_TEXTURE_2D, mLeftTrail.name); + glDrawTexiOES(p.x, p.y, 0, mLeftTrail.w, mLeftTrail.h); + + // trail... + p.x = (x + c.x) - (mRightTrail.w + mBrightSpot.w / 2) + 16; + p.y = c.y - mRightTrail.h / 2; + fade = t < 0.25f ? t * 4.0f : 1.0f; + fade *= fade; + glColor4f(fade, fade, fade, fade); + glBindTexture(GL_TEXTURE_2D, mRightTrail.name); + glDrawTexiOES(p.x, p.y, 0, mRightTrail.w, mRightTrail.h); + } else { + // vanishing trail.. + p.x = (amplitude + c.x) - (mRightTrail.w + mBrightSpot.w / 2) + 16; + p.y = c.y - mRightTrail.h / 2; + float fade = 2.0f * (0.5f - (t - 0.5f)); + //fade *= fade; + glColor4f(fade, fade, fade, fade); + glBindTexture(GL_TEXTURE_2D, mRightTrail.name); + glDrawTexiOES(p.x, p.y, 0, mRightTrail.w, mRightTrail.h); + + // trail... + p.x = (x + c.x) - mBrightSpot.w / 2; + p.y = c.y - mLeftTrail.h / 2; + fade = t < 0.5f + 0.25f ? (t - 0.5f) * 4.0f : 1.0f; + fade *= fade; + glColor4f(fade, fade, fade, fade); + glBindTexture(GL_TEXTURE_2D, mLeftTrail.name); + glDrawTexiOES(p.x, p.y, 0, mLeftTrail.w, mLeftTrail.h); + } + + const Point p(x + c.x - mBrightSpot.w / 2, c.y - mBrightSpot.h / 2); + glBindTexture(GL_TEXTURE_2D, mBrightSpot.name); + glColor4f(1, 0.5, 0.5, 1); + glDrawTexiOES(p.x, p.y, 0, mBrightSpot.w, mBrightSpot.h); + + // update animation + nsecs_t time = systemTime() - startTime; + t = ((4.0f / (360.0f * us2ns(16667))) * time); + t = t - floorf(t); + + eglSwapBuffers(mDisplay, mSurface); + + if (exitPending()) { + if (fadeTime == 0) { + fadeTime = time; + } + time -= fadeTime; + alpha = 1.0f - ((float(time) * 6.0f) / float(s2ns(1))); + + session()->openTransaction(); + mFlingerSurface->setAlpha(alpha * alpha); + session()->closeTransaction(); + } + } while (alpha > 0); + + // cleanup + glFinish(); + glDeleteTextures(1, &mLeftTrail.name); + glDeleteTextures(1, &mRightTrail.name); + glDeleteTextures(1, &mBrightSpot.name); + return false; +} + +// --------------------------------------------------------------------------- + +} +; // namespace android diff --git a/libs/surfaceflinger/BootAnimation.h b/libs/surfaceflinger/BootAnimation.h new file mode 100644 index 000000000..b20cea09a --- /dev/null +++ b/libs/surfaceflinger/BootAnimation.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_BOOTANIMATION_H +#define ANDROID_BOOTANIMATION_H + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include "Barrier.h" + +class SkBitmap; + +namespace android { + +class AssetManager; +class EGLNativeWindowSurface; + +// --------------------------------------------------------------------------- + +class BootAnimation : public Thread +{ +public: + BootAnimation(const sp& composer); + virtual ~BootAnimation(); + + const sp& session() const; + virtual void requestExit(); + +private: + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + + struct Texture { + GLint w; + GLint h; + GLuint name; + }; + + status_t initTexture(Texture* texture, AssetManager& asset, const char* name); + bool android(); + bool cylon(); + + sp mSession; + AssetManager mAssets; + Texture mLeftTrail; + Texture mRightTrail; + Texture mBrightSpot; + Texture mAndroid[3]; + int mWidth; + int mHeight; + EGLDisplay mDisplay; + EGLDisplay mContext; + EGLDisplay mSurface; + sp mFlingerSurface; + sp mNativeWindowSurface; + Barrier mBarrier; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_BOOTANIMATION_H diff --git a/libs/surfaceflinger/CPUGauge.cpp b/libs/surfaceflinger/CPUGauge.cpp new file mode 100644 index 000000000..74a9270b5 --- /dev/null +++ b/libs/surfaceflinger/CPUGauge.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "CPUGauge" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "CPUGauge.h" + +namespace android { + +CPUGauge::CPUGauge( const sp& composer, + nsecs_t interval, + int clock, + int refclock) + : Thread(false), + mInterval(interval), mClock(clock), mRefClock(refclock), + mReferenceTime(0), + mReferenceWorkingTime(0), mCpuUsage(0), + mRefIdleTime(0), mIdleTime(0) +{ + mFd = fopen("/proc/stat", "r"); + setvbuf(mFd, NULL, _IONBF, 0); + + mSession = SurfaceComposerClient::clientForConnection( + composer->createConnection()->asBinder()); +} + +CPUGauge::~CPUGauge() +{ + fclose(mFd); +} + +const sp& CPUGauge::session() const +{ + return mSession; +} + +void CPUGauge::onFirstRef() +{ + run("CPU Gauge"); +} + +status_t CPUGauge::readyToRun() +{ + LOGI("Starting CPU gauge..."); + return NO_ERROR; +} + +bool CPUGauge::threadLoop() +{ + DisplayInfo dinfo; + session()->getDisplayInfo(0, &dinfo); + sp s(session()->createSurface(getpid(), 0, dinfo.w, 4, PIXEL_FORMAT_OPAQUE)); + session()->openTransaction(); + s->setLayer(INT_MAX); + session()->closeTransaction(); + + static const GGLfixed colors[4][4] = { + { 0x00000, 0x10000, 0x00000, 0x10000 }, + { 0x10000, 0x10000, 0x00000, 0x10000 }, + { 0x10000, 0x00000, 0x00000, 0x10000 }, + { 0x00000, 0x00000, 0x00000, 0x10000 }, + }; + + GGLContext* gl; + gglInit(&gl); + gl->activeTexture(gl, 0); + gl->disable(gl, GGL_TEXTURE_2D); + gl->disable(gl, GGL_BLEND); + + const int w = dinfo.w; + + while(!exitPending()) + { + mLock.lock(); + const float cpuUsage = this->cpuUsage(); + const float totalCpuUsage = 1.0f - idle(); + mLock.unlock(); + + Surface::SurfaceInfo info; + s->lock(&info); + GGLSurface fb; + fb.version = sizeof(GGLSurface); + fb.width = info.w; + fb.height = info.h; + fb.stride = info.w; + fb.format = info.format; + fb.data = (GGLubyte*)info.bits; + + gl->colorBuffer(gl, &fb); + gl->color4xv(gl, colors[3]); + gl->recti(gl, 0, 0, w, 4); + gl->color4xv(gl, colors[2]); // red + gl->recti(gl, 0, 0, int(totalCpuUsage*w), 2); + gl->color4xv(gl, colors[0]); // green + gl->recti(gl, 0, 2, int(cpuUsage*w), 4); + + s->unlockAndPost(); + + usleep(ns2us(mInterval)); + } + + gglUninit(gl); + return false; +} + +void CPUGauge::sample() +{ + if (mLock.tryLock() == NO_ERROR) { + const nsecs_t now = systemTime(mRefClock); + const nsecs_t referenceTime = now-mReferenceTime; + if (referenceTime >= mInterval) { + const float reftime = 1.0f / referenceTime; + const nsecs_t nowWorkingTime = systemTime(mClock); + + char buf[256]; + fgets(buf, 256, mFd); + rewind(mFd); + char *str = buf+5; + char const * const usermode = strsep(&str, " "); (void)usermode; + char const * const usernice = strsep(&str, " "); (void)usernice; + char const * const systemmode = strsep(&str, " ");(void)systemmode; + char const * const idle = strsep(&str, " "); + const nsecs_t nowIdleTime = atoi(idle) * 10000000LL; + mIdleTime = float(nowIdleTime - mRefIdleTime) * reftime; + mRefIdleTime = nowIdleTime; + + const nsecs_t workingTime = nowWorkingTime - mReferenceWorkingTime; + const float newCpuUsage = float(workingTime) * reftime; + if (mCpuUsage != newCpuUsage) { + mCpuUsage = newCpuUsage; + mReferenceWorkingTime = nowWorkingTime; + mReferenceTime = now; + } + } + mLock.unlock(); + } +} + + +}; // namespace android diff --git a/libs/surfaceflinger/CPUGauge.h b/libs/surfaceflinger/CPUGauge.h new file mode 100644 index 000000000..5bb53c07f --- /dev/null +++ b/libs/surfaceflinger/CPUGauge.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_CPUGAUGE_H +#define ANDROID_CPUGAUGE_H + +#include +#include +#include + +#include +#include + +#include + +#include + +namespace android { + +class CPUGauge : public Thread +{ +public: + CPUGauge( const sp& composer, + nsecs_t interval=s2ns(1), + int clock=SYSTEM_TIME_THREAD, + int refclock=SYSTEM_TIME_MONOTONIC); + + ~CPUGauge(); + + const sp& session() const; + + void sample(); + + inline float cpuUsage() const { return mCpuUsage; } + inline float idle() const { return mIdleTime; } + +private: + virtual void onFirstRef(); + virtual status_t readyToRun(); + virtual bool threadLoop(); + + Mutex mLock; + + sp mSession; + + const nsecs_t mInterval; + const int mClock; + const int mRefClock; + + nsecs_t mReferenceTime; + nsecs_t mReferenceWorkingTime; + float mCpuUsage; + nsecs_t mRefIdleTime; + float mIdleTime; + FILE* mFd; +}; + + +}; // namespace android + +#endif // ANDROID_CPUGAUGE_H diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp new file mode 100644 index 000000000..f14d7e99c --- /dev/null +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + + +#include "DisplayHardware/DisplayHardware.h" + +#include +#include + +using namespace android; + +static __attribute__((noinline)) +const char *egl_strerror(EGLint err) +{ + switch (err){ + case EGL_SUCCESS: return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; + case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; + case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; + case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; + case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; + case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; + case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; + case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; + default: return "UNKNOWN"; + } +} + +static __attribute__((noinline)) +void checkGLErrors() +{ + GLenum error = glGetError(); + if (error != GL_NO_ERROR) + LOGE("GL error 0x%04x", int(error)); +} + +static __attribute__((noinline)) +void checkEGLErrors(const char* token) +{ + EGLint error = eglGetError(); + // GLESonGL seems to be returning 0 when there is no errors? + if (error && error != EGL_SUCCESS) + LOGE("%s error 0x%04x (%s)", + token, int(error), egl_strerror(error)); +} + + +/* + * Initialize the display to the specified values. + * + */ + +DisplayHardware::DisplayHardware( + const sp& flinger, + uint32_t dpy) + : DisplayHardwareBase(flinger, dpy) +{ + init(dpy); +} + +DisplayHardware::~DisplayHardware() +{ + fini(); +} + +float DisplayHardware::getDpiX() const { return mDpiX; } +float DisplayHardware::getDpiY() const { return mDpiY; } +float DisplayHardware::getDensity() const { return mDensity; } +float DisplayHardware::getRefreshRate() const { return mRefreshRate; } +int DisplayHardware::getWidth() const { return mWidth; } +int DisplayHardware::getHeight() const { return mHeight; } +PixelFormat DisplayHardware::getFormat() const { return mFormat; } + +void DisplayHardware::init(uint32_t dpy) +{ + // initialize EGL + const EGLint attribs[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_DEPTH_SIZE, 0, + EGL_NONE + }; + EGLint w, h, dummy; + EGLint numConfigs, n; + EGLConfig config; + EGLSurface surface; + EGLContext context; + mFlags = 0; + + // TODO: all the extensions below should be queried through + // eglGetProcAddress(). + + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(display, NULL, NULL); + eglGetConfigs(display, NULL, 0, &numConfigs); + eglChooseConfig(display, attribs, &config, 1, &n); + + /* + * Gather EGL extensions + */ + + const char* const egl_extensions = eglQueryString( + display, EGL_EXTENSIONS); + + LOGI("EGL informations:"); + LOGI("# of configs : %d", numConfigs); + LOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); + LOGI("version : %s", eglQueryString(display, EGL_VERSION)); + LOGI("extensions: %s", egl_extensions); + LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); + + // TODO: get this from the devfb driver (probably should be HAL module) + mFlags |= SWAP_RECTANGLE_EXTENSION; + + // TODO: get the real "update_on_demand" behavior (probably should be HAL module) + mFlags |= UPDATE_ON_DEMAND; + + if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) { + if (dummy == EGL_SLOW_CONFIG) + mFlags |= SLOW_CONFIG; + } + + /* + * Create our main surface + */ + + mDisplaySurface = new EGLDisplaySurface(); + + surface = eglCreateWindowSurface(display, config, mDisplaySurface.get(), NULL); + //checkEGLErrors("eglCreateDisplaySurfaceANDROID"); + + if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) { + if (dummy == EGL_BUFFER_PRESERVED) { + mFlags |= BUFFER_PRESERVED; + } + } + + GLint value = EGL_UNKNOWN; + eglQuerySurface(display, surface, EGL_HORIZONTAL_RESOLUTION, &value); + if (value == EGL_UNKNOWN) { + mDpiX = 160.0f; + } else { + mDpiX = 25.4f * float(value)/EGL_DISPLAY_SCALING; + } + value = EGL_UNKNOWN; + eglQuerySurface(display, surface, EGL_VERTICAL_RESOLUTION, &value); + if (value == EGL_UNKNOWN) { + mDpiY = 160.0f; + } else { + mDpiY = 25.4f * float(value)/EGL_DISPLAY_SCALING; + } + mRefreshRate = 60.f; // TODO: get the real refresh rate + + + char property[PROPERTY_VALUE_MAX]; + if (property_get("ro.sf.lcd_density", property, NULL) <= 0) { + LOGW("ro.sf.lcd_density not defined, using 160 dpi by default."); + strcpy(property, "160"); + } + mDensity = atoi(property) * (1.0f/160.0f); + + + /* + * Create our OpenGL ES context + */ + + context = eglCreateContext(display, config, NULL, NULL); + //checkEGLErrors("eglCreateContext"); + + eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); + eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); + + + /* + * Gather OpenGL ES extensions + */ + + eglMakeCurrent(display, surface, surface, context); + const char* const gl_extensions = (const char*)glGetString(GL_EXTENSIONS); + LOGI("OpenGL informations:"); + LOGI("vendor : %s", glGetString(GL_VENDOR)); + LOGI("renderer : %s", glGetString(GL_RENDERER)); + LOGI("version : %s", glGetString(GL_VERSION)); + LOGI("extensions: %s", gl_extensions); + + if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) { + mFlags |= NPOT_EXTENSION; + } + if (strstr(gl_extensions, "GL_OES_draw_texture")) { + mFlags |= DRAW_TEXTURE_EXTENSION; + } + if (strstr(gl_extensions, "GL_ANDROID_direct_texture")) { + mFlags |= DIRECT_TEXTURE; + } + + // Unbind the context from this thread + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + mDisplay = display; + mConfig = config; + mSurface = surface; + mContext = context; + mFormat = GGL_PIXEL_FORMAT_RGB_565; + + hw_module_t const* module; + + mBlitEngine = NULL; + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &mBlitEngine); + } + + mOverlayEngine = NULL; + if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { + overlay_control_open(module, &mOverlayEngine); + } +} + +/* + * Clean up. Throw out our local state. + * + * (It's entirely possible we'll never get here, since this is meant + * for real hardware, which doesn't restart.) + */ + +void DisplayHardware::fini() +{ + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mDisplay); + copybit_close(mBlitEngine); + overlay_control_close(mOverlayEngine); +} + +void DisplayHardware::releaseScreen() const +{ + DisplayHardwareBase::releaseScreen(); +} + +void DisplayHardware::acquireScreen() const +{ + DisplayHardwareBase::acquireScreen(); +} + +void DisplayHardware::getDisplaySurface(copybit_image_t* img) const +{ + img->w = mDisplaySurface->stride; + img->h = mDisplaySurface->height; + img->format = mDisplaySurface->format; + img->offset = mDisplaySurface->offset; + img->base = (void*)mDisplaySurface->base; + img->fd = mDisplaySurface->fd; +} + +void DisplayHardware::getDisplaySurface(GGLSurface* fb) const +{ + fb->version= sizeof(GGLSurface); + fb->width = mDisplaySurface->width; + fb->height = mDisplaySurface->height; + fb->stride = mDisplaySurface->stride; + fb->format = mDisplaySurface->format; + fb->data = (GGLubyte*)mDisplaySurface->base + mDisplaySurface->offset; +} + +uint32_t DisplayHardware::getPageFlipCount() const { + return mDisplaySurface->getPageFlipCount(); +} + +/* + * "Flip" the front and back buffers. + */ + +void DisplayHardware::flip(const Region& dirty) const +{ + checkGLErrors(); + + EGLDisplay dpy = mDisplay; + EGLSurface surface = mSurface; + + Region newDirty(dirty); + newDirty.andSelf(Rect(mWidth, mHeight)); + + if (mFlags & BUFFER_PRESERVED) { + const Region copyback(mDirty.subtract(newDirty)); + mDirty = newDirty; + mDisplaySurface->copyFrontToBack(copyback); + } + + if (mFlags & SWAP_RECTANGLE_EXTENSION) { + const Rect& b(newDirty.bounds()); + mDisplaySurface->setSwapRectangle( + b.left, b.top, b.width(), b.height()); + } + + eglSwapBuffers(dpy, surface); + checkEGLErrors("eglSwapBuffers"); + + // for debugging + //glClearColor(1,0,0,0); + //glClear(GL_COLOR_BUFFER_BIT); +} + +uint32_t DisplayHardware::getFlags() const +{ + return mFlags; +} + +void DisplayHardware::makeCurrent() const +{ + eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); +} + +void DisplayHardware::copyFrontToImage(const copybit_image_t& front) const { + mDisplaySurface->copyFrontToImage(front); +} + +void DisplayHardware::copyBackToImage(const copybit_image_t& front) const { + mDisplaySurface->copyBackToImage(front); +} diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h new file mode 100644 index 000000000..550a4d127 --- /dev/null +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_DISPLAY_HARDWARE_H +#define ANDROID_DISPLAY_HARDWARE_H + +#include + +#include +#include + +#include + +#include "DisplayHardware/DisplayHardwareBase.h" + +struct overlay_control_device_t; +struct copybit_device_t; +struct copybit_image_t; +struct copybit_t; + +namespace android { + +class EGLDisplaySurface; + +class DisplayHardware : public DisplayHardwareBase +{ +public: + enum { + DIRECT_TEXTURE = 0x00000002, + SWAP_RECTANGLE_EXTENSION= 0x00000004, + COPY_BITS_EXTENSION = 0x00000008, + NPOT_EXTENSION = 0x00000100, + DRAW_TEXTURE_EXTENSION = 0x00000200, + BUFFER_PRESERVED = 0x00010000, + UPDATE_ON_DEMAND = 0x00020000, // video driver feature + SLOW_CONFIG = 0x00040000, // software + }; + + DisplayHardware( + const sp& flinger, + uint32_t displayIndex); + + ~DisplayHardware(); + + void releaseScreen() const; + void acquireScreen() const; + + // Flip the front and back buffers if the back buffer is "dirty". Might + // be instantaneous, might involve copying the frame buffer around. + void flip(const Region& dirty) const; + + float getDpiX() const; + float getDpiY() const; + float getRefreshRate() const; + float getDensity() const; + int getWidth() const; + int getHeight() const; + PixelFormat getFormat() const; + uint32_t getFlags() const; + void makeCurrent() const; + + uint32_t getPageFlipCount() const; + void getDisplaySurface(copybit_image_t* img) const; + void getDisplaySurface(GGLSurface* fb) const; + EGLDisplay getEGLDisplay() const { return mDisplay; } + copybit_device_t* getBlitEngine() const { return mBlitEngine; } + overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; } + + void copyFrontToImage(const copybit_image_t& front) const; + void copyBackToImage(const copybit_image_t& front) const; + + Rect bounds() const { + return Rect(mWidth, mHeight); + } + +private: + void init(uint32_t displayIndex) __attribute__((noinline)); + void fini() __attribute__((noinline)); + + EGLDisplay mDisplay; + EGLSurface mSurface; + EGLContext mContext; + EGLConfig mConfig; + float mDpiX; + float mDpiY; + float mRefreshRate; + float mDensity; + int mWidth; + int mHeight; + PixelFormat mFormat; + uint32_t mFlags; + mutable Region mDirty; + sp mDisplaySurface; + copybit_device_t* mBlitEngine; + overlay_control_device_t* mOverlayEngine; +}; + +}; // namespace android + +#endif // ANDROID_DISPLAY_HARDWARE_H diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp new file mode 100644 index 000000000..f75e5c229 --- /dev/null +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "DisplayHardware/DisplayHardwareBase.h" +#include "SurfaceFlinger.h" + +// ---------------------------------------------------------------------------- +// the sim build doesn't have gettid + +#ifndef HAVE_GETTID +# define gettid getpid +#endif + +// ---------------------------------------------------------------------------- +namespace android { + +static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep"; +static char const * kWakeFileName = "/sys/power/wait_for_fb_wake"; +static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep"; +static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake"; + +// This dir exists if the framebuffer console is present, either built into +// the kernel or loaded as a module. +static char const * const kFbconSysDir = "/sys/class/graphics/fbcon"; + +// ---------------------------------------------------------------------------- + +DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase( + const sp& flinger) + : Thread(false), mFlinger(flinger) { +} + +DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() { +} + +// ---------------------------------------------------------------------------- + +DisplayHardwareBase::DisplayEventThread::DisplayEventThread( + const sp& flinger) + : DisplayEventThreadBase(flinger) +{ +} + +DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() +{ +} + +bool DisplayHardwareBase::DisplayEventThread::threadLoop() +{ + int err = 0; + char buf; + int fd; + + fd = open(kSleepFileName, O_RDONLY, 0); + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + close(fd); + LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); + if (err >= 0) { + sp flinger = mFlinger.promote(); + LOGD("About to give-up screen, flinger = %p", flinger.get()); + if (flinger != 0) { + mBarrier.close(); + flinger->screenReleased(0); + mBarrier.wait(); + } + } + fd = open(kWakeFileName, O_RDONLY, 0); + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + close(fd); + LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); + if (err >= 0) { + sp flinger = mFlinger.promote(); + LOGD("Screen about to return, flinger = %p", flinger.get()); + if (flinger != 0) + flinger->screenAcquired(0); + } + return true; +} + +status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const +{ + mBarrier.open(); + return NO_ERROR; +} + +status_t DisplayHardwareBase::DisplayEventThread::readyToRun() +{ + if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) { + if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) { + LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName); + return NO_INIT; + } + kSleepFileName = kOldSleepFileName; + kWakeFileName = kOldWakeFileName; + } + return NO_ERROR; +} + +status_t DisplayHardwareBase::DisplayEventThread::initCheck() const +{ + return (((access(kSleepFileName, R_OK) == 0 && + access(kWakeFileName, R_OK) == 0) || + (access(kOldSleepFileName, R_OK) == 0 && + access(kOldWakeFileName, R_OK) == 0)) && + access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT; +} + +// ---------------------------------------------------------------------------- + +pid_t DisplayHardwareBase::ConsoleManagerThread::sSignalCatcherPid = 0; + +DisplayHardwareBase::ConsoleManagerThread::ConsoleManagerThread( + const sp& flinger) + : DisplayEventThreadBase(flinger), consoleFd(-1) +{ + sSignalCatcherPid = 0; + + // create a new console + char const * const ttydev = "/dev/tty0"; + int fd = open(ttydev, O_RDWR | O_SYNC); + if (fd<0) { + LOGE("Can't open %s", ttydev); + this->consoleFd = -errno; + return; + } + + // to make sure that we are in text mode + int res = ioctl(fd, KDSETMODE, (void*) KD_TEXT); + if (res<0) { + LOGE("ioctl(%d, KDSETMODE, ...) failed, res %d (%s)", + fd, res, strerror(errno)); + } + + // get the current console + struct vt_stat vs; + res = ioctl(fd, VT_GETSTATE, &vs); + if (res<0) { + LOGE("ioctl(%d, VT_GETSTATE, ...) failed, res %d (%s)", + fd, res, strerror(errno)); + this->consoleFd = -errno; + return; + } + + // switch to console 7 (which is what X normaly uses) + int vtnum = 7; + do { + res = ioctl(fd, VT_ACTIVATE, (void*)vtnum); + } while(res < 0 && errno == EINTR); + if (res<0) { + LOGE("ioctl(%d, VT_ACTIVATE, ...) failed, %d (%s) for %d", + fd, errno, strerror(errno), vtnum); + this->consoleFd = -errno; + return; + } + + do { + res = ioctl(fd, VT_WAITACTIVE, (void*)vtnum); + } while(res < 0 && errno == EINTR); + if (res<0) { + LOGE("ioctl(%d, VT_WAITACTIVE, ...) failed, %d %d %s for %d", + fd, res, errno, strerror(errno), vtnum); + this->consoleFd = -errno; + return; + } + + // open the new console + close(fd); + fd = open(ttydev, O_RDWR | O_SYNC); + if (fd<0) { + LOGE("Can't open new console %s", ttydev); + this->consoleFd = -errno; + return; + } + + /* disable console line buffer, echo, ... */ + struct termios ttyarg; + ioctl(fd, TCGETS , &ttyarg); + ttyarg.c_iflag = 0; + ttyarg.c_lflag = 0; + ioctl(fd, TCSETS , &ttyarg); + + // set up signals so we're notified when the console changes + // we can't use SIGUSR1 because it's used by the java-vm + vm.mode = VT_PROCESS; + vm.waitv = 0; + vm.relsig = SIGUSR2; + vm.acqsig = SIGUNUSED; + vm.frsig = 0; + + struct sigaction act; + sigemptyset(&act.sa_mask); + act.sa_handler = sigHandler; + act.sa_flags = 0; + sigaction(vm.relsig, &act, NULL); + + sigemptyset(&act.sa_mask); + act.sa_handler = sigHandler; + act.sa_flags = 0; + sigaction(vm.acqsig, &act, NULL); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, vm.relsig); + sigaddset(&mask, vm.acqsig); + sigprocmask(SIG_BLOCK, &mask, NULL); + + // switch to graphic mode + res = ioctl(fd, KDSETMODE, (void*)KD_GRAPHICS); + LOGW_IF(res<0, + "ioctl(%d, KDSETMODE, KD_GRAPHICS) failed, res %d", fd, res); + + this->prev_vt_num = vs.v_active; + this->vt_num = vtnum; + this->consoleFd = fd; +} + +DisplayHardwareBase::ConsoleManagerThread::~ConsoleManagerThread() +{ + if (this->consoleFd >= 0) { + int fd = this->consoleFd; + int prev_vt_num = this->prev_vt_num; + int res; + ioctl(fd, KDSETMODE, (void*)KD_TEXT); + do { + res = ioctl(fd, VT_ACTIVATE, (void*)prev_vt_num); + } while(res < 0 && errno == EINTR); + do { + res = ioctl(fd, VT_WAITACTIVE, (void*)prev_vt_num); + } while(res < 0 && errno == EINTR); + close(fd); + char const * const ttydev = "/dev/tty0"; + fd = open(ttydev, O_RDWR | O_SYNC); + ioctl(fd, VT_DISALLOCATE, 0); + close(fd); + } +} + +status_t DisplayHardwareBase::ConsoleManagerThread::readyToRun() +{ + if (this->consoleFd >= 0) { + sSignalCatcherPid = gettid(); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, vm.relsig); + sigaddset(&mask, vm.acqsig); + sigprocmask(SIG_BLOCK, &mask, NULL); + + int res = ioctl(this->consoleFd, VT_SETMODE, &vm); + if (res<0) { + LOGE("ioctl(%d, VT_SETMODE, ...) failed, %d (%s)", + this->consoleFd, errno, strerror(errno)); + } + return NO_ERROR; + } + return this->consoleFd; +} + +void DisplayHardwareBase::ConsoleManagerThread::requestExit() +{ + Thread::requestExit(); + if (sSignalCatcherPid != 0) { + // wake the thread up + kill(sSignalCatcherPid, SIGINT); + // wait for it... + } +} + +void DisplayHardwareBase::ConsoleManagerThread::sigHandler(int sig) +{ + // resend the signal to our signal catcher thread + LOGW("received signal %d in thread %d, resending to %d", + sig, gettid(), sSignalCatcherPid); + + // we absolutely need the delays below because without them + // our main thread never gets a chance to handle the signal. + usleep(10000); + kill(sSignalCatcherPid, sig); + usleep(10000); +} + +status_t DisplayHardwareBase::ConsoleManagerThread::releaseScreen() const +{ + int fd = this->consoleFd; + int err = ioctl(fd, VT_RELDISP, (void*)1); + LOGE_IF(err<0, "ioctl(%d, VT_RELDISP, 1) failed %d (%s)", + fd, errno, strerror(errno)); + return (err<0) ? (-errno) : status_t(NO_ERROR); +} + +bool DisplayHardwareBase::ConsoleManagerThread::threadLoop() +{ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, vm.relsig); + sigaddset(&mask, vm.acqsig); + + int sig = 0; + sigwait(&mask, &sig); + + if (sig == vm.relsig) { + sp flinger = mFlinger.promote(); + //LOGD("About to give-up screen, flinger = %p", flinger.get()); + if (flinger != 0) + flinger->screenReleased(0); + } else if (sig == vm.acqsig) { + sp flinger = mFlinger.promote(); + //LOGD("Screen about to return, flinger = %p", flinger.get()); + if (flinger != 0) + flinger->screenAcquired(0); + } + + return true; +} + +status_t DisplayHardwareBase::ConsoleManagerThread::initCheck() const +{ + return consoleFd >= 0 ? NO_ERROR : NO_INIT; +} + +// ---------------------------------------------------------------------------- + +DisplayHardwareBase::DisplayHardwareBase(const sp& flinger, + uint32_t displayIndex) + : mCanDraw(true) +{ + mDisplayEventThread = new DisplayEventThread(flinger); + if (mDisplayEventThread->initCheck() != NO_ERROR) { + // fall-back on the console + mDisplayEventThread = new ConsoleManagerThread(flinger); + } +} + +DisplayHardwareBase::~DisplayHardwareBase() +{ + // request exit + mDisplayEventThread->requestExitAndWait(); +} + + +bool DisplayHardwareBase::canDraw() const +{ + return mCanDraw; +} + +void DisplayHardwareBase::releaseScreen() const +{ + status_t err = mDisplayEventThread->releaseScreen(); + if (err >= 0) { + //LOGD("screen given-up"); + mCanDraw = false; + } +} + +void DisplayHardwareBase::acquireScreen() const +{ + status_t err = mDisplayEventThread->acquireScreen(); + if (err >= 0) { + //LOGD("screen returned"); + mCanDraw = true; + } +} + +}; // namespace android diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h new file mode 100644 index 000000000..8369bb886 --- /dev/null +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_DISPLAY_HARDWARE_BASE_H +#define ANDROID_DISPLAY_HARDWARE_BASE_H + +#include +#include +#include +#include +#include +#include "Barrier.h" + +namespace android { + +class SurfaceFlinger; + +class DisplayHardwareBase +{ +public: + DisplayHardwareBase( + const sp& flinger, + uint32_t displayIndex); + + ~DisplayHardwareBase(); + + // console managment + void releaseScreen() const; + void acquireScreen() const; + bool canDraw() const; + +private: + class DisplayEventThreadBase : public Thread { + protected: + wp mFlinger; + public: + DisplayEventThreadBase(const sp& flinger); + virtual ~DisplayEventThreadBase(); + virtual void onFirstRef() { + run("DisplayEventThread", PRIORITY_URGENT_DISPLAY); + } + virtual status_t acquireScreen() const { return NO_ERROR; }; + virtual status_t releaseScreen() const { return NO_ERROR; }; + virtual status_t initCheck() const = 0; + }; + + class DisplayEventThread : public DisplayEventThreadBase + { + mutable Barrier mBarrier; + public: + DisplayEventThread(const sp& flinger); + virtual ~DisplayEventThread(); + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual status_t releaseScreen() const; + virtual status_t initCheck() const; + }; + + class ConsoleManagerThread : public DisplayEventThreadBase + { + int consoleFd; + int vt_num; + int prev_vt_num; + vt_mode vm; + static void sigHandler(int sig); + static pid_t sSignalCatcherPid; + public: + ConsoleManagerThread(const sp& flinger); + virtual ~ConsoleManagerThread(); + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void requestExit(); + virtual status_t releaseScreen() const; + virtual status_t initCheck() const; + }; + + sp mDisplayEventThread; + mutable int mCanDraw; +}; + +}; // namespace android + +#endif // ANDROID_DISPLAY_HARDWARE_BASE_H diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp b/libs/surfaceflinger/GPUHardware/GPUHardware.cpp new file mode 100644 index 000000000..eb75f9998 --- /dev/null +++ b/libs/surfaceflinger/GPUHardware/GPUHardware.cpp @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2008 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "VRamHeap.h" +#include "GPUHardware.h" + +#if HAVE_ANDROID_OS +#include +#endif + +#include "GPUHardware/GPUHardware.h" + + +/* + * Manage the GPU. This implementation is very specific to the G1. + * There are no abstraction here. + * + * All this code will soon go-away and be replaced by a new architecture + * for managing graphics accelerators. + * + * In the meantime, it is conceptually possible to instantiate a + * GPUHardwareInterface for another GPU (see GPUFactory at the bottom + * of this file); practically... doubtful. + * + */ + +namespace android { + +// --------------------------------------------------------------------------- + +class GPUClientHeap; +class GPUAreaHeap; + +class GPUHardware : public GPUHardwareInterface, public IBinder::DeathRecipient +{ +public: + static const int GPU_RESERVED_SIZE; + static const int GPUR_SIZE; + + GPUHardware(); + virtual ~GPUHardware(); + + virtual void revoke(int pid); + virtual sp request(int pid); + virtual status_t request(int pid, + const sp& callback, + ISurfaceComposer::gpu_info_t* gpu); + + virtual status_t friendlyRevoke(); + virtual void unconditionalRevoke(); + + virtual pid_t getOwner() const { return mOwner; } + + // used for debugging only... + virtual sp getAllocator() const; + +private: + + + enum { + NO_OWNER = -1, + }; + + struct GPUArea { + sp heap; + sp clientHeap; + sp map(); + }; + + struct Client { + pid_t pid; + GPUArea smi; + GPUArea ebi; + GPUArea reg; + void createClientHeaps(); + void revokeAllHeaps(); + }; + + Client& getClientLocked(pid_t pid); + status_t requestLocked(int pid); + void releaseLocked(); + void takeBackGPULocked(); + void registerCallbackLocked(const sp& callback, + Client& client); + + virtual void binderDied(const wp& who); + + mutable Mutex mLock; + sp mSMIHeap; + sp mEBIHeap; + sp mREGHeap; + + KeyedVector mClients; + DefaultKeyedVector< wp, pid_t > mRegisteredClients; + + pid_t mOwner; + + sp mCurrentAllocator; + sp mCallback; + + sp mAllocator; + + Condition mCondition; +}; + +// size reserved for GPU surfaces +// 1200 KB fits exactly: +// - two 320*480 16-bits double-buffered surfaces +// - one 320*480 32-bits double-buffered surface +// - one 320*240 16-bits double-buffered, 4x anti-aliased surface +const int GPUHardware::GPU_RESERVED_SIZE = 1200 * 1024; +const int GPUHardware::GPUR_SIZE = 1 * 1024 * 1024; + +// --------------------------------------------------------------------------- + +/* + * GPUHandle is a special IMemory given to the client. It represents their + * handle to the GPU. Once they give it up, they loose GPU access, or if + * they explicitly revoke their access through the binder code 1000. + * In both cases, this triggers a callback to revoke() + * first, and then actually powers down the chip. + * + * In the case of a misbehaving app, GPUHardware can ask for an immediate + * release of the GPU to the target process which should answer by calling + * code 1000 on GPUHandle. If it doesn't in a timely manner, the GPU will + * be revoked from under their feet. + * + * We should never hold a strong reference on GPUHandle. In practice this + * shouldn't be a big issue though because clients should use code 1000 and + * not rely on the dtor being called. + * + */ + +class GPUClientHeap : public MemoryHeapPmem +{ +public: + GPUClientHeap(const wp& gpu, + const sp& heap) + : MemoryHeapPmem(heap), mGPU(gpu) { } +protected: + wp mGPU; +}; + +class GPUAreaHeap : public MemoryHeapBase +{ +public: + GPUAreaHeap(const wp& gpu, + const char* const vram, size_t size=0, size_t reserved=0) + : MemoryHeapBase(vram, size), mGPU(gpu) { + if (base() != MAP_FAILED) { + if (reserved == 0) + reserved = virtualSize(); + mAllocator = new SimpleBestFitAllocator(reserved); + } + } + virtual sp createClientHeap() { + sp parentHeap(this); + return new GPUClientHeap(mGPU, parentHeap); + } + virtual const sp& getAllocator() const { + return mAllocator; + } +private: + sp mAllocator; +protected: + wp mGPU; +}; + +class GPURegisterHeap : public GPUAreaHeap +{ +public: + GPURegisterHeap(const sp& gpu) + : GPUAreaHeap(gpu, "/dev/hw3d", GPUHardware::GPUR_SIZE) { } + virtual sp createClientHeap() { + sp parentHeap(this); + return new MemoryHeapRegs(mGPU, parentHeap); + } +private: + class MemoryHeapRegs : public GPUClientHeap { + public: + MemoryHeapRegs(const wp& gpu, + const sp& heap) + : GPUClientHeap(gpu, heap) { } + sp createMemory(size_t offset, size_t size); + virtual void revoke(); + private: + class GPUHandle : public MemoryHeapPmem::MemoryPmem { + public: + GPUHandle(const sp& gpu, + const sp& heap) + : MemoryHeapPmem::MemoryPmem(heap), + mGPU(gpu), mOwner(gpu->getOwner()) { } + virtual ~GPUHandle(); + virtual sp getMemory( + ssize_t* offset, size_t* size) const; + virtual void revoke() { }; + virtual status_t onTransact( + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + private: + void revokeNotification(); + wp mGPU; + pid_t mOwner; + }; + }; +}; + +GPURegisterHeap::MemoryHeapRegs::GPUHandle::~GPUHandle() { + //LOGD("GPUHandle %p released, revoking GPU", this); + revokeNotification(); +} +void GPURegisterHeap::MemoryHeapRegs::GPUHandle::revokeNotification() { + sp hw(mGPU.promote()); + if (hw != 0) { + hw->revoke(mOwner); + } +} +sp GPURegisterHeap::MemoryHeapRegs::GPUHandle::getMemory( + ssize_t* offset, size_t* size) const +{ + sp heap = getHeap(); + if (offset) *offset = 0; + if (size) *size = heap !=0 ? heap->virtualSize() : 0; + return heap; +} +status_t GPURegisterHeap::MemoryHeapRegs::GPUHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + status_t err = BnMemory::onTransact(code, data, reply, flags); + if (err == UNKNOWN_TRANSACTION && code == 1000) { + int callingPid = IPCThreadState::self()->getCallingPid(); + //LOGD("pid %d voluntarily revoking gpu", callingPid); + if (callingPid == mOwner) { + revokeNotification(); + // we've revoked the GPU, don't do it again later when we + // are destroyed. + mGPU.clear(); + } else { + LOGW("%d revoking someone else's gpu? (owner=%d)", + callingPid, mOwner); + } + err = NO_ERROR; + } + return err; +} + +// --------------------------------------------------------------------------- + + +sp GPURegisterHeap::MemoryHeapRegs::createMemory( + size_t offset, size_t size) +{ + sp memory; + sp gpu = mGPU.promote(); + if (heapID()>0 && gpu!=0) { +#if HAVE_ANDROID_OS + /* this is where the GPU is powered on and the registers are mapped + * in the client */ + //LOGD("ioctl(HW3D_GRANT_GPU)"); + int err = ioctl(heapID(), HW3D_GRANT_GPU, base()); + if (err) { + // it can happen if the master heap has been closed already + // in which case the GPU already is revoked (app crash for + // instance). + LOGW("HW3D_GRANT_GPU failed (%s), mFD=%d, base=%p", + strerror(errno), heapID(), base()); + } + memory = new GPUHandle(gpu, this); +#endif + } + return memory; +} + +void GPURegisterHeap::MemoryHeapRegs::revoke() +{ + MemoryHeapPmem::revoke(); +#if HAVE_ANDROID_OS + if (heapID() > 0) { + //LOGD("ioctl(HW3D_REVOKE_GPU)"); + int err = ioctl(heapID(), HW3D_REVOKE_GPU, base()); + LOGE_IF(err, "HW3D_REVOKE_GPU failed (%s), mFD=%d, base=%p", + strerror(errno), heapID(), base()); + } +#endif +} + +/*****************************************************************************/ + +GPUHardware::GPUHardware() + : mOwner(NO_OWNER) +{ +} + +GPUHardware::~GPUHardware() +{ +} + +status_t GPUHardware::requestLocked(int pid) +{ + const int self_pid = getpid(); + if (pid == self_pid) { + // can't use GPU from surfaceflinger's process + return PERMISSION_DENIED; + } + + if (mOwner != pid) { + if (mREGHeap != 0) { + if (mOwner != NO_OWNER) { + // someone already has the gpu. + takeBackGPULocked(); + releaseLocked(); + } + } else { + // first time, initialize the stuff. + if (mSMIHeap == 0) + mSMIHeap = new GPUAreaHeap(this, "/dev/pmem_gpu0"); + if (mEBIHeap == 0) + mEBIHeap = new GPUAreaHeap(this, + "/dev/pmem_gpu1", 0, GPU_RESERVED_SIZE); + mREGHeap = new GPURegisterHeap(this); + mAllocator = mEBIHeap->getAllocator(); + if (mAllocator == NULL) { + // something went terribly wrong. + mSMIHeap.clear(); + mEBIHeap.clear(); + mREGHeap.clear(); + return INVALID_OPERATION; + } + } + Client& client = getClientLocked(pid); + mCurrentAllocator = new MemoryDealer(client.ebi.clientHeap, mAllocator); + mOwner = pid; + } + return NO_ERROR; +} + +sp GPUHardware::request(int pid) +{ + sp dealer; + Mutex::Autolock _l(mLock); + Client* client; + LOGD("pid %d requesting gpu surface (current owner = %d)", pid, mOwner); + if (requestLocked(pid) == NO_ERROR) { + dealer = mCurrentAllocator; + LOGD_IF(dealer!=0, "gpu surface granted to pid %d", mOwner); + } + return dealer; +} + +status_t GPUHardware::request(int pid, const sp& callback, + ISurfaceComposer::gpu_info_t* gpu) +{ + if (callback == 0) + return BAD_VALUE; + + sp gpuHandle; + LOGD("pid %d requesting gpu core (owner = %d)", pid, mOwner); + Mutex::Autolock _l(mLock); + status_t err = requestLocked(pid); + if (err == NO_ERROR) { + // it's guaranteed to be there, be construction + Client& client = mClients.editValueFor(pid); + registerCallbackLocked(callback, client); + gpu->count = 2; + gpu->regions[0].region = client.smi.map(); + gpu->regions[1].region = client.ebi.map(); + gpu->regs = client.reg.map(); + gpu->regions[0].reserved = 0; + gpu->regions[1].reserved = GPU_RESERVED_SIZE; + if (gpu->regs != 0) { + //LOGD("gpu core granted to pid %d, handle base=%p", + // mOwner, gpu->regs->pointer()); + } + mCallback = callback; + } else { + LOGW("couldn't grant gpu core to pid %d", pid); + } + return err; +} + +void GPUHardware::revoke(int pid) +{ + Mutex::Autolock _l(mLock); + if (mOwner > 0) { + if (pid != mOwner) { + LOGW("GPU owned by %d, revoke from %d", mOwner, pid); + return; + } + //LOGD("revoke pid=%d, owner=%d", pid, mOwner); + // mOwner could be <0 if the same process acquired the GPU + // several times without releasing it first. + mCondition.signal(); + releaseLocked(); + } +} + +status_t GPUHardware::friendlyRevoke() +{ + Mutex::Autolock _l(mLock); + //LOGD("friendlyRevoke owner=%d", mOwner); + takeBackGPULocked(); + releaseLocked(); + return NO_ERROR; +} + +void GPUHardware::takeBackGPULocked() +{ + sp callback = mCallback; + mCallback.clear(); + if (callback != 0) { + callback->gpuLost(); // one-way + mCondition.waitRelative(mLock, ms2ns(250)); + } +} + +void GPUHardware::releaseLocked() +{ + //LOGD("revoking gpu from pid %d", mOwner); + if (mOwner != NO_OWNER) { + // this may fail because the client might have died, and have + // been removed from the list. + ssize_t index = mClients.indexOfKey(mOwner); + if (index >= 0) { + Client& client(mClients.editValueAt(index)); + client.revokeAllHeaps(); + } + mOwner = NO_OWNER; + mCurrentAllocator.clear(); + mCallback.clear(); + } +} + +GPUHardware::Client& GPUHardware::getClientLocked(pid_t pid) +{ + ssize_t index = mClients.indexOfKey(pid); + if (index < 0) { + Client client; + client.pid = pid; + client.smi.heap = mSMIHeap; + client.ebi.heap = mEBIHeap; + client.reg.heap = mREGHeap; + index = mClients.add(pid, client); + } + Client& client(mClients.editValueAt(index)); + client.createClientHeaps(); + return client; +} + +// ---------------------------------------------------------------------------- +// for debugging / testing ... + +sp GPUHardware::getAllocator() const { + Mutex::Autolock _l(mLock); + return mAllocator; +} + +void GPUHardware::unconditionalRevoke() +{ + Mutex::Autolock _l(mLock); + releaseLocked(); +} + +// --------------------------------------------------------------------------- + +sp GPUHardware::GPUArea::map() { + sp memory; + if (clientHeap != 0 && heap != 0) { + memory = clientHeap->mapMemory(0, heap->virtualSize()); + } + return memory; +} + +void GPUHardware::Client::createClientHeaps() +{ + if (smi.clientHeap == 0) + smi.clientHeap = smi.heap->createClientHeap(); + if (ebi.clientHeap == 0) + ebi.clientHeap = ebi.heap->createClientHeap(); + if (reg.clientHeap == 0) + reg.clientHeap = reg.heap->createClientHeap(); +} + +void GPUHardware::Client::revokeAllHeaps() +{ + if (smi.clientHeap != 0) + smi.clientHeap->revoke(); + if (ebi.clientHeap != 0) + ebi.clientHeap->revoke(); + if (reg.clientHeap != 0) + reg.clientHeap->revoke(); +} + +void GPUHardware::registerCallbackLocked(const sp& callback, + Client& client) +{ + sp binder = callback->asBinder(); + if (mRegisteredClients.add(binder, client.pid) >= 0) { + binder->linkToDeath(this); + } +} + +void GPUHardware::binderDied(const wp& who) +{ + Mutex::Autolock _l(mLock); + pid_t pid = mRegisteredClients.valueFor(who); + if (pid != 0) { + ssize_t index = mClients.indexOfKey(pid); + if (index >= 0) { + //LOGD("*** removing client at %d", index); + Client& client(mClients.editValueAt(index)); + client.revokeAllHeaps(); // not really needed in theory + mClients.removeItemsAt(index); + if (mClients.size() == 0) { + //LOGD("*** was last client closing everything"); + mCallback.clear(); + mAllocator.clear(); + mCurrentAllocator.clear(); + mSMIHeap.clear(); + mREGHeap.clear(); + + // NOTE: we cannot clear the EBI heap because surfaceflinger + // itself may be using it, since this is where surfaces + // are allocated. if we're in the middle of compositing + // a surface (even if its process just died), we cannot + // rip the heap under our feet. + + mOwner = NO_OWNER; + } + } + } +} + +// --------------------------------------------------------------------------- + +sp GPUFactory::getGPU() +{ + return new GPUHardware(); +} + +// --------------------------------------------------------------------------- +}; // namespace android + diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.h b/libs/surfaceflinger/GPUHardware/GPUHardware.h new file mode 100644 index 000000000..335452808 --- /dev/null +++ b/libs/surfaceflinger/GPUHardware/GPUHardware.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_GPU_HARDWARE_H +#define ANDROID_GPU_HARDWARE_H + +#include +#include + +#include +#include +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +class IGPUCallback; + +class GPUHardwareInterface : public virtual RefBase +{ +public: + virtual void revoke(int pid) = 0; + virtual sp request(int pid) = 0; + virtual status_t request(int pid, const sp& callback, + ISurfaceComposer::gpu_info_t* gpu) = 0; + + virtual status_t friendlyRevoke() = 0; + + // used for debugging only... + virtual sp getAllocator() const = 0; + virtual pid_t getOwner() const = 0; + virtual void unconditionalRevoke() = 0; +}; + +// --------------------------------------------------------------------------- + +class GPUFactory +{ +public: + // the gpu factory + static sp getGPU(); +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GPU_HARDWARE_H diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp new file mode 100644 index 000000000..f65d66984 --- /dev/null +++ b/libs/surfaceflinger/Layer.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "clz.h" +#include "Layer.h" +#include "LayerBitmap.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" +#include "DisplayHardware/DisplayHardware.h" + + +#define DEBUG_RESIZE 0 + + +namespace android { + +// --------------------------------------------------------------------------- + +const uint32_t Layer::typeInfo = LayerBaseClient::typeInfo | 4; +const char* const Layer::typeID = "Layer"; + +// --------------------------------------------------------------------------- + +Layer::Layer(SurfaceFlinger* flinger, DisplayID display, Client* c, int32_t i) + : LayerBaseClient(flinger, display, c, i), + mSecure(false), + mFrontBufferIndex(1), + mNeedsBlending(true), + mResizeTransactionDone(false), + mTextureName(-1U), mTextureWidth(0), mTextureHeight(0) +{ + // no OpenGL operation is possible here, since we might not be + // in the OpenGL thread. +} + +Layer::~Layer() +{ + client->free(clientIndex()); + // this should always be called from the OpenGL thread + if (mTextureName != -1U) { + //glDeleteTextures(1, &mTextureName); + deletedTextures.add(mTextureName); + } +} + +void Layer::initStates(uint32_t w, uint32_t h, uint32_t flags) +{ + LayerBase::initStates(w,h,flags); + + if (flags & ISurfaceComposer::eDestroyBackbuffer) + lcblk->flags |= eNoCopyBack; +} + +sp Layer::getSurface() const +{ + return mSurface; +} + +status_t Layer::setBuffers( Client* client, + uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags) +{ + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + if (err) return err; + + // TODO: if eHardware is explicitly requested, we should fail + // on systems where we can't allocate memory that can be used with + // DMA engines for instance. + + // FIXME: we always ask for hardware for now (this should come from copybit) + flags |= ISurfaceComposer::eHardware; + + const uint32_t memory_flags = flags & + (ISurfaceComposer::eGPU | + ISurfaceComposer::eHardware | + ISurfaceComposer::eSecure); + + // pixel-alignment. the final alignment may be bigger because + // we always force a 4-byte aligned bpr. + uint32_t alignment = 1; + + if (flags & ISurfaceComposer::eGPU) { + // FIXME: this value should come from the h/w + alignment = 8; + // FIXME: this is msm7201A specific, as its GPU only supports + // BGRA_8888. + if (format == PIXEL_FORMAT_RGBA_8888) { + format = PIXEL_FORMAT_BGRA_8888; + } + } + + mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; + mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; + sp allocators[2]; + for (int i=0 ; i<2 ; i++) { + allocators[i] = client->createAllocator(memory_flags); + if (allocators[i] == 0) + return NO_MEMORY; + mBuffers[i].init(allocators[i]); + int err = mBuffers[i].setBits(w, h, alignment, format, LayerBitmap::SECURE_BITS); + if (err != NO_ERROR) + return err; + mBuffers[i].clear(); // clear the bits for security + mBuffers[i].getInfo(lcblk->surface + i); + } + + mSurface = new Surface(clientIndex(), + allocators[0]->getMemoryHeap(), + allocators[1]->getMemoryHeap(), + mIdentity); + + return NO_ERROR; +} + +void Layer::reloadTexture(const Region& dirty) +{ + if (UNLIKELY(mTextureName == -1U)) { + // create the texture name the first time + // can't do that in the ctor, because it runs in another thread. + mTextureName = createTexture(); + } + const GGLSurface& t(frontBuffer().surface()); + loadTexture(dirty, mTextureName, t, mTextureWidth, mTextureHeight); +} + + +void Layer::onDraw(const Region& clip) const +{ + if (UNLIKELY(mTextureName == -1LU)) { + //LOGW("Layer %p doesn't have a texture", this); + // the texture has not been created yet, this Layer has + // in fact never been drawn into. this happens frequently with + // SurfaceView. + clearWithOpenGL(clip); + return; + } + + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const LayerBitmap& front(frontBuffer()); + const GGLSurface& t(front.surface()); + + status_t err = NO_ERROR; + const int can_use_copybit = canUseCopybit(); + if (can_use_copybit) { + // StopWatch watch("copybit"); + const State& s(drawingState()); + + copybit_image_t dst; + hw.getDisplaySurface(&dst); + const copybit_rect_t& drect + = reinterpret_cast(mTransformedBounds); + + copybit_image_t src; + front.getBitmapSurface(&src); + copybit_rect_t srect = { 0, 0, t.width, t.height }; + + copybit_device_t* copybit = mFlinger->getBlitEngine(); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, getOrientation()); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); + copybit->set_parameter(copybit, COPYBIT_DITHER, + s.flags & ISurfaceComposer::eLayerDither ? + COPYBIT_ENABLE : COPYBIT_DISABLE); + + region_iterator it(clip); + err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); + } + + if (!can_use_copybit || err) { + drawWithOpenGL(clip, mTextureName, t); + } +} + +status_t Layer::reallocateBuffer(int32_t index, uint32_t w, uint32_t h) +{ + LOGD_IF(DEBUG_RESIZE, + "reallocateBuffer (layer=%p), " + "requested (%dx%d), " + "index=%d, (%dx%d), (%dx%d)", + this, + int(w), int(h), + int(index), + int(mBuffers[0].width()), int(mBuffers[0].height()), + int(mBuffers[1].width()), int(mBuffers[1].height())); + + status_t err = mBuffers[index].resize(w, h); + if (err == NO_ERROR) { + mBuffers[index].getInfo(lcblk->surface + index); + } else { + LOGE("resizing buffer %d to (%u,%u) failed [%08x] %s", + index, w, h, err, strerror(err)); + // XXX: what to do, what to do? We could try to free some + // hidden surfaces, instead of killing this one? + } + return err; +} + +uint32_t Layer::doTransaction(uint32_t flags) +{ + const Layer::State& front(drawingState()); + const Layer::State& temp(currentState()); + + // the test front.{w|h} != temp.{w|h} is not enough because it is possible + // that the size changed back to its previous value before the buffer + // was resized (in the eLocked case below), in which case, we still + // need to execute the code below so the clients have a chance to be + // release. resze() deals with the fact that the size can be the same. + + /* + * Various states we could be in... + + resize = state & eResizeRequested; + if (backbufferChanged) { + if (resize == 0) { + // ERROR, the resized buffer doesn't have its resize flag set + } else if (resize == mask) { + // ERROR one of the buffer has already been resized + } else if (resize == mask ^ eResizeRequested) { + // ERROR, the resized buffer doesn't have its resize flag set + } else if (resize == eResizeRequested) { + // OK, Normal case, proceed with resize + } + } else { + if (resize == 0) { + // OK, nothing special, do nothing + } else if (resize == mask) { + // restarted transaction, do nothing + } else if (resize == mask ^ eResizeRequested) { + // restarted transaction, do nothing + } else if (resize == eResizeRequested) { + // OK, size reset to previous value, proceed with resize + } + } + */ + + // Index of the back buffer + const bool backbufferChanged = (front.w != temp.w) || (front.h != temp.h); + const uint32_t state = lcblk->swapState; + const int32_t clientBackBufferIndex = layer_cblk_t::backBuffer(state); + const uint32_t mask = clientBackBufferIndex ? eResizeBuffer1 : eResizeBuffer0; + uint32_t resizeFlags = state & eResizeRequested; + + if (UNLIKELY(backbufferChanged && (resizeFlags != eResizeRequested))) { + LOGE( "backbuffer size changed, but both resize flags are not set! " + "(layer=%p), state=%08x, requested (%dx%d), drawing (%d,%d), " + "index=%d, (%dx%d), (%dx%d)", + this, state, + int(temp.w), int(temp.h), + int(drawingState().w), int(drawingState().h), + int(clientBackBufferIndex), + int(mBuffers[0].width()), int(mBuffers[0].height()), + int(mBuffers[1].width()), int(mBuffers[1].height())); + // if we get there we're pretty screwed. the only reasonable + // thing to do is to pretend we should do the resize since + // backbufferChanged is set (this also will give a chance to + // client to get unblocked) + resizeFlags = eResizeRequested; + } + + if (resizeFlags == eResizeRequested) { + // NOTE: asserting that clientBackBufferIndex!=mFrontBufferIndex + // here, would be wrong and misleading because by this point + // mFrontBufferIndex has not been updated yet. + + LOGD_IF(DEBUG_RESIZE, + "resize (layer=%p), state=%08x, " + "requested (%dx%d), " + "drawing (%d,%d), " + "index=%d, (%dx%d), (%dx%d)", + this, state, + int(temp.w), int(temp.h), + int(drawingState().w), int(drawingState().h), + int(clientBackBufferIndex), + int(mBuffers[0].width()), int(mBuffers[0].height()), + int(mBuffers[1].width()), int(mBuffers[1].height())); + + if (state & eLocked) { + // if the buffer is locked, we can't resize anything because + // - the backbuffer is currently in use by the user + // - the front buffer is being shown + // We just act as if the transaction didn't happen and we + // reschedule it later... + flags |= eRestartTransaction; + } else { + // This buffer needs to be resized + status_t err = + resize(clientBackBufferIndex, temp.w, temp.h, "transaction"); + if (err == NO_ERROR) { + const uint32_t mask = clientBackBufferIndex ? eResizeBuffer1 : eResizeBuffer0; + android_atomic_and(~mask, &(lcblk->swapState)); + // since a buffer became available, we can let the client go... + mFlinger->scheduleBroadcast(client); + mResizeTransactionDone = true; + + // we're being resized and there is a freeze display request, + // acquire a freeze lock, so that the screen stays put + // until we've redrawn at the new size; this is to avoid + // glitches upon orientation changes. + if (mFlinger->hasFreezeRequest()) { + // if the surface is hidden, don't try to acquire the + // freeze lock, since hidden surfaces may never redraw + if (!(front.flags & ISurfaceComposer::eLayerHidden)) { + mFreezeLock = mFlinger->getFreezeLock(); + } + } + } + } + } + + if (temp.sequence != front.sequence) { + if (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha == 0) { + // this surface is now hidden, so it shouldn't hold a freeze lock + // (it may never redraw, which is fine if it is hidden) + mFreezeLock.clear(); + } + } + + return LayerBase::doTransaction(flags); +} + +status_t Layer::resize( + int32_t clientBackBufferIndex, + uint32_t width, uint32_t height, + const char* what) +{ + /* + * handle resize (backbuffer and frontbuffer reallocation) + */ + + const LayerBitmap& clientBackBuffer(mBuffers[clientBackBufferIndex]); + + // if the new (transaction) size is != from the the backbuffer + // then we need to reallocate the backbuffer + bool backbufferChanged = (clientBackBuffer.width() != width) || + (clientBackBuffer.height() != height); + + LOGD_IF(!backbufferChanged, + "(%s) eResizeRequested (layer=%p), but size not changed: " + "requested (%dx%d), drawing (%d,%d), current (%d,%d)," + "state=%08lx, index=%d, (%dx%d), (%dx%d)", + what, this, + int(width), int(height), + int(drawingState().w), int(drawingState().h), + int(currentState().w), int(currentState().h), + long(lcblk->swapState), + int(clientBackBufferIndex), + int(mBuffers[0].width()), int(mBuffers[0].height()), + int(mBuffers[1].width()), int(mBuffers[1].height())); + + // this can happen when changing the size back and forth quickly + status_t err = NO_ERROR; + if (backbufferChanged) { + err = reallocateBuffer(clientBackBufferIndex, width, height); + } + if (UNLIKELY(err != NO_ERROR)) { + // couldn't reallocate the surface + android_atomic_write(eInvalidSurface, &lcblk->swapState); + memset(lcblk->surface+clientBackBufferIndex, 0, sizeof(surface_info_t)); + } + return err; +} + +void Layer::setSizeChanged(uint32_t w, uint32_t h) +{ + LOGD_IF(DEBUG_RESIZE, + "setSizeChanged w=%d, h=%d (old: w=%d, h=%d)", + w, h, mCurrentState.w, mCurrentState.h); + android_atomic_or(eResizeRequested, &(lcblk->swapState)); +} + +// ---------------------------------------------------------------------------- +// pageflip handling... +// ---------------------------------------------------------------------------- + +void Layer::lockPageFlip(bool& recomputeVisibleRegions) +{ + uint32_t state = android_atomic_or(eBusy, &(lcblk->swapState)); + // preemptively block the client, because he might set + // eFlipRequested at any time and want to use this buffer + // for the next frame. This will be unset below if it + // turns out we didn't need it. + + uint32_t mask = eInvalidSurface | eFlipRequested | eResizeRequested; + if (!(state & mask)) + return; + + if (UNLIKELY(state & eInvalidSurface)) { + // if eInvalidSurface is set, this means the surface + // became invalid during a transaction (NO_MEMORY for instance) + mFlinger->scheduleBroadcast(client); + return; + } + + if (UNLIKELY(state & eFlipRequested)) { + uint32_t oldState; + mPostedDirtyRegion = post(&oldState, recomputeVisibleRegions); + if (oldState & eNextFlipPending) { + // Process another round (we know at least a buffer + // is ready for that client). + mFlinger->signalEvent(); + } + } +} + +Region Layer::post(uint32_t* previousSate, bool& recomputeVisibleRegions) +{ + // atomically swap buffers and (re)set eFlipRequested + int32_t oldValue, newValue; + layer_cblk_t * const lcblk = this->lcblk; + do { + oldValue = lcblk->swapState; + // get the current value + + LOG_ASSERT(oldValue&eFlipRequested, + "eFlipRequested not set, yet we're flipping! (state=0x%08lx)", + long(oldValue)); + + newValue = (oldValue ^ eIndex); + // swap buffers + + newValue &= ~(eFlipRequested | eNextFlipPending); + // clear eFlipRequested and eNextFlipPending + + if (oldValue & eNextFlipPending) + newValue |= eFlipRequested; + // if eNextFlipPending is set (second buffer already has something + // in it) we need to reset eFlipRequested because the client + // might never do it + + } while(android_atomic_cmpxchg(oldValue, newValue, &(lcblk->swapState))); + *previousSate = oldValue; + + const int32_t index = (newValue & eIndex) ^ 1; + mFrontBufferIndex = index; + + // ... post the new front-buffer + Region dirty(lcblk->region + index); + dirty.andSelf(frontBuffer().bounds()); + + //LOGI("Did post oldValue=%08lx, newValue=%08lx, mFrontBufferIndex=%u\n", + // oldValue, newValue, mFrontBufferIndex); + //dirty.dump("dirty"); + + if (UNLIKELY(oldValue & eResizeRequested)) { + + LOGD_IF(DEBUG_RESIZE, + "post (layer=%p), state=%08x, " + "index=%d, (%dx%d), (%dx%d)", + this, newValue, + int(1-index), + int(mBuffers[0].width()), int(mBuffers[0].height()), + int(mBuffers[1].width()), int(mBuffers[1].height())); + + // here, we just posted the surface and we have resolved + // the front/back buffer indices. The client is blocked, so + // it cannot start using the new backbuffer. + + // If the backbuffer was resized in THIS round, we actually cannot + // resize the frontbuffer because it has *just* been drawn (and we + // would have nothing to draw). In this case we just skip the resize + // it'll happen after the next page flip or during the next + // transaction. + + const uint32_t mask = (1-index) ? eResizeBuffer1 : eResizeBuffer0; + if (mResizeTransactionDone && (newValue & mask)) { + // Resize the layer's second buffer only if the transaction + // happened. It may not have happened yet if eResizeRequested + // was set immediately after the "transactionRequested" test, + // in which case the drawing state's size would be wrong. + mFreezeLock.clear(); + const Layer::State& s(drawingState()); + if (resize(1-index, s.w, s.h, "post") == NO_ERROR) { + do { + oldValue = lcblk->swapState; + if ((oldValue & eResizeRequested) == eResizeRequested) { + // ugh, another resize was requested since we processed + // the first buffer, don't free the client, and let + // the next transaction handle everything. + break; + } + newValue = oldValue & ~mask; + } while(android_atomic_cmpxchg(oldValue, newValue, &(lcblk->swapState))); + } + mResizeTransactionDone = false; + recomputeVisibleRegions = true; + this->contentDirty = true; + } + } + + reloadTexture(dirty); + + return dirty; +} + +Point Layer::getPhysicalSize() const +{ + const LayerBitmap& front(frontBuffer()); + return Point(front.width(), front.height()); +} + +void Layer::unlockPageFlip( + const Transform& planeTransform, Region& outDirtyRegion) +{ + Region dirtyRegion(mPostedDirtyRegion); + if (!dirtyRegion.isEmpty()) { + mPostedDirtyRegion.clear(); + // The dirty region is given in the layer's coordinate space + // transform the dirty region by the surface's transformation + // and the global transformation. + const Layer::State& s(drawingState()); + const Transform tr(planeTransform * s.transform); + dirtyRegion = tr.transform(dirtyRegion); + + // At this point, the dirty region is in screen space. + // Make sure it's constrained by the visible region (which + // is in screen space as well). + dirtyRegion.andSelf(visibleRegionScreen); + outDirtyRegion.orSelf(dirtyRegion); + + // client could be blocked, so signal them so they get a + // chance to reevaluate their condition. + mFlinger->scheduleBroadcast(client); + } +} + +void Layer::finishPageFlip() +{ + if (LIKELY(!(lcblk->swapState & eInvalidSurface))) { + LOGE_IF(!(lcblk->swapState & eBusy), + "layer %p wasn't locked!", this); + android_atomic_and(~eBusy, &(lcblk->swapState)); + } + mFlinger->scheduleBroadcast(client); +} + + +// --------------------------------------------------------------------------- + + +}; // namespace android diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h new file mode 100644 index 000000000..2867f2b00 --- /dev/null +++ b/libs/surfaceflinger/Layer.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_LAYER_H +#define ANDROID_LAYER_H + +#include +#include + +#include + +#include +#include + +#include + +#include "LayerBitmap.h" +#include "LayerBase.h" +#include "Transform.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class Client; +class LayerBitmap; +class MemoryDealer; +class FreezeLock; + +// --------------------------------------------------------------------------- + +class Layer : public LayerBaseClient +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + Layer(SurfaceFlinger* flinger, DisplayID display, + Client* c, int32_t i); + + virtual ~Layer(); + + inline PixelFormat pixelFormat() const { + return frontBuffer().pixelFormat(); + } + + status_t setBuffers( Client* client, + uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags=0); + + virtual void onDraw(const Region& clip) const; + virtual void initStates(uint32_t w, uint32_t h, uint32_t flags); + virtual void setSizeChanged(uint32_t w, uint32_t h); + virtual uint32_t doTransaction(uint32_t transactionFlags); + virtual Point getPhysicalSize() const; + virtual void lockPageFlip(bool& recomputeVisibleRegions); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + virtual void finishPageFlip(); + virtual bool needsBlending() const { return mNeedsBlending; } + virtual bool isSecure() const { return mSecure; } + virtual GLuint getTextureName() const { return mTextureName; } + virtual sp getSurface() const; + + const LayerBitmap& getBuffer(int i) const { return mBuffers[i]; } + LayerBitmap& getBuffer(int i) { return mBuffers[i]; } + + // only for debugging + const sp& getFreezeLock() const { return mFreezeLock; } + +private: + inline const LayerBitmap& + frontBuffer() const { return getBuffer(mFrontBufferIndex); } + inline LayerBitmap& + frontBuffer() { return getBuffer(mFrontBufferIndex); } + inline const LayerBitmap& + backBuffer() const { return getBuffer(1-mFrontBufferIndex); } + inline LayerBitmap& + backBuffer() { return getBuffer(1-mFrontBufferIndex); } + + void reloadTexture(const Region& dirty); + + status_t resize(int32_t index, uint32_t w, uint32_t h, const char* what); + Region post(uint32_t* oldState, bool& recomputeVisibleRegions); + status_t reallocateBuffer(int32_t index, uint32_t w, uint32_t h); + + sp mSurface; + + bool mSecure; + LayerBitmap mBuffers[2]; + int32_t mFrontBufferIndex; + bool mNeedsBlending; + bool mResizeTransactionDone; + Region mPostedDirtyRegion; + sp mFreezeLock; + + GLuint mTextureName; + GLuint mTextureWidth; + GLuint mTextureHeight; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_H diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp new file mode 100644 index 000000000..0cf53f796 --- /dev/null +++ b/libs/surfaceflinger/LayerBase.cpp @@ -0,0 +1,740 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "clz.h" +#include "LayerBase.h" +#include "LayerBlur.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + + +// We don't honor the premultiplied alpha flags, which means that +// premultiplied surface may be composed using a non-premultiplied +// equation. We do this because it may be a lot faster on some hardware +// The correct value is HONOR_PREMULTIPLIED_ALPHA = 1 +#define HONOR_PREMULTIPLIED_ALPHA 0 + +namespace android { + +// --------------------------------------------------------------------------- + +const uint32_t LayerBase::typeInfo = 1; +const char* const LayerBase::typeID = "LayerBase"; + +const uint32_t LayerBaseClient::typeInfo = LayerBase::typeInfo | 2; +const char* const LayerBaseClient::typeID = "LayerBaseClient"; + +// --------------------------------------------------------------------------- + +Vector LayerBase::deletedTextures; + +int32_t LayerBase::sIdentity = 0; + +LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) + : dpy(display), contentDirty(false), + mFlinger(flinger), + mTransformed(false), + mOrientation(0), + mCanUseCopyBit(false), + mTransactionFlags(0), + mPremultipliedAlpha(true), + mIdentity(uint32_t(android_atomic_inc(&sIdentity))), + mInvalidate(0) +{ + const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); + mFlags = hw.getFlags(); +} + +LayerBase::~LayerBase() +{ +} + +const GraphicPlane& LayerBase::graphicPlane(int dpy) const +{ + return mFlinger->graphicPlane(dpy); +} + +GraphicPlane& LayerBase::graphicPlane(int dpy) +{ + return mFlinger->graphicPlane(dpy); +} + +void LayerBase::initStates(uint32_t w, uint32_t h, uint32_t flags) +{ + uint32_t layerFlags = 0; + if (flags & ISurfaceComposer::eHidden) + layerFlags = ISurfaceComposer::eLayerHidden; + + if (flags & ISurfaceComposer::eNonPremultiplied) + mPremultipliedAlpha = false; + + mCurrentState.z = 0; + mCurrentState.w = w; + mCurrentState.h = h; + mCurrentState.alpha = 0xFF; + mCurrentState.flags = layerFlags; + mCurrentState.sequence = 0; + mCurrentState.transform.set(0, 0); + + // drawing state & current state are identical + mDrawingState = mCurrentState; +} + +void LayerBase::commitTransaction(bool skipSize) { + const uint32_t w = mDrawingState.w; + const uint32_t h = mDrawingState.h; + mDrawingState = mCurrentState; + if (skipSize) { + mDrawingState.w = w; + mDrawingState.h = h; + } +} +void LayerBase::forceVisibilityTransaction() { + // this can be called without SurfaceFlinger.mStateLock, but if we + // can atomically increment the sequence number, it doesn't matter. + android_atomic_inc(&mCurrentState.sequence); + requestTransaction(); +} +bool LayerBase::requestTransaction() { + int32_t old = setTransactionFlags(eTransactionNeeded); + return ((old & eTransactionNeeded) == 0); +} +uint32_t LayerBase::getTransactionFlags(uint32_t flags) { + return android_atomic_and(~flags, &mTransactionFlags) & flags; +} +uint32_t LayerBase::setTransactionFlags(uint32_t flags) { + return android_atomic_or(flags, &mTransactionFlags); +} + +void LayerBase::setSizeChanged(uint32_t w, uint32_t h) { +} + +bool LayerBase::setPosition(int32_t x, int32_t y) { + if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) + return false; + mCurrentState.sequence++; + mCurrentState.transform.set(x, y); + requestTransaction(); + return true; +} +bool LayerBase::setLayer(uint32_t z) { + if (mCurrentState.z == z) + return false; + mCurrentState.sequence++; + mCurrentState.z = z; + requestTransaction(); + return true; +} +bool LayerBase::setSize(uint32_t w, uint32_t h) { + if (mCurrentState.w == w && mCurrentState.h == h) + return false; + setSizeChanged(w, h); + mCurrentState.w = w; + mCurrentState.h = h; + requestTransaction(); + return true; +} +bool LayerBase::setAlpha(uint8_t alpha) { + if (mCurrentState.alpha == alpha) + return false; + mCurrentState.sequence++; + mCurrentState.alpha = alpha; + requestTransaction(); + return true; +} +bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) { + // TODO: check the matrix has changed + mCurrentState.sequence++; + mCurrentState.transform.set( + matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy); + requestTransaction(); + return true; +} +bool LayerBase::setTransparentRegionHint(const Region& transparent) { + // TODO: check the region has changed + mCurrentState.sequence++; + mCurrentState.transparentRegion = transparent; + requestTransaction(); + return true; +} +bool LayerBase::setFlags(uint8_t flags, uint8_t mask) { + const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask); + if (mCurrentState.flags == newFlags) + return false; + mCurrentState.sequence++; + mCurrentState.flags = newFlags; + requestTransaction(); + return true; +} + +Rect LayerBase::visibleBounds() const +{ + return mTransformedBounds; +} + +void LayerBase::setVisibleRegion(const Region& visibleRegion) { + // always called from main thread + visibleRegionScreen = visibleRegion; +} + +void LayerBase::setCoveredRegion(const Region& coveredRegion) { + // always called from main thread + coveredRegionScreen = coveredRegion; +} + +uint32_t LayerBase::doTransaction(uint32_t flags) +{ + const Layer::State& front(drawingState()); + const Layer::State& temp(currentState()); + + if (temp.sequence != front.sequence) { + // invalidate and recompute the visible regions if needed + flags |= eVisibleRegion; + this->contentDirty = true; + } + + // Commit the transaction + commitTransaction(flags & eRestartTransaction); + return flags; +} + +Point LayerBase::getPhysicalSize() const +{ + const Layer::State& front(drawingState()); + return Point(front.w, front.h); +} + +void LayerBase::validateVisibility(const Transform& planeTransform) +{ + const Layer::State& s(drawingState()); + const Transform tr(planeTransform * s.transform); + const bool transformed = tr.transformed(); + + const Point size(getPhysicalSize()); + uint32_t w = size.x; + uint32_t h = size.y; + tr.transform(mVertices[0], 0, 0); + tr.transform(mVertices[1], 0, h); + tr.transform(mVertices[2], w, h); + tr.transform(mVertices[3], w, 0); + if (UNLIKELY(transformed)) { + // NOTE: here we could also punt if we have too many rectangles + // in the transparent region + if (tr.preserveRects()) { + // transform the transparent region + transparentRegionScreen = tr.transform(s.transparentRegion); + } else { + // transformation too complex, can't do the transparent region + // optimization. + transparentRegionScreen.clear(); + } + } else { + transparentRegionScreen = s.transparentRegion; + } + + // cache a few things... + mOrientation = tr.getOrientation(); + mTransformedBounds = tr.makeBounds(w, h); + mTransformed = transformed; + mLeft = tr.tx(); + mTop = tr.ty(); + + // see if we can/should use 2D h/w with the new configuration + mCanUseCopyBit = false; + copybit_device_t* copybit = mFlinger->getBlitEngine(); + if (copybit) { + const int step = copybit->get(copybit, COPYBIT_ROTATION_STEP_DEG); + const int scaleBits = copybit->get(copybit, COPYBIT_SCALING_FRAC_BITS); + mCanUseCopyBit = true; + if ((mOrientation < 0) && (step > 1)) { + // arbitrary orientations not supported + mCanUseCopyBit = false; + } else if ((mOrientation > 0) && (step > 90)) { + // 90 deg rotations not supported + mCanUseCopyBit = false; + } else if ((tr.getType() & SkMatrix::kScale_Mask) && (scaleBits < 12)) { + // arbitrary scaling not supported + mCanUseCopyBit = false; + } +#if HONOR_PREMULTIPLIED_ALPHA + else if (needsBlending() && mPremultipliedAlpha) { + // pre-multiplied alpha not supported + mCanUseCopyBit = false; + } +#endif + else { + // here, we determined we can use copybit + if (tr.getType() & SkMatrix::kScale_Mask) { + // and we have scaling + if (!transparentRegionScreen.isRect()) { + // we punt because blending is cheap (h/w) and the region is + // complex, which may causes artifacts when copying + // scaled content + transparentRegionScreen.clear(); + } + } + } + } +} + +void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) +{ +} + +void LayerBase::unlockPageFlip( + const Transform& planeTransform, Region& outDirtyRegion) +{ + if ((android_atomic_and(~1, &mInvalidate)&1) == 1) { + outDirtyRegion.orSelf(visibleRegionScreen); + } +} + +void LayerBase::finishPageFlip() +{ +} + +void LayerBase::invalidate() +{ + if ((android_atomic_or(1, &mInvalidate)&1) == 0) { + mFlinger->signalEvent(); + } +} + +void LayerBase::drawRegion(const Region& reg) const +{ + Region::iterator iterator(reg); + if (iterator) { + Rect r; + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const int32_t fbWidth = hw.getWidth(); + const int32_t fbHeight = hw.getHeight(); + const GLshort vertices[][2] = { { 0, 0 }, { fbWidth, 0 }, + { fbWidth, fbHeight }, { 0, fbHeight } }; + glVertexPointer(2, GL_SHORT, 0, vertices); + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } +} + +void LayerBase::draw(const Region& inClip) const +{ + // invalidate the region we'll update + Region clip(inClip); // copy-on-write, so no-op most of the time + + // Remove the transparent area from the clipping region + const State& s = drawingState(); + if (LIKELY(!s.transparentRegion.isEmpty())) { + clip.subtract(transparentRegionScreen); + if (clip.isEmpty()) { + // usually this won't happen because this should be taken care of + // by SurfaceFlinger::computeVisibleRegions() + return; + } + } + + // reset GL state + glEnable(GL_SCISSOR_TEST); + + onDraw(clip); + + /* + glDisable(GL_TEXTURE_2D); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4x(0, 0x8000, 0, 0x10000); + drawRegion(transparentRegionScreen); + glDisable(GL_BLEND); + */ +} + +GLuint LayerBase::createTexture() const +{ + GLuint textureName = -1; + glGenTextures(1, &textureName); + glBindTexture(GL_TEXTURE_2D, textureName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (mFlags & DisplayHardware::SLOW_CONFIG) { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } else { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + return textureName; +} + +void LayerBase::clearWithOpenGL(const Region& clip) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + glColor4x(0,0,0,0); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + Rect r; + Region::iterator iterator(clip); + if (iterator) { + glEnable(GL_SCISSOR_TEST); + glVertexPointer(2, GL_FIXED, 0, mVertices); + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } +} + +void LayerBase::drawWithOpenGL(const Region& clip, + GLint textureName, const GGLSurface& t, int transform) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + const State& s(drawingState()); + + // bind our texture + validateTexture(textureName); + glEnable(GL_TEXTURE_2D); + + // Dithering... + if (s.flags & ISurfaceComposer::eLayerDither) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); + } + + if (UNLIKELY(s.alpha < 0xFF)) { + // We have an alpha-modulation. We need to modulate all + // texture components by alpha because we're always using + // premultiplied alpha. + + // If the texture doesn't have an alpha channel we can + // use REPLACE and switch to non premultiplied alpha + // blending (SRCA/ONE_MINUS_SRCA). + + GLenum env, src; + if (needsBlending()) { + env = GL_MODULATE; + src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + } else { + env = GL_REPLACE; + src = GL_SRC_ALPHA; + } + const GGLfixed alpha = (s.alpha << 16)/255; + glColor4x(alpha, alpha, alpha, alpha); + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env); + } else { + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor4x(0x10000, 0x10000, 0x10000, 0x10000); + if (needsBlending()) { + GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } + } + + if (UNLIKELY(transformed() + || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) + { + //StopWatch watch("GL transformed"); + Region::iterator iterator(clip); + if (iterator) { + // always use high-quality filtering with fast configurations + bool fast = !(mFlags & DisplayHardware::SLOW_CONFIG); + if (!fast && s.flags & ISurfaceComposer::eLayerFilter) { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + const GLfixed texCoords[4][2] = { + { 0, 0 }, + { 0, 0x10000 }, + { 0x10000, 0x10000 }, + { 0x10000, 0 } + }; + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + if (transform == HAL_TRANSFORM_ROT_90) { + glTranslatef(0, 1, 0); + glRotatef(-90, 0, 0, 1); + } + + if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { + // find the smallest power-of-two that will accommodate our surface + GLuint tw = 1 << (31 - clz(t.width)); + GLuint th = 1 << (31 - clz(t.height)); + if (tw < t.width) tw <<= 1; + if (th < t.height) th <<= 1; + // this divide should be relatively fast because it's + // a power-of-two (optimized path in libgcc) + GLfloat ws = GLfloat(t.width) /tw; + GLfloat hs = GLfloat(t.height)/th; + glScalef(ws, hs, 1.0f); + } + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FIXED, 0, mVertices); + glTexCoordPointer(2, GL_FIXED, 0, texCoords); + + Rect r; + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + if (!fast && s.flags & ISurfaceComposer::eLayerFilter) { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + } else { + Region::iterator iterator(clip); + if (iterator) { + Rect r; + GLint crop[4] = { 0, t.height, t.width, -t.height }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + int x = tx(); + int y = ty(); + y = fbHeight - (y + t.height); + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawTexiOES(x, y, 0, t.width, t.height); + } + } + } +} + +void LayerBase::validateTexture(GLint textureName) const +{ + glBindTexture(GL_TEXTURE_2D, textureName); + // TODO: reload the texture if needed + // this is currently done in loadTexture() below +} + +void LayerBase::loadTexture(const Region& dirty, + GLint textureName, const GGLSurface& t, + GLuint& textureWidth, GLuint& textureHeight) const +{ + // TODO: defer the actual texture reload until LayerBase::validateTexture + // is called. + + uint32_t flags = mFlags; + glBindTexture(GL_TEXTURE_2D, textureName); + + GLuint tw = t.width; + GLuint th = t.height; + + /* + * In OpenGL ES we can't specify a stride with glTexImage2D (however, + * GL_UNPACK_ALIGNMENT is 4, which in essence allows a limited form of + * stride). + * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we + * need to do something reasonable (here creating a bigger texture). + * + * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); + * + * This situation doesn't happen often, but some h/w have a limitation + * for their framebuffer (eg: must be multiple of 8 pixels), and + * we need to take that into account when using these buffers as + * textures. + * + * This should never be a problem with POT textures + */ + + tw += (((t.stride - tw) * bytesPerPixel(t.format)) / 4); + + /* + * round to POT if needed + */ + + GLuint texture_w = tw; + GLuint texture_h = th; + if (!(flags & DisplayHardware::NPOT_EXTENSION)) { + // find the smallest power-of-two that will accommodate our surface + texture_w = 1 << (31 - clz(t.width)); + texture_h = 1 << (31 - clz(t.height)); + if (texture_w < t.width) texture_w <<= 1; + if (texture_h < t.height) texture_h <<= 1; + if (texture_w != tw || texture_h != th) { + // we can't use DIRECT_TEXTURE since we changed the size + // of the texture + flags &= ~DisplayHardware::DIRECT_TEXTURE; + } + } + + if (flags & DisplayHardware::DIRECT_TEXTURE) { + // here we're guaranteed that texture_{w|h} == t{w|h} + if (t.format == GGL_PIXEL_FORMAT_RGB_565) { + glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, + GL_RGB, tw, th, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, t.data); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { + glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, + GL_RGBA, tw, th, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, t.data); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { + glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, + GL_RGBA, tw, th, 0, + GL_RGBA, GL_UNSIGNED_BYTE, t.data); + } else if (t.format == GGL_PIXEL_FORMAT_BGRA_8888) { + // TODO: add GL_BGRA extension + } else { + // oops, we don't handle this format, try the regular path + goto regular; + } + textureWidth = tw; + textureHeight = th; + } else { +regular: + Rect bounds(dirty.bounds()); + GLvoid* data = 0; + if (texture_w!=textureWidth || texture_h!=textureHeight) { + // texture size changed, we need to create a new one + + if (!textureWidth || !textureHeight) { + // this is the first time, load the whole texture + if (texture_w==tw && texture_h==th) { + // we can do it one pass + data = t.data; + } else { + // we have to create the texture first because it + // doesn't match the size of the buffer + bounds.set(Rect(tw, th)); + } + } + + if (t.format == GGL_PIXEL_FORMAT_RGB_565) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGB, texture_w, texture_h, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture_w, texture_h, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture_w, texture_h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + } else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP || + t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) { + // just show the Y plane of YUV buffers + data = t.data; + glTexImage2D(GL_TEXTURE_2D, 0, + GL_LUMINANCE, texture_w, texture_h, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + } else { + // oops, we don't handle this format! + LOGE("layer %p, texture=%d, using format %d, which is not " + "supported by the GL", this, textureName, t.format); + textureName = -1; + } + textureWidth = texture_w; + textureHeight = texture_h; + } + if (!data && textureName>=0) { + if (t.format == GGL_PIXEL_FORMAT_RGB_565) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + t.data + bounds.top*t.width*2); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, + t.data + bounds.top*t.width*2); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.width*4); + } + } + } +} + +bool LayerBase::canUseCopybit() const +{ + return mCanUseCopyBit; +} + +// --------------------------------------------------------------------------- + +LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + Client* c, int32_t i) + : LayerBase(flinger, display), client(c), + lcblk( c ? &(c->ctrlblk->layers[i]) : 0 ), + mIndex(i) +{ + if (client) { + client->bindLayer(this, i); + + // Initialize this layer's control block + memset(this->lcblk, 0, sizeof(layer_cblk_t)); + this->lcblk->identity = mIdentity; + Region::writeEmpty(&(this->lcblk->region[0]), sizeof(flat_region_t)); + Region::writeEmpty(&(this->lcblk->region[1]), sizeof(flat_region_t)); + } +} + +LayerBaseClient::~LayerBaseClient() +{ + if (client) { + client->free(mIndex); + } +} + +int32_t LayerBaseClient::serverIndex() const { + if (client) { + return (client->cid<<16)|mIndex; + } + return 0xFFFF0000 | mIndex; +} + +sp LayerBaseClient::getSurface() const +{ + return new Surface(clientIndex(), mIdentity); +} + + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h new file mode 100644 index 000000000..a020f44e8 --- /dev/null +++ b/libs/surfaceflinger/LayerBase.h @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_LAYER_BASE_H +#define ANDROID_LAYER_BASE_H + +#include +#include + +#include + +#include +#include + +#include + +#include "Transform.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class SurfaceFlinger; +class DisplayHardware; +class GraphicPlane; +class Client; + +// --------------------------------------------------------------------------- + +class LayerBase +{ + // poor man's dynamic_cast below + template + struct getTypeInfoOfAnyType { + static uint32_t get() { return T::typeInfo; } + }; + + template + struct getTypeInfoOfAnyType { + static uint32_t get() { return getTypeInfoOfAnyType::get(); } + }; + +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + template + static T dynamicCast(LayerBase* base) { + uint32_t mostDerivedInfo = base->getTypeInfo(); + uint32_t castToInfo = getTypeInfoOfAnyType::get(); + if ((mostDerivedInfo & castToInfo) == castToInfo) + return static_cast(base); + return 0; + } + + + static Vector deletedTextures; + + LayerBase(SurfaceFlinger* flinger, DisplayID display); + virtual ~LayerBase(); + + DisplayID dpy; + mutable bool contentDirty; + Region visibleRegionScreen; + Region transparentRegionScreen; + Region coveredRegionScreen; + + struct State { + uint32_t w; + uint32_t h; + uint32_t z; + uint8_t alpha; + uint8_t flags; + uint8_t reserved[2]; + int32_t sequence; // changes when visible regions can change + uint32_t tint; + Transform transform; + Region transparentRegion; + }; + + // modify current state + bool setPosition(int32_t x, int32_t y); + bool setLayer(uint32_t z); + bool setSize(uint32_t w, uint32_t h); + bool setAlpha(uint8_t alpha); + bool setMatrix(const layer_state_t::matrix22_t& matrix); + bool setTransparentRegionHint(const Region& opaque); + bool setFlags(uint8_t flags, uint8_t mask); + + void commitTransaction(bool skipSize); + bool requestTransaction(); + void forceVisibilityTransaction(); + + uint32_t getTransactionFlags(uint32_t flags); + uint32_t setTransactionFlags(uint32_t flags); + + Rect visibleBounds() const; + void drawRegion(const Region& reg) const; + + void invalidate(); + + /** + * draw - performs some global clipping optimizations + * and calls onDraw(). + * Typically this method is not overridden, instead implement onDraw() + * to perform the actual drawing. + */ + virtual void draw(const Region& clip) const; + + /** + * onDraw - draws the surface. + */ + virtual void onDraw(const Region& clip) const = 0; + + /** + * initStates - called just after construction + */ + virtual void initStates(uint32_t w, uint32_t h, uint32_t flags); + + /** + * setSizeChanged - called when the *current* state's size is changed. + */ + virtual void setSizeChanged(uint32_t w, uint32_t h); + + /** + * doTransaction - process the transaction. This is a good place to figure + * out which attributes of the surface have changed. + */ + virtual uint32_t doTransaction(uint32_t transactionFlags); + + /** + * setVisibleRegion - called to set the new visible region. This gives + * a chance to update the new visible region or record the fact it changed. + */ + virtual void setVisibleRegion(const Region& visibleRegion); + + /** + * setCoveredRegion - called when the covered region changes. The covered + * region correspond to any area of the surface that is covered + * (transparently or not) by another surface. + */ + virtual void setCoveredRegion(const Region& coveredRegion); + + /** + * getPhysicalSize - returns the physical size of the drawing state of + * the surface. If the surface is backed by a bitmap, this is the size of + * the bitmap (as opposed to the size of the drawing state). + */ + virtual Point getPhysicalSize() const; + + /** + * validateVisibility - cache a bunch of things + */ + virtual void validateVisibility(const Transform& globalTransform); + + /** + * lockPageFlip - called each time the screen is redrawn and returns whether + * the visible regions need to be recomputed (this is a fairly heavy + * operation, so this should be set only if needed). Typically this is used + * to figure out if the content or size of a surface has changed. + */ + virtual void lockPageFlip(bool& recomputeVisibleRegions); + + /** + * unlockPageFlip - called each time the screen is redrawn. updates the + * final dirty region wrt the planeTransform. + * At this point, all visible regions, surface position and size, etc... are + * correct. + */ + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + + /** + * finishPageFlip - called after all surfaces have drawn. + */ + virtual void finishPageFlip(); + + /** + * needsBlending - true if this surface needs blending + */ + virtual bool needsBlending() const { return false; } + + /** + * transformed -- true is this surface needs a to be transformed + */ + virtual bool transformed() const { return mTransformed; } + + /** + * isSecure - true if this surface is secure, that is if it prevents + * screenshots or vns servers. + */ + virtual bool isSecure() const { return false; } + + enum { // flags for doTransaction() + eVisibleRegion = 0x00000002, + eRestartTransaction = 0x00000008 + }; + + + inline const State& drawingState() const { return mDrawingState; } + inline const State& currentState() const { return mCurrentState; } + inline State& currentState() { return mCurrentState; } + + static int compareCurrentStateZ(LayerBase*const* layerA, LayerBase*const* layerB) { + return layerA[0]->currentState().z - layerB[0]->currentState().z; + } + + int32_t getOrientation() const { return mOrientation; } + int tx() const { return mLeft; } + int ty() const { return mTop; } + +protected: + const GraphicPlane& graphicPlane(int dpy) const; + GraphicPlane& graphicPlane(int dpy); + + GLuint createTexture() const; + + void drawWithOpenGL(const Region& clip, + GLint textureName, + const GGLSurface& surface, + int transform = 0) const; + + void clearWithOpenGL(const Region& clip) const; + + void loadTexture(const Region& dirty, + GLint textureName, const GGLSurface& t, + GLuint& textureWidth, GLuint& textureHeight) const; + + bool canUseCopybit() const; + + SurfaceFlinger* mFlinger; + uint32_t mFlags; + + // cached during validateVisibility() + bool mTransformed; + int32_t mOrientation; + GLfixed mVertices[4][2]; + Rect mTransformedBounds; + bool mCanUseCopyBit; + int mLeft; + int mTop; + + // these are protected by an external lock + State mCurrentState; + State mDrawingState; + volatile int32_t mTransactionFlags; + + // don't change, don't need a lock + bool mPremultipliedAlpha; + + // only read + const uint32_t mIdentity; + + // atomic + volatile int32_t mInvalidate; + + +private: + void validateTexture(GLint textureName) const; + static int32_t sIdentity; +}; + + +// --------------------------------------------------------------------------- + +class LayerBaseClient : public LayerBase +{ +public: + class Surface; + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i); + virtual ~LayerBaseClient(); + + + Client* const client; + layer_cblk_t* const lcblk; + + inline int32_t clientIndex() const { return mIndex; } + int32_t serverIndex() const; + + virtual sp getSurface() const; + + uint32_t getIdentity() const { return mIdentity; } + + class Surface : public BnSurface + { + public: + Surface(SurfaceID id, int identity) { + mParams.token = id; + mParams.identity = identity; + } + Surface(SurfaceID id, + const sp& heap0, + const sp& heap1, + int identity) + { + mParams.token = id; + mParams.identity = identity; + mParams.heap[0] = heap0; + mParams.heap[1] = heap1; + } + virtual ~Surface() { + // TODO: We now have a point here were we can clean-up the + // client's mess. + // This is also where surface id should be recycled. + //LOGD("Surface %d, heaps={%p, %p} destroyed", + // mId, mHeap[0].get(), mHeap[1].get()); + } + + virtual void getSurfaceData( + ISurfaceFlingerClient::surface_data_t* params) const { + *params = mParams; + } + + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers) + { return INVALID_OPERATION; } + virtual void postBuffer(ssize_t offset) { } + virtual void unregisterBuffers() { }; + virtual sp createOverlay( + uint32_t w, uint32_t h, int32_t format) { + return NULL; + }; + + private: + ISurfaceFlingerClient::surface_data_t mParams; + }; + +private: + int32_t mIndex; + +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BASE_H diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp new file mode 100644 index 000000000..e84435088 --- /dev/null +++ b/libs/surfaceflinger/LayerBitmap.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "LayerBitmap.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" + + +namespace android { + +// --------------------------------------------------------------------------- + +LayerBitmap::LayerBitmap() + : mAllocFlags(0), mOffset(0), mSize(-1U), mAlignment(2) +{ + memset(&mSurface, 0, sizeof(mSurface)); +} + +LayerBitmap::~LayerBitmap() +{ + mSurface.data = 0; +} + +status_t LayerBitmap::init(const sp& allocator) +{ + if (mAllocator != NULL) + return BAD_VALUE; + mAllocator = allocator; + return NO_ERROR; +} + +status_t LayerBitmap::setBits(uint32_t w, uint32_t h, uint32_t alignment, + PixelFormat format, uint32_t flags) +{ + const sp& allocator(mAllocator); + if (allocator == NULL) + return NO_INIT; + + if (UNLIKELY(w == mSurface.width && h == mSurface.height && + format == mSurface.format)) + { // same format and size, do nothing. + return NO_ERROR; + } + + PixelFormatInfo info; + getPixelFormatInfo(format, &info); + + uint32_t allocFlags = MemoryDealer::PAGE_ALIGNED; + const uint32_t align = 4; // must match GL_UNPACK_ALIGNMENT + const uint32_t Bpp = info.bytesPerPixel; + uint32_t stride = (w + (alignment-1)) & ~(alignment-1); + stride = ((stride * Bpp + (align-1)) & ~(align-1)) / Bpp; + size_t size = info.getScanlineSize(stride) * h; + if (allocFlags & MemoryDealer::PAGE_ALIGNED) { + size_t pagesize = getpagesize(); + size = (size + (pagesize-1)) & ~(pagesize-1); + } + + /* FIXME: we should be able to have a h/v stride because the user of the + * surface might have stride limitation (for instance h/w codecs often do) + */ + int32_t vstride = 0; + + mAlignment = alignment; + mAllocFlags = allocFlags; + mOffset = 0; + if (mSize != size) { + // would be nice to have a reallocate() api + mBitsMemory.clear(); // free-memory + mBitsMemory = allocator->allocate(size, allocFlags); + mSize = size; + } else { + // don't erase memory if we didn't have to reallocate + flags &= ~SECURE_BITS; + } + if (mBitsMemory != 0) { + mOffset = mBitsMemory->offset(); + mSurface.data = static_cast(mBitsMemory->pointer()); + mSurface.version = sizeof(GGLSurface); + mSurface.width = w; + mSurface.height = h; + mSurface.stride = stride; + mSurface.vstride = vstride; + mSurface.format = format; + if (flags & SECURE_BITS) + clear(); + } + + if (mBitsMemory==0 || mSurface.data==0) { + LOGE("not enough memory for layer bitmap size=%u", size); + allocator->dump("LayerBitmap"); + mSurface.data = 0; + mSize = -1U; + return NO_MEMORY; + } + return NO_ERROR; +} + +void LayerBitmap::clear() +{ + // NOTE: this memset should not be necessary, at least for + // opaque surface. However, for security reasons it's better to keep it + // (in the case of pmem, it's possible that the memory contains old + // data) + if (mSurface.data) { + memset(mSurface.data, 0, mSize); + //if (bytesPerPixel(mSurface.format) == 4) { + // android_memset32((uint32_t*)mSurface.data, 0xFF0000FF, mSize); + //} else { + // android_memset16((uint16_t*)mSurface.data, 0xF800, mSize); + //} + } +} + +status_t LayerBitmap::getInfo(surface_info_t* info) const +{ + if (mSurface.data == 0) { + memset(info, 0, sizeof(surface_info_t)); + info->bits_offset = NO_MEMORY; + return NO_MEMORY; + } + info->w = uint16_t(width()); + info->h = uint16_t(height()); + info->stride= uint16_t(stride()); + info->bpr = uint16_t(stride() * bytesPerPixel(pixelFormat())); + info->format= uint8_t(pixelFormat()); + info->flags = surface_info_t::eBufferDirty; + info->bits_offset = ssize_t(mOffset); + return NO_ERROR; +} + +status_t LayerBitmap::resize(uint32_t w, uint32_t h) +{ + int err = setBits(w, h, mAlignment, pixelFormat(), SECURE_BITS); + return err; +} + +size_t LayerBitmap::size() const +{ + return mSize; +} + +void LayerBitmap::getBitmapSurface(copybit_image_t* img) const +{ + const sp& mh(getAllocator()->getMemoryHeap()); + void* sbase = mh->base(); + const GGLSurface& t(surface()); + img->w = t.stride ?: t.width; + img->h = t.vstride ?: t.height; + img->format = t.format; + img->offset = intptr_t(t.data) - intptr_t(sbase); + img->base = sbase; + img->fd = mh->heapID(); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerBitmap.h b/libs/surfaceflinger/LayerBitmap.h new file mode 100644 index 000000000..9ad64c40b --- /dev/null +++ b/libs/surfaceflinger/LayerBitmap.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_LAYER_BITMAP_H +#define ANDROID_LAYER_BITMAP_H + +#include +#include + +#include +#include +#include +#include +#include + +class copybit_image_t; + +namespace android { + +// --------------------------------------------------------------------------- + +class IMemory; +class MemoryDealer; +class LayerBitmap; + +// --------------------------------------------------------------------------- + +class LayerBitmap +{ +public: + + enum { + // erase memory to ensure security when necessary + SECURE_BITS = 0x00000001 + }; + + LayerBitmap(); + ~LayerBitmap(); + status_t init(const sp& allocator); + + status_t setBits(uint32_t w, uint32_t h, uint32_t alignment, + PixelFormat format, uint32_t flags = 0); + void clear(); + + status_t getInfo(surface_info_t* info) const; + status_t resize(uint32_t w, uint32_t h); + + const GGLSurface& surface() const { return mSurface; } + Rect bounds() const { return Rect(width(), height()); } + uint32_t width() const { return surface().width; } + uint32_t height() const { return surface().height; } + uint32_t stride() const { return surface().stride; } + PixelFormat pixelFormat() const { return surface().format; } + void* serverBits() const { return surface().data; } + size_t size() const; + const sp& getAllocator() const { return mAllocator; } + void getBitmapSurface(copybit_image_t* img) const; + +private: + sp mAllocator; + sp mBitsMemory; + uint32_t mAllocFlags; + ssize_t mOffset; + GGLSurface mSurface; + size_t mSize; + uint32_t mAlignment; +}; + +}; // namespace android + +#endif // ANDROID_LAYER_BITMAP_H diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp new file mode 100644 index 000000000..d3e456f1b --- /dev/null +++ b/libs/surfaceflinger/LayerBlur.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include + +#include +#include + +#include +#include + +#include "BlurFilter.h" +#include "LayerBlur.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerBlur::typeInfo = LayerBaseClient::typeInfo | 8; +const char* const LayerBlur::typeID = "LayerBlur"; + +// --------------------------------------------------------------------------- + +LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i) + : LayerBaseClient(flinger, display, client, i), mCacheDirty(true), + mRefreshCache(true), mCacheAge(0), mTextureName(-1U) +{ +} + +LayerBlur::~LayerBlur() +{ + if (mTextureName != -1U) { + //glDeleteTextures(1, &mTextureName); + deletedTextures.add(mTextureName); + } +} + +void LayerBlur::setVisibleRegion(const Region& visibleRegion) +{ + LayerBaseClient::setVisibleRegion(visibleRegion); + if (visibleRegionScreen.isEmpty()) { + if (mTextureName != -1U) { + // We're not visible, free the texture up. + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &mTextureName); + mTextureName = -1U; + } + } +} + +uint32_t LayerBlur::doTransaction(uint32_t flags) +{ + // we're doing a transaction, refresh the cache! + if (!mFlinger->isFrozen()) { + mRefreshCache = true; + mCacheDirty = true; + flags |= eVisibleRegion; + this->contentDirty = true; + } + return LayerBase::doTransaction(flags); +} + +void LayerBlur::unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion) +{ + // this code-path must be as tight as possible, it's called each time + // the screen is composited. + if (UNLIKELY(!visibleRegionScreen.isEmpty())) { + // if anything visible below us is invalidated, the cache becomes dirty + if (!mCacheDirty && + !visibleRegionScreen.intersect(outDirtyRegion).isEmpty()) { + mCacheDirty = true; + } + if (mCacheDirty) { + if (!mFlinger->isFrozen()) { + // update everything below us that is visible + outDirtyRegion.orSelf(visibleRegionScreen); + nsecs_t now = systemTime(); + if ((now - mCacheAge) >= ms2ns(500)) { + mCacheAge = now; + mRefreshCache = true; + mCacheDirty = false; + } else { + if (!mAutoRefreshPending) { + mFlinger->signalDelayedEvent(ms2ns(500)); + mAutoRefreshPending = true; + } + } + } + } + } + LayerBase::unlockPageFlip(planeTransform, outDirtyRegion); +} + +void LayerBlur::onDraw(const Region& clip) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + int x = mTransformedBounds.left; + int y = mTransformedBounds.top; + int w = mTransformedBounds.width(); + int h = mTransformedBounds.height(); + GLint X = x; + GLint Y = fbHeight - (y + h); + if (X < 0) { + w += X; + X = 0; + } + if (Y < 0) { + h += Y; + Y = 0; + } + if (w<0 || h<0) { + // we're outside of the framebuffer + return; + } + + if (mTextureName == -1U) { + // create the texture name the first time + // can't do that in the ctor, because it runs in another thread. + glGenTextures(1, &mTextureName); + } + + Region::iterator iterator(clip); + if (iterator) { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, mTextureName); + + if (mRefreshCache) { + mRefreshCache = false; + mAutoRefreshPending = false; + + // allocate enough memory for 4-bytes (2 pixels) aligned data + const int32_t s = (w + 1) & ~1; + uint16_t* const pixels = (uint16_t*)malloc(s*h*2); + + // This reads the frame-buffer, so a h/w GL would have to + // finish() its rendering first. we don't want to do that + // too often. Read data is 4-bytes aligned. + glReadPixels(X, Y, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); + + // blur that texture. + GGLSurface bl; + bl.version = sizeof(GGLSurface); + bl.width = w; + bl.height = h; + bl.stride = s; + bl.format = GGL_PIXEL_FORMAT_RGB_565; + bl.data = (GGLubyte*)pixels; + blurFilter(&bl, 8, 2); + + // NOTE: this works only because we have POT. we'd have to round the + // texture size up, otherwise. + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); + + free((void*)pixels); + } + + const State& s = drawingState(); + if (UNLIKELY(s.alpha < 0xFF)) { + const GGLfixed alpha = (s.alpha << 16)/255; + glColor4x(0, 0, 0, alpha); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } else { + glDisable(GL_BLEND); + } + + glDisable(GL_DITHER); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + if (UNLIKELY(transformed() + || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) { + // This is a very rare scenario. + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(1.0f/w, -1.0f/h, 1); + glTranslatef(-x, -y, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FIXED, 0, mVertices); + glTexCoordPointer(2, GL_FIXED, 0, mVertices); + Rect r; + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } else { + Region::iterator iterator(clip); + if (iterator) { + // NOTE: this is marginally faster with the software gl, because + // glReadPixels() reads the fb bottom-to-top, however we'll + // skip all the jaccobian computations. + Rect r; + GLint crop[4] = { 0, 0, w, h }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + y = fbHeight - (y + h); + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawTexiOES(x, y, 0, w, h); + } + } + } + } + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerBlur.h b/libs/surfaceflinger/LayerBlur.h new file mode 100644 index 000000000..24b115657 --- /dev/null +++ b/libs/surfaceflinger/LayerBlur.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_LAYER_BLUR_H +#define ANDROID_LAYER_BLUR_H + +#include +#include + +#include + +#include + +#include "LayerBase.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class LayerBlur : public LayerBaseClient +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerBlur(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i); + virtual ~LayerBlur(); + + virtual void onDraw(const Region& clip) const; + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + + virtual uint32_t doTransaction(uint32_t flags); + virtual void setVisibleRegion(const Region& visibleRegion); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + +private: + bool mCacheDirty; + mutable bool mRefreshCache; + mutable bool mAutoRefreshPending; + nsecs_t mCacheAge; + mutable GLuint mTextureName; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BLUR_H diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp new file mode 100644 index 000000000..00fab70d4 --- /dev/null +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -0,0 +1,655 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include "LayerBuffer.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" +#include "DisplayHardware/DisplayHardware.h" + + +namespace android { + +// --------------------------------------------------------------------------- + +const uint32_t LayerBuffer::typeInfo = LayerBaseClient::typeInfo | 0x20; +const char* const LayerBuffer::typeID = "LayerBuffer"; + +// --------------------------------------------------------------------------- + +LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i) + : LayerBaseClient(flinger, display, client, i), + mNeedsBlending(false) +{ +} + +LayerBuffer::~LayerBuffer() +{ + sp s(getClientSurface()); + if (s != 0) { + s->disown(); + mClientSurface.clear(); + } +} + +sp LayerBuffer::getClientSurface() const +{ + Mutex::Autolock _l(mLock); + return mClientSurface.promote(); +} + +sp LayerBuffer::getSurface() const +{ + sp s; + Mutex::Autolock _l(mLock); + s = mClientSurface.promote(); + if (s == 0) { + s = new SurfaceBuffer(clientIndex(), + const_cast(this)); + mClientSurface = s; + } + return s; +} + +bool LayerBuffer::needsBlending() const { + return mNeedsBlending; +} + +void LayerBuffer::setNeedsBlending(bool blending) { + mNeedsBlending = blending; +} + +void LayerBuffer::postBuffer(ssize_t offset) +{ + sp source(getSource()); + if (source != 0) + source->postBuffer(offset); +} + +void LayerBuffer::unregisterBuffers() +{ + sp source(clearSource()); + if (source != 0) + source->unregisterBuffers(); +} + +uint32_t LayerBuffer::doTransaction(uint32_t flags) +{ + sp source(getSource()); + if (source != 0) + source->onTransaction(flags); + return LayerBase::doTransaction(flags); +} + +void LayerBuffer::unlockPageFlip(const Transform& planeTransform, + Region& outDirtyRegion) +{ + // this code-path must be as tight as possible, it's called each time + // the screen is composited. + sp source(getSource()); + if (source != 0) + source->onVisibilityResolved(planeTransform); + LayerBase::unlockPageFlip(planeTransform, outDirtyRegion); +} + +void LayerBuffer::onDraw(const Region& clip) const +{ + sp source(getSource()); + if (LIKELY(source != 0)) { + source->onDraw(clip); + } else { + clearWithOpenGL(clip); + } +} + +bool LayerBuffer::transformed() const +{ + sp source(getSource()); + if (LIKELY(source != 0)) + return source->transformed(); + return false; +} + +/** + * This creates a "buffer" source for this surface + */ +status_t LayerBuffer::registerBuffers(const ISurface::BufferHeap& buffers) +{ + Mutex::Autolock _l(mLock); + if (mSource != 0) + return INVALID_OPERATION; + + sp source = new BufferSource(*this, buffers); + + status_t result = source->getStatus(); + if (result == NO_ERROR) { + mSource = source; + } + return result; +} + +/** + * This creates an "overlay" source for this surface + */ +sp LayerBuffer::createOverlay(uint32_t w, uint32_t h, int32_t f) +{ + sp result; + Mutex::Autolock _l(mLock); + if (mSource != 0) + return result; + + sp source = new OverlaySource(*this, &result, w, h, f); + if (result != 0) { + mSource = source; + } + return result; +} + +sp LayerBuffer::getSource() const { + Mutex::Autolock _l(mLock); + return mSource; +} + +sp LayerBuffer::clearSource() { + sp source; + Mutex::Autolock _l(mLock); + source = mSource; + mSource.clear(); + return source; +} + +// ============================================================================ +// LayerBuffer::SurfaceBuffer +// ============================================================================ + +LayerBuffer::SurfaceBuffer::SurfaceBuffer(SurfaceID id, LayerBuffer* owner) +: LayerBaseClient::Surface(id, owner->getIdentity()), mOwner(owner) +{ +} + +LayerBuffer::SurfaceBuffer::~SurfaceBuffer() +{ + unregisterBuffers(); + mOwner = 0; +} + +status_t LayerBuffer::SurfaceBuffer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case REGISTER_BUFFERS: + case UNREGISTER_BUFFERS: + case CREATE_OVERLAY: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int self_pid = getpid(); + if (LIKELY(pid != self_pid)) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.ACCESS_SURFACE_FLINGER"))) + { + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + } + } + return LayerBaseClient::Surface::onTransact(code, data, reply, flags); +} + +status_t LayerBuffer::SurfaceBuffer::registerBuffers(const ISurface::BufferHeap& buffers) +{ + LayerBuffer* owner(getOwner()); + if (owner) + return owner->registerBuffers(buffers); + return NO_INIT; +} + +void LayerBuffer::SurfaceBuffer::postBuffer(ssize_t offset) +{ + LayerBuffer* owner(getOwner()); + if (owner) + owner->postBuffer(offset); +} + +void LayerBuffer::SurfaceBuffer::unregisterBuffers() +{ + LayerBuffer* owner(getOwner()); + if (owner) + owner->unregisterBuffers(); +} + +sp LayerBuffer::SurfaceBuffer::createOverlay( + uint32_t w, uint32_t h, int32_t format) { + sp result; + LayerBuffer* owner(getOwner()); + if (owner) + result = owner->createOverlay(w, h, format); + return result; +} + +void LayerBuffer::SurfaceBuffer::disown() +{ + Mutex::Autolock _l(mLock); + mOwner = 0; +} + +// ============================================================================ +// LayerBuffer::Buffer +// ============================================================================ + +LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, ssize_t offset) + : mBufferHeap(buffers) +{ + NativeBuffer& src(mNativeBuffer); + src.crop.l = 0; + src.crop.t = 0; + src.crop.r = buffers.w; + src.crop.b = buffers.h; + src.img.w = buffers.hor_stride ?: buffers.w; + src.img.h = buffers.ver_stride ?: buffers.h; + src.img.format = buffers.format; + src.img.offset = offset; + src.img.base = buffers.heap->base(); + src.img.fd = buffers.heap->heapID(); +} + +LayerBuffer::Buffer::~Buffer() +{ +} + +// ============================================================================ +// LayerBuffer::Source +// LayerBuffer::BufferSource +// LayerBuffer::OverlaySource +// ============================================================================ + +LayerBuffer::Source::Source(LayerBuffer& layer) + : mLayer(layer) +{ +} +LayerBuffer::Source::~Source() { +} +void LayerBuffer::Source::onDraw(const Region& clip) const { +} +void LayerBuffer::Source::onTransaction(uint32_t flags) { +} +void LayerBuffer::Source::onVisibilityResolved( + const Transform& planeTransform) { +} +void LayerBuffer::Source::postBuffer(ssize_t offset) { +} +void LayerBuffer::Source::unregisterBuffers() { +} +bool LayerBuffer::Source::transformed() const { + return mLayer.mTransformed; +} + +// --------------------------------------------------------------------------- + +LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, + const ISurface::BufferHeap& buffers) + : Source(layer), mStatus(NO_ERROR), + mBufferSize(0), mTextureName(-1U) +{ + if (buffers.heap == NULL) { + // this is allowed, but in this case, it is illegal to receive + // postBuffer(). The surface just erases the framebuffer with + // fully transparent pixels. + mBufferHeap = buffers; + mLayer.setNeedsBlending(false); + return; + } + + status_t err = (buffers.heap->heapID() >= 0) ? NO_ERROR : NO_INIT; + if (err != NO_ERROR) { + LOGE("LayerBuffer::BufferSource: invalid heap (%s)", strerror(err)); + mStatus = err; + return; + } + + PixelFormatInfo info; + err = getPixelFormatInfo(buffers.format, &info); + if (err != NO_ERROR) { + LOGE("LayerBuffer::BufferSource: invalid format %d (%s)", + buffers.format, strerror(err)); + mStatus = err; + return; + } + + if (buffers.hor_stride<0 || buffers.ver_stride<0) { + LOGE("LayerBuffer::BufferSource: invalid parameters " + "(w=%d, h=%d, xs=%d, ys=%d)", + buffers.w, buffers.h, buffers.hor_stride, buffers.ver_stride); + mStatus = BAD_VALUE; + return; + } + + mBufferHeap = buffers; + mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0); + mBufferSize = info.getScanlineSize(buffers.hor_stride)*buffers.ver_stride; + mLayer.forceVisibilityTransaction(); + +} + +LayerBuffer::BufferSource::~BufferSource() +{ + if (mTextureName != -1U) { + LayerBase::deletedTextures.add(mTextureName); + } +} + +void LayerBuffer::BufferSource::postBuffer(ssize_t offset) +{ + ISurface::BufferHeap buffers; + { // scope for the lock + Mutex::Autolock _l(mLock); + buffers = mBufferHeap; + if (buffers.heap != 0) { + const size_t memorySize = buffers.heap->getSize(); + if ((size_t(offset) + mBufferSize) > memorySize) { + LOGE("LayerBuffer::BufferSource::postBuffer() " + "invalid buffer (offset=%d, size=%d, heap-size=%d", + int(offset), int(mBufferSize), int(memorySize)); + return; + } + } + } + + sp buffer; + if (buffers.heap != 0) { + buffer = new LayerBuffer::Buffer(buffers, offset); + if (buffer->getStatus() != NO_ERROR) + buffer.clear(); + setBuffer(buffer); + mLayer.invalidate(); + } +} + +void LayerBuffer::BufferSource::unregisterBuffers() +{ + Mutex::Autolock _l(mLock); + mBufferHeap.heap.clear(); + mBuffer.clear(); + mLayer.invalidate(); +} + +sp LayerBuffer::BufferSource::getBuffer() const +{ + Mutex::Autolock _l(mLock); + return mBuffer; +} + +void LayerBuffer::BufferSource::setBuffer(const sp& buffer) +{ + Mutex::Autolock _l(mLock); + mBuffer = buffer; +} + +bool LayerBuffer::BufferSource::transformed() const +{ + return mBufferHeap.transform ? true : Source::transformed(); +} + +void LayerBuffer::BufferSource::onDraw(const Region& clip) const +{ + sp buffer(getBuffer()); + if (UNLIKELY(buffer == 0)) { + // nothing to do, we don't have a buffer + mLayer.clearWithOpenGL(clip); + return; + } + + status_t err = NO_ERROR; + NativeBuffer src(buffer->getBuffer()); + const Rect& transformedBounds = mLayer.getTransformedBounds(); + const int can_use_copybit = mLayer.canUseCopybit(); + + if (can_use_copybit) { + const int src_width = src.crop.r - src.crop.l; + const int src_height = src.crop.b - src.crop.t; + int W = transformedBounds.width(); + int H = transformedBounds.height(); + if (mLayer.getOrientation() & Transform::ROT_90) { + int t(W); W=H; H=t; + } + + /* With LayerBuffer, it is likely that we'll have to rescale the + * surface, because this is often used for video playback or + * camera-preview. Since we want these operation as fast as possible + * we make sure we can use the 2D H/W even if it doesn't support + * the requested scale factor, in which case we perform the scaling + * in several passes. */ + + copybit_device_t* copybit = mLayer.mFlinger->getBlitEngine(); + const float min = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT); + const float mag = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT); + + float xscale = 1.0f; + if (src_width > W*min) xscale = 1.0f / min; + else if (src_width*mag < W) xscale = mag; + + float yscale = 1.0f; + if (src_height > H*min) yscale = 1.0f / min; + else if (src_height*mag < H) yscale = mag; + + if (UNLIKELY(xscale!=1.0f || yscale!=1.0f)) { + if (UNLIKELY(mTemporaryDealer == 0)) { + // allocate a memory-dealer for this the first time + mTemporaryDealer = mLayer.mFlinger->getSurfaceHeapManager() + ->createHeap(ISurfaceComposer::eHardware); + mTempBitmap.init(mTemporaryDealer); + } + + const int tmp_w = floorf(src_width * xscale); + const int tmp_h = floorf(src_height * yscale); + err = mTempBitmap.setBits(tmp_w, tmp_h, 1, src.img.format); + + if (LIKELY(err == NO_ERROR)) { + NativeBuffer tmp; + mTempBitmap.getBitmapSurface(&tmp.img); + tmp.crop.l = 0; + tmp.crop.t = 0; + tmp.crop.r = tmp.img.w; + tmp.crop.b = tmp.img.h; + + region_iterator tmp_it(Region(Rect(tmp.crop.r, tmp.crop.b))); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); + err = copybit->stretch(copybit, + &tmp.img, &src.img, &tmp.crop, &src.crop, &tmp_it); + src = tmp; + } + } + + const DisplayHardware& hw(mLayer.graphicPlane(0).displayHardware()); + copybit_image_t dst; + hw.getDisplaySurface(&dst); + const copybit_rect_t& drect + = reinterpret_cast(transformedBounds); + const State& s(mLayer.drawingState()); + region_iterator it(clip); + + // pick the right orientation for this buffer + int orientation = mLayer.getOrientation(); + if (UNLIKELY(mBufferHeap.transform)) { + Transform rot90; + GraphicPlane::orientationToTransfrom( + ISurfaceComposer::eOrientation90, 0, 0, &rot90); + const Transform& planeTransform(mLayer.graphicPlane(0).transform()); + const Layer::State& s(mLayer.drawingState()); + Transform tr(planeTransform * s.transform * rot90); + orientation = tr.getOrientation(); + } + + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, orientation); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + + err = copybit->stretch(copybit, + &dst, &src.img, &drect, &src.crop, &it); + if (err != NO_ERROR) { + LOGE("copybit failed (%s)", strerror(err)); + } + } + + if (!can_use_copybit || err) { + if (UNLIKELY(mTextureName == -1LU)) { + mTextureName = mLayer.createTexture(); + } + GLuint w = 0; + GLuint h = 0; + GGLSurface t; + t.version = sizeof(GGLSurface); + t.width = src.crop.r; + t.height = src.crop.b; + t.stride = src.img.w; + t.vstride= src.img.h; + t.format = src.img.format; + t.data = (GGLubyte*)(intptr_t(src.img.base) + src.img.offset); + const Region dirty(Rect(t.width, t.height)); + mLayer.loadTexture(dirty, mTextureName, t, w, h); + mLayer.drawWithOpenGL(clip, mTextureName, t, mBufferHeap.transform); + } +} + + +// --------------------------------------------------------------------------- + +LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, + sp* overlayRef, + uint32_t w, uint32_t h, int32_t format) + : Source(layer), mVisibilityChanged(false), + mOverlay(0), mOverlayHandle(0), mOverlayDevice(0) +{ + overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine(); + if (overlay_dev == NULL) { + // overlays not supported + return; + } + + mOverlayDevice = overlay_dev; + overlay_t* overlay = overlay_dev->createOverlay(overlay_dev, w, h, format); + if (overlay == NULL) { + // couldn't create the overlay (no memory? no more overlays?) + return; + } + + // enable dithering... + overlay_dev->setParameter(overlay_dev, overlay, + OVERLAY_DITHER, OVERLAY_ENABLE); + + mOverlay = overlay; + mWidth = overlay->w; + mHeight = overlay->h; + mFormat = overlay->format; + mWidthStride = overlay->w_stride; + mHeightStride = overlay->h_stride; + + mOverlayHandle = overlay->getHandleRef(overlay); + + // NOTE: here it's okay to acquire a reference to "this"m as long as + // the reference is not released before we leave the ctor. + sp channel = new OverlayChannel(this); + + *overlayRef = new OverlayRef(mOverlayHandle, channel, + mWidth, mHeight, mFormat, mWidthStride, mHeightStride); +} + +LayerBuffer::OverlaySource::~OverlaySource() +{ + if (mOverlay && mOverlayDevice) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->destroyOverlay(overlay_dev, mOverlay); + } +} + +void LayerBuffer::OverlaySource::onTransaction(uint32_t flags) +{ + const Layer::State& front(mLayer.drawingState()); + const Layer::State& temp(mLayer.currentState()); + if (temp.sequence != front.sequence) { + mVisibilityChanged = true; + } +} + +void LayerBuffer::OverlaySource::onVisibilityResolved( + const Transform& planeTransform) +{ + // this code-path must be as tight as possible, it's called each time + // the screen is composited. + if (UNLIKELY(mOverlay != 0)) { + if (mVisibilityChanged) { + mVisibilityChanged = false; + const Rect& bounds = mLayer.getTransformedBounds(); + int x = bounds.left; + int y = bounds.top; + int w = bounds.width(); + int h = bounds.height(); + + // we need a lock here to protect "destroy" + Mutex::Autolock _l(mLock); + if (mOverlay) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h); + overlay_dev->setParameter(overlay_dev, mOverlay, + OVERLAY_TRANSFORM, mLayer.getOrientation()); + } + } + } +} + +void LayerBuffer::OverlaySource::serverDestroy() +{ + mLayer.clearSource(); + destroyOverlay(); +} + +void LayerBuffer::OverlaySource::destroyOverlay() +{ + // we need a lock here to protect "onVisibilityResolved" + Mutex::Autolock _l(mLock); + if (mOverlay) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->destroyOverlay(overlay_dev, mOverlay); + mOverlay = 0; + } +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h new file mode 100644 index 000000000..2dc77f150 --- /dev/null +++ b/libs/surfaceflinger/LayerBuffer.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_LAYER_BUFFER_H +#define ANDROID_LAYER_BUFFER_H + +#include +#include + +#include +#include +#include + +#include "LayerBase.h" +#include "LayerBitmap.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryDealer; +class Region; +class OverlayRef; + +class LayerBuffer : public LayerBaseClient +{ + class Source : public LightRefBase { + public: + Source(LayerBuffer& layer); + virtual ~Source(); + virtual void onDraw(const Region& clip) const; + virtual void onTransaction(uint32_t flags); + virtual void onVisibilityResolved(const Transform& planeTransform); + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual bool transformed() const; + protected: + LayerBuffer& mLayer; + }; + + +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerBuffer(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i); + virtual ~LayerBuffer(); + + virtual bool needsBlending() const; + + virtual sp getSurface() const; + virtual void onDraw(const Region& clip) const; + virtual uint32_t doTransaction(uint32_t flags); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + virtual bool transformed() const; + + status_t registerBuffers(const ISurface::BufferHeap& buffers); + void postBuffer(ssize_t offset); + void unregisterBuffers(); + sp createOverlay(uint32_t w, uint32_t h, int32_t format); + + sp getSource() const; + sp clearSource(); + void setNeedsBlending(bool blending); + const Rect& getTransformedBounds() const { + return mTransformedBounds; + } + +private: + struct NativeBuffer { + copybit_image_t img; + copybit_rect_t crop; + }; + + class Buffer : public LightRefBase { + public: + Buffer(const ISurface::BufferHeap& buffers, ssize_t offset); + inline status_t getStatus() const { + return mBufferHeap.heap!=0 ? NO_ERROR : NO_INIT; + } + inline const NativeBuffer& getBuffer() const { + return mNativeBuffer; + } + protected: + friend class LightRefBase; + Buffer& operator = (const Buffer& rhs); + Buffer(const Buffer& rhs); + ~Buffer(); + private: + ISurface::BufferHeap mBufferHeap; + NativeBuffer mNativeBuffer; + }; + + class BufferSource : public Source { + public: + BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers); + virtual ~BufferSource(); + + status_t getStatus() const { return mStatus; } + sp getBuffer() const; + void setBuffer(const sp& buffer); + + virtual void onDraw(const Region& clip) const; + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual bool transformed() const; + private: + mutable Mutex mLock; + sp mBuffer; + status_t mStatus; + ISurface::BufferHeap mBufferHeap; + size_t mBufferSize; + mutable sp mTemporaryDealer; + mutable LayerBitmap mTempBitmap; + mutable GLuint mTextureName; + }; + + class OverlaySource : public Source { + public: + OverlaySource(LayerBuffer& layer, + sp* overlayRef, + uint32_t w, uint32_t h, int32_t format); + virtual ~OverlaySource(); + virtual void onTransaction(uint32_t flags); + virtual void onVisibilityResolved(const Transform& planeTransform); + private: + void serverDestroy(); + void destroyOverlay(); + class OverlayChannel : public BnOverlay { + mutable Mutex mLock; + sp mSource; + virtual void destroy() { + sp source; + { // scope for the lock; + Mutex::Autolock _l(mLock); + source = mSource; + mSource.clear(); + } + if (source != 0) { + source->serverDestroy(); + } + } + public: + OverlayChannel(const sp& source) + : mSource(source) { + } + }; + friend class OverlayChannel; + bool mVisibilityChanged; + + overlay_t* mOverlay; + overlay_handle_t mOverlayHandle; + overlay_control_device_t* mOverlayDevice; + uint32_t mWidth; + uint32_t mHeight; + int32_t mFormat; + int32_t mWidthStride; + int32_t mHeightStride; + mutable Mutex mLock; + }; + + + class SurfaceBuffer : public LayerBaseClient::Surface + { + public: + SurfaceBuffer(SurfaceID id, LayerBuffer* owner); + virtual ~SurfaceBuffer(); + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual sp createOverlay( + uint32_t w, uint32_t h, int32_t format); + void disown(); + private: + LayerBuffer* getOwner() const { + Mutex::Autolock _l(mLock); + return mOwner; + } + mutable Mutex mLock; + LayerBuffer* mOwner; + }; + + friend class SurfaceFlinger; + sp getClientSurface() const; + + mutable Mutex mLock; + sp mSource; + + bool mInvalidate; + bool mNeedsBlending; + mutable wp mClientSurface; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BUFFER_H diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp new file mode 100644 index 000000000..0c347cc51 --- /dev/null +++ b/libs/surfaceflinger/LayerDim.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include + +#include +#include + +#include "LayerDim.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10; +const char* const LayerDim::typeID = "LayerDim"; +sp LayerDim::mDimmerDealer; +LayerBitmap LayerDim::mDimmerBitmap; + +// --------------------------------------------------------------------------- + +LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i) + : LayerBaseClient(flinger, display, client, i) +{ +} + +void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) +{ + // must only be called once. + mDimmerDealer = flinger->getSurfaceHeapManager() + ->createHeap(ISurfaceComposer::eHardware); + if (mDimmerDealer != 0) { + mDimmerBitmap.init(mDimmerDealer); + mDimmerBitmap.setBits(w, h, 1, PIXEL_FORMAT_RGB_565); + mDimmerBitmap.clear(); + } +} + +LayerDim::~LayerDim() +{ +} + +void LayerDim::onDraw(const Region& clip) const +{ + const State& s(drawingState()); + + Region::iterator iterator(clip); + if (s.alpha>0 && iterator) { + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + + status_t err = NO_ERROR; + const int can_use_copybit = canUseCopybit(); + if (can_use_copybit) { + // StopWatch watch("copybit"); + copybit_image_t dst; + hw.getDisplaySurface(&dst); + const copybit_rect_t& drect + = reinterpret_cast(mTransformedBounds); + + copybit_image_t src; + mDimmerBitmap.getBitmapSurface(&src); + const copybit_rect_t& srect(drect); + + copybit_device_t* copybit = mFlinger->getBlitEngine(); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + region_iterator it(clip); + err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); + } + + if (!can_use_copybit || err) { + const GGLfixed alpha = (s.alpha << 16)/255; + const uint32_t fbHeight = hw.getHeight(); + glDisable(GL_TEXTURE_2D); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4x(0, 0, 0, alpha); + glVertexPointer(2, GL_FIXED, 0, mVertices); + Rect r; + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerDim.h b/libs/surfaceflinger/LayerDim.h new file mode 100644 index 000000000..3e37a4760 --- /dev/null +++ b/libs/surfaceflinger/LayerDim.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_LAYER_DIM_H +#define ANDROID_LAYER_DIM_H + +#include +#include + +#include "LayerBase.h" +#include "LayerBitmap.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class LayerDim : public LayerBaseClient +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerDim(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i); + virtual ~LayerDim(); + + virtual void onDraw(const Region& clip) const; + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + + static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h); + +private: + static sp mDimmerDealer; + static LayerBitmap mDimmerBitmap; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_DIM_H diff --git a/libs/surfaceflinger/LayerOrientationAnim.cpp b/libs/surfaceflinger/LayerOrientationAnim.cpp new file mode 100644 index 000000000..2b72d7ce9 --- /dev/null +++ b/libs/surfaceflinger/LayerOrientationAnim.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include + +#include +#include + +#include + +#include + +#include "LayerBase.h" +#include "LayerOrientationAnim.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" +#include "OrientationAnimation.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerOrientationAnim::typeInfo = LayerBase::typeInfo | 0x80; +const char* const LayerOrientationAnim::typeID = "LayerOrientationAnim"; + +// --------------------------------------------------------------------------- + +LayerOrientationAnim::LayerOrientationAnim( + SurfaceFlinger* flinger, DisplayID display, + OrientationAnimation* anim, + const LayerBitmap& bitmap, + const LayerBitmap& bitmapIn) + : LayerBase(flinger, display), mAnim(anim), + mBitmap(bitmap), mBitmapIn(bitmapIn), + mTextureName(-1), mTextureNameIn(-1) +{ + mStartTime = systemTime(); + mFinishTime = 0; + mOrientationCompleted = false; + mFirstRedraw = false; + mLastNormalizedTime = 0; + mLastScale = 0; + mNeedsBlending = false; +} + +LayerOrientationAnim::~LayerOrientationAnim() +{ + if (mTextureName != -1U) { + LayerBase::deletedTextures.add(mTextureName); + } + if (mTextureNameIn != -1U) { + LayerBase::deletedTextures.add(mTextureNameIn); + } +} + +bool LayerOrientationAnim::needsBlending() const +{ + return mNeedsBlending; +} + +Point LayerOrientationAnim::getPhysicalSize() const +{ + const GraphicPlane& plane(graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + return Point(hw.getWidth(), hw.getHeight()); +} + +void LayerOrientationAnim::validateVisibility(const Transform&) +{ + const Layer::State& s(drawingState()); + const Transform tr(s.transform); + const Point size(getPhysicalSize()); + uint32_t w = size.x; + uint32_t h = size.y; + mTransformedBounds = tr.makeBounds(w, h); + mLeft = tr.tx(); + mTop = tr.ty(); + transparentRegionScreen.clear(); + mTransformed = true; + mCanUseCopyBit = false; + copybit_device_t* copybit = mFlinger->getBlitEngine(); + if (copybit) { + mCanUseCopyBit = true; + } +} + +void LayerOrientationAnim::onOrientationCompleted() +{ + mFinishTime = systemTime(); + mOrientationCompleted = true; + mFirstRedraw = true; + mNeedsBlending = true; + mFlinger->invalidateLayerVisibility(this); +} + +void LayerOrientationAnim::onDraw(const Region& clip) const +{ + // Animation... + const float MIN_SCALE = 0.5f; + const float DURATION = ms2ns(200); + const float BOUNCES_PER_SECOND = 1.618f; + const float BOUNCES_AMPLITUDE = 1.0f/32.0f; + + const nsecs_t now = systemTime(); + float scale, alpha; + + if (mOrientationCompleted) { + if (mFirstRedraw) { + mFirstRedraw = false; + + // make a copy of what's on screen + copybit_image_t image; + mBitmapIn.getBitmapSurface(&image); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + hw.copyBackToImage(image); + + // and erase the screen for this round + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + + // FIXME: code below is gross + mNeedsBlending = false; + LayerOrientationAnim* self(const_cast(this)); + mFlinger->invalidateLayerVisibility(self); + } + + // make sure pick-up where we left off + const float duration = DURATION * mLastNormalizedTime; + const float normalizedTime = (float(now - mFinishTime) / duration); + if (normalizedTime <= 1.0f) { + const float squaredTime = normalizedTime*normalizedTime; + scale = (1.0f - mLastScale)*squaredTime + mLastScale; + alpha = (1.0f - normalizedTime); + alpha *= alpha; + alpha *= alpha; + } else { + mAnim->onAnimationFinished(); + scale = 1.0f; + alpha = 0.0f; + } + } else { + const float normalizedTime = float(now - mStartTime) / DURATION; + if (normalizedTime <= 1.0f) { + mLastNormalizedTime = normalizedTime; + const float squaredTime = normalizedTime*normalizedTime; + scale = (MIN_SCALE-1.0f)*squaredTime + 1.0f; + alpha = 1.0f; + } else { + mLastNormalizedTime = 1.0f; + const float to_seconds = DURATION / seconds(1); + const float phi = BOUNCES_PER_SECOND * + (((normalizedTime - 1.0f) * to_seconds)*M_PI*2); + scale = MIN_SCALE + BOUNCES_AMPLITUDE * (1.0f - cosf(phi)); + alpha = 1.0f; + } + mLastScale = scale; + } + drawScaled(scale, alpha); +} + +void LayerOrientationAnim::drawScaled(float f, float alpha) const +{ + copybit_image_t dst; + const GraphicPlane& plane(graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + hw.getDisplaySurface(&dst); + + // clear screen + // TODO: with update on demand, we may be able + // to not erase the screen at all during the animation + if (!mOrientationCompleted) { + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + } + + const int w = dst.w*f; + const int h = dst.h*f; + const int xc = uint32_t(dst.w-w)/2; + const int yc = uint32_t(dst.h-h)/2; + const copybit_rect_t drect = { xc, yc, xc+w, yc+h }; + + copybit_image_t src; + mBitmap.getBitmapSurface(&src); + const copybit_rect_t srect = { 0, 0, src.w, src.h }; + + int err = NO_ERROR; + const int can_use_copybit = canUseCopybit(); + if (can_use_copybit) { + copybit_device_t* copybit = mFlinger->getBlitEngine(); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + + if (alpha < 1.0f) { + copybit_image_t srcIn; + mBitmapIn.getBitmapSurface(&srcIn); + region_iterator it(Region(Rect( drect.l, drect.t, drect.r, drect.b ))); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + err = copybit->stretch(copybit, &dst, &srcIn, &drect, &srect, &it); + } + + if (!err && alpha > 0.0f) { + region_iterator it(Region(Rect( drect.l, drect.t, drect.r, drect.b ))); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, int(alpha*255)); + err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); + } + LOGE_IF(err != NO_ERROR, "copybit failed (%s)", strerror(err)); + } + if (!can_use_copybit || err) { + GGLSurface t; + t.version = sizeof(GGLSurface); + t.width = src.w; + t.height = src.h; + t.stride = src.w; + t.vstride= src.h; + t.format = src.format; + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + + Transform tr; + tr.set(f,0,0,f); + tr.set(xc, yc); + + // FIXME: we should not access mVertices and mDrawingState like that, + // but since we control the animation, we know it's going to work okay. + // eventually we'd need a more formal way of doing things like this. + LayerOrientationAnim& self(const_cast(*this)); + tr.transform(self.mVertices[0], 0, 0); + tr.transform(self.mVertices[1], 0, src.h); + tr.transform(self.mVertices[2], src.w, src.h); + tr.transform(self.mVertices[3], src.w, 0); + if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { + // Too slow to do this in software + self.mDrawingState.flags |= ISurfaceComposer::eLayerFilter; + } + + if (alpha < 1.0f) { + copybit_image_t src; + mBitmapIn.getBitmapSurface(&src); + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + if (UNLIKELY(mTextureNameIn == -1LU)) { + mTextureNameIn = createTexture(); + GLuint w=0, h=0; + const Region dirty(Rect(t.width, t.height)); + loadTexture(dirty, mTextureNameIn, t, w, h); + } + self.mDrawingState.alpha = 255; + const Region clip(Rect( drect.l, drect.t, drect.r, drect.b )); + drawWithOpenGL(clip, mTextureName, t); + } + + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + if (UNLIKELY(mTextureName == -1LU)) { + mTextureName = createTexture(); + GLuint w=0, h=0; + const Region dirty(Rect(t.width, t.height)); + loadTexture(dirty, mTextureName, t, w, h); + } + self.mDrawingState.alpha = int(alpha*255); + const Region clip(Rect( drect.l, drect.t, drect.r, drect.b )); + drawWithOpenGL(clip, mTextureName, t); + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerOrientationAnim.h b/libs/surfaceflinger/LayerOrientationAnim.h new file mode 100644 index 000000000..73676859b --- /dev/null +++ b/libs/surfaceflinger/LayerOrientationAnim.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_LAYER_ORIENTATION_ANIM_H +#define ANDROID_LAYER_ORIENTATION_ANIM_H + +#include +#include +#include +#include + +#include "LayerBase.h" +#include "LayerBitmap.h" + +namespace android { + +// --------------------------------------------------------------------------- +class OrientationAnimation; + +class LayerOrientationAnim : public LayerBase +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerOrientationAnim(SurfaceFlinger* flinger, DisplayID display, + OrientationAnimation* anim, + const LayerBitmap& zoomOut, + const LayerBitmap& zoomIn); + virtual ~LayerOrientationAnim(); + + void onOrientationCompleted(); + + virtual void onDraw(const Region& clip) const; + virtual Point getPhysicalSize() const; + virtual void validateVisibility(const Transform& globalTransform); + virtual bool needsBlending() const; + virtual bool isSecure() const { return false; } +private: + void drawScaled(float scale, float alpha) const; + + OrientationAnimation* mAnim; + LayerBitmap mBitmap; + LayerBitmap mBitmapIn; + nsecs_t mStartTime; + nsecs_t mFinishTime; + bool mOrientationCompleted; + mutable bool mFirstRedraw; + mutable float mLastNormalizedTime; + mutable float mLastScale; + mutable GLuint mTextureName; + mutable GLuint mTextureNameIn; + mutable bool mNeedsBlending; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_ORIENTATION_ANIM_H diff --git a/libs/surfaceflinger/MODULE_LICENSE_APACHE2 b/libs/surfaceflinger/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/libs/surfaceflinger/OrientationAnimation.cpp b/libs/surfaceflinger/OrientationAnimation.cpp new file mode 100644 index 000000000..f6f1326b1 --- /dev/null +++ b/libs/surfaceflinger/OrientationAnimation.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include + +#include "LayerOrientationAnim.h" +#include "OrientationAnimation.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" + +#include "DisplayHardware/DisplayHardware.h" + +namespace android { + +// --------------------------------------------------------------------------- + +OrientationAnimation::OrientationAnimation(const sp& flinger) + : mFlinger(flinger), mLayerOrientationAnim(NULL), mState(DONE) +{ + // allocate a memory-dealer for this the first time + mTemporaryDealer = mFlinger->getSurfaceHeapManager()->createHeap( + ISurfaceComposer::eHardware); +} + +OrientationAnimation::~OrientationAnimation() +{ +} + +void OrientationAnimation::onOrientationChanged() +{ + if (mState == DONE) + mState = PREPARE; +} + +void OrientationAnimation::onAnimationFinished() +{ + if (mState != DONE) + mState = FINISH; +} + +bool OrientationAnimation::run_impl() +{ + bool skip_frame; + switch (mState) { + default: + case DONE: + skip_frame = done(); + break; + case PREPARE: + skip_frame = prepare(); + break; + case PHASE1: + skip_frame = phase1(); + break; + case PHASE2: + skip_frame = phase2(); + break; + case FINISH: + skip_frame = finished(); + break; + } + return skip_frame; +} + +bool OrientationAnimation::done() +{ + if (mFlinger->isFrozen()) { + // we are not allowed to draw, but pause a bit to make sure + // apps don't end up using the whole CPU, if they depend on + // surfaceflinger for synchronization. + usleep(8333); // 8.3ms ~ 120fps + return true; + } + return false; +} + +bool OrientationAnimation::prepare() +{ + mState = PHASE1; + + const GraphicPlane& plane(mFlinger->graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + const uint32_t w = hw.getWidth(); + const uint32_t h = hw.getHeight(); + + LayerBitmap bitmap; + bitmap.init(mTemporaryDealer); + bitmap.setBits(w, h, 1, hw.getFormat()); + + LayerBitmap bitmapIn; + bitmapIn.init(mTemporaryDealer); + bitmapIn.setBits(w, h, 1, hw.getFormat()); + + copybit_image_t front; + bitmap.getBitmapSurface(&front); + hw.copyFrontToImage(front); + + LayerOrientationAnim* l = new LayerOrientationAnim( + mFlinger.get(), 0, this, bitmap, bitmapIn); + l->initStates(w, h, 0); + l->setLayer(INT_MAX-1); + mFlinger->addLayer(l); + mLayerOrientationAnim = l; + return true; +} + +bool OrientationAnimation::phase1() +{ + if (mFlinger->isFrozen() == false) { + // start phase 2 + mState = PHASE2; + mLayerOrientationAnim->onOrientationCompleted(); + mLayerOrientationAnim->invalidate(); + return true; + + } + mLayerOrientationAnim->invalidate(); + return false; +} + +bool OrientationAnimation::phase2() +{ + // do the 2nd phase of the animation + mLayerOrientationAnim->invalidate(); + return false; +} + +bool OrientationAnimation::finished() +{ + mState = DONE; + mFlinger->removeLayer(mLayerOrientationAnim); + mLayerOrientationAnim = NULL; + return true; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/OrientationAnimation.h b/libs/surfaceflinger/OrientationAnimation.h new file mode 100644 index 000000000..ba33fcedd --- /dev/null +++ b/libs/surfaceflinger/OrientationAnimation.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_ORIENTATION_ANIMATION_H +#define ANDROID_ORIENTATION_ANIMATION_H + +#include +#include + +#include "SurfaceFlinger.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class SurfaceFlinger; +class MemoryDealer; +class LayerOrientationAnim; + +class OrientationAnimation +{ +public: + OrientationAnimation(const sp& flinger); + virtual ~OrientationAnimation(); + + void onOrientationChanged(); + void onAnimationFinished(); + inline bool run() { + if (LIKELY(mState == DONE)) + return false; + return run_impl(); + } + +private: + enum { + DONE = 0, + PREPARE, + PHASE1, + PHASE2, + FINISH + }; + + bool run_impl(); + bool done(); + bool prepare(); + bool phase1(); + bool phase2(); + bool finished(); + + sp mFlinger; + sp mTemporaryDealer; + LayerOrientationAnim* mLayerOrientationAnim; + int mState; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ORIENTATION_ANIMATION_H diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp new file mode 100644 index 000000000..900282a27 --- /dev/null +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -0,0 +1,1840 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "clz.h" +#include "CPUGauge.h" +#include "Layer.h" +#include "LayerBlur.h" +#include "LayerBuffer.h" +#include "LayerDim.h" +#include "LayerBitmap.h" +#include "LayerOrientationAnim.h" +#include "OrientationAnimation.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" + +#include "DisplayHardware/DisplayHardware.h" +#include "GPUHardware/GPUHardware.h" + + +#define DISPLAY_COUNT 1 + +namespace android { + +// --------------------------------------------------------------------------- + +void SurfaceFlinger::instantiate() { + defaultServiceManager()->addService( + String16("SurfaceFlinger"), new SurfaceFlinger()); +} + +void SurfaceFlinger::shutdown() { + // we should unregister here, but not really because + // when (if) the service manager goes away, all the services + // it has a reference to will leave too. +} + +// --------------------------------------------------------------------------- + +SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs) + : lookup(rhs.lookup), layers(rhs.layers) +{ +} + +ssize_t SurfaceFlinger::LayerVector::indexOf( + LayerBase* key, size_t guess) const +{ + if (guess=0) { + const size_t idx = lookup.valueAt(i); + LOG_ASSERT(layers[idx]==key, + "LayerVector[%p]: layers[%d]=%p, key=%p", + this, int(idx), layers[idx], key); + return idx; + } + return i; +} + +ssize_t SurfaceFlinger::LayerVector::add( + LayerBase* layer, + Vector::compar_t cmp) +{ + size_t count = layers.size(); + ssize_t l = 0; + ssize_t h = count-1; + ssize_t mid; + LayerBase* const* a = layers.array(); + while (l <= h) { + mid = l + (h - l)/2; + const int c = cmp(a+mid, &layer); + if (c == 0) { l = mid; break; } + else if (c<0) { l = mid+1; } + else { h = mid-1; } + } + size_t order = l; + while (order= order) { + lookup.editValueAt(i)++; + } + } + layers.insertAt(layer, order); + lookup.add(layer, order); + return order; +} + +ssize_t SurfaceFlinger::LayerVector::remove(LayerBase* layer) +{ + const ssize_t keyIndex = lookup.indexOfKey(layer); + if (keyIndex >= 0) { + const size_t index = lookup.valueAt(keyIndex); + LOG_ASSERT(layers[index]==layer, + "LayerVector[%p]: layers[%u]=%p, layer=%p", + this, int(index), layers[index], layer); + layers.removeItemsAt(index); + lookup.removeItemsAt(keyIndex); + const size_t count = lookup.size(); + for (size_t i=0 ; i= size_t(index)) { + lookup.editValueAt(i)--; + } + } + return index; + } + return NAME_NOT_FOUND; +} + +ssize_t SurfaceFlinger::LayerVector::reorder( + LayerBase* layer, + Vector::compar_t cmp) +{ + // XXX: it's a little lame. but oh well... + ssize_t err = remove(layer); + if (err >=0) + err = add(layer, cmp); + return err; +} + +// --------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +SurfaceFlinger::SurfaceFlinger() + : BnSurfaceComposer(), Thread(false), + mTransactionFlags(0), + mTransactionCount(0), + mBootTime(systemTime()), + mLastScheduledBroadcast(NULL), + mVisibleRegionsDirty(false), + mDeferReleaseConsole(false), + mFreezeDisplay(false), + mFreezeCount(0), + mDebugRegion(0), + mDebugCpu(0), + mDebugFps(0), + mDebugBackground(0), + mDebugNoBootAnimation(0), + mSyncObject(), + mDeplayedTransactionPending(0), + mConsoleSignals(0), + mSecureFrameBuffer(0) +{ + init(); +} + +void SurfaceFlinger::init() +{ + LOGI("SurfaceFlinger is starting"); + + // debugging stuff... + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.showupdates", value, "0"); + mDebugRegion = atoi(value); + property_get("debug.sf.showcpu", value, "0"); + mDebugCpu = atoi(value); + property_get("debug.sf.showbackground", value, "0"); + mDebugBackground = atoi(value); + property_get("debug.sf.showfps", value, "0"); + mDebugFps = atoi(value); + property_get("debug.sf.nobootanimation", value, "0"); + mDebugNoBootAnimation = atoi(value); + + LOGI_IF(mDebugRegion, "showupdates enabled"); + LOGI_IF(mDebugCpu, "showcpu enabled"); + LOGI_IF(mDebugBackground, "showbackground enabled"); + LOGI_IF(mDebugFps, "showfps enabled"); + LOGI_IF(mDebugNoBootAnimation, "boot animation disabled"); +} + +SurfaceFlinger::~SurfaceFlinger() +{ + glDeleteTextures(1, &mWormholeTexName); + delete mOrientationAnimation; +} + +copybit_device_t* SurfaceFlinger::getBlitEngine() const +{ + return graphicPlane(0).displayHardware().getBlitEngine(); +} + +overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const +{ + return graphicPlane(0).displayHardware().getOverlayEngine(); +} + +sp SurfaceFlinger::getCblk() const +{ + return mServerCblkMemory; +} + +status_t SurfaceFlinger::requestGPU(const sp& callback, + gpu_info_t* gpu) +{ + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + status_t err = mGPU->request(pid, callback, gpu); + return err; +} + +status_t SurfaceFlinger::revokeGPU() +{ + return mGPU->friendlyRevoke(); +} + +sp SurfaceFlinger::createConnection() +{ + Mutex::Autolock _l(mStateLock); + uint32_t token = mTokens.acquire(); + + Client* client = new Client(token, this); + if ((client == 0) || (client->ctrlblk == 0)) { + mTokens.release(token); + return 0; + } + status_t err = mClientsMap.add(token, client); + if (err < 0) { + delete client; + mTokens.release(token); + return 0; + } + sp bclient = + new BClient(this, token, client->controlBlockMemory()); + return bclient; +} + +void SurfaceFlinger::destroyConnection(ClientID cid) +{ + Mutex::Autolock _l(mStateLock); + Client* const client = mClientsMap.valueFor(cid); + if (client) { + // free all the layers this client owns + const Vector& layers = client->getLayers(); + const size_t count = layers.size(); + for (size_t i=0 ; iClient mapping. + mClientsMap.removeItem(cid); + + // and add it to the list of disconnected clients + mDisconnectedClients.add(client); + + // request a transaction + setTransactionFlags(eTransactionNeeded); + } +} + +const GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) const +{ + LOGE_IF(uint32_t(dpy) >= DISPLAY_COUNT, "Invalid DisplayID %d", dpy); + const GraphicPlane& plane(mGraphicPlanes[dpy]); + return plane; +} + +GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) +{ + return const_cast( + const_cast(this)->graphicPlane(dpy)); +} + +void SurfaceFlinger::bootFinished() +{ + const nsecs_t now = systemTime(); + const nsecs_t duration = now - mBootTime; + LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); + if (mBootAnimation != 0) { + mBootAnimation->requestExit(); + mBootAnimation.clear(); + } +} + +void SurfaceFlinger::onFirstRef() +{ + run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY); + + // Wait for the main thread to be done with its initialization + mReadyToRunBarrier.wait(); +} + + +static inline uint16_t pack565(int r, int g, int b) { + return (r<<11)|(g<<5)|b; +} + +// this is defined in libGLES_CM.so +extern ISurfaceComposer* GLES_localSurfaceManager; + +status_t SurfaceFlinger::readyToRun() +{ + LOGI( "SurfaceFlinger's main thread ready to run. " + "Initializing graphics H/W..."); + + // create the shared control-block + mServerHeap = new MemoryDealer(4096, MemoryDealer::READ_ONLY); + LOGE_IF(mServerHeap==0, "can't create shared memory dealer"); + + mServerCblkMemory = mServerHeap->allocate(4096); + LOGE_IF(mServerCblkMemory==0, "can't create shared control block"); + + mServerCblk = static_cast(mServerCblkMemory->pointer()); + LOGE_IF(mServerCblk==0, "can't get to shared control block's address"); + new(mServerCblk) surface_flinger_cblk_t; + + // get a reference to the GPU if we have one + mGPU = GPUFactory::getGPU(); + + // create the surface Heap manager, which manages the heaps + // (be it in RAM or VRAM) where surfaces are allocated + // We give 8 MB per client. + mSurfaceHeapManager = new SurfaceHeapManager(this, 8 << 20); + + + GLES_localSurfaceManager = static_cast(this); + + // we only support one display currently + int dpy = 0; + + { + // initialize the main display + GraphicPlane& plane(graphicPlane(dpy)); + DisplayHardware* const hw = new DisplayHardware(this, dpy); + plane.setDisplayHardware(hw); + } + + // initialize primary screen + // (other display should be initialized in the same manner, but + // asynchronously, as they could come and go. None of this is supported + // yet). + const GraphicPlane& plane(graphicPlane(dpy)); + const DisplayHardware& hw = plane.displayHardware(); + const uint32_t w = hw.getWidth(); + const uint32_t h = hw.getHeight(); + const uint32_t f = hw.getFormat(); + hw.makeCurrent(); + + // initialize the shared control block + mServerCblk->connected |= 1<displays + dpy; + memset(dcblk, 0, sizeof(display_cblk_t)); + dcblk->w = w; + dcblk->h = h; + dcblk->format = f; + dcblk->orientation = ISurfaceComposer::eOrientationDefault; + dcblk->xdpi = hw.getDpiX(); + dcblk->ydpi = hw.getDpiY(); + dcblk->fps = hw.getRefreshRate(); + dcblk->density = hw.getDensity(); + asm volatile ("":::"memory"); + + // Initialize OpenGL|ES + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glEnableClientState(GL_VERTEX_ARRAY); + glEnable(GL_SCISSOR_TEST); + glShadeModel(GL_FLAT); + glDisable(GL_DITHER); + glDisable(GL_CULL_FACE); + + const uint16_t g0 = pack565(0x0F,0x1F,0x0F); + const uint16_t g1 = pack565(0x17,0x2f,0x17); + const uint16_t textureData[4] = { g0, g1, g1, g0 }; + glGenTextures(1, &mWormholeTexName); + glBindTexture(GL_TEXTURE_2D, mWormholeTexName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, textureData); + + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthof(0, w, h, 0, 0, 1); + + LayerDim::initDimmer(this, w, h); + + mReadyToRunBarrier.open(); + + /* + * We're now ready to accept clients... + */ + + mOrientationAnimation = new OrientationAnimation(this); + + // start CPU gauge display + if (mDebugCpu) + mCpuGauge = new CPUGauge(this, ms2ns(500)); + + // the boot animation! + if (mDebugNoBootAnimation == false) + mBootAnimation = new BootAnimation(this); + + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Events Handler +#endif + +void SurfaceFlinger::waitForEvent() +{ + // wait for something to do + if (UNLIKELY(isFrozen())) { + // wait 5 seconds + int err = mSyncObject.wait(ms2ns(5000)); + if (err != NO_ERROR) { + if (isFrozen()) { + // we timed out and are still frozen + LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d", + mFreezeDisplay, mFreezeCount); + mFreezeCount = 0; + } + } + } else { + mSyncObject.wait(); + } +} + +void SurfaceFlinger::signalEvent() { + mSyncObject.open(); +} + +void SurfaceFlinger::signal() const { + mSyncObject.open(); +} + +void SurfaceFlinger::signalDelayedEvent(nsecs_t delay) +{ + if (android_atomic_or(1, &mDeplayedTransactionPending) == 0) { + sp delayedEvent(new DelayedTransaction(this, delay)); + delayedEvent->run("DelayedeEvent", PRIORITY_URGENT_DISPLAY); + } +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Main loop +#endif + +bool SurfaceFlinger::threadLoop() +{ + waitForEvent(); + + // check for transactions + if (UNLIKELY(mConsoleSignals)) { + handleConsoleEvents(); + } + + if (LIKELY(mTransactionCount == 0)) { + // if we're in a global transaction, don't do anything. + const uint32_t mask = eTransactionNeeded | eTraversalNeeded; + uint32_t transactionFlags = getTransactionFlags(mask); + if (LIKELY(transactionFlags)) { + handleTransaction(transactionFlags); + } + } + + // post surfaces (if needed) + handlePageFlip(); + + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + if (LIKELY(hw.canDraw())) { + // repaint the framebuffer (if needed) + handleRepaint(); + + // release the clients before we flip ('cause flip might block) + unlockClients(); + executeScheduledBroadcasts(); + + // sample the cpu gauge + if (UNLIKELY(mDebugCpu)) { + handleDebugCpu(); + } + + postFramebuffer(); + } else { + // pretend we did the post + unlockClients(); + executeScheduledBroadcasts(); + usleep(16667); // 60 fps period + } + return true; +} + +void SurfaceFlinger::postFramebuffer() +{ + const bool skip = mOrientationAnimation->run(); + if (UNLIKELY(skip)) { + return; + } + + if (!mInvalidRegion.isEmpty()) { + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + + if (UNLIKELY(mDebugFps)) { + debugShowFPS(); + } + + hw.flip(mInvalidRegion); + + mInvalidRegion.clear(); + + if (Layer::deletedTextures.size()) { + glDeleteTextures( + Layer::deletedTextures.size(), + Layer::deletedTextures.array()); + Layer::deletedTextures.clear(); + } + } +} + +void SurfaceFlinger::handleConsoleEvents() +{ + // something to do with the console + const DisplayHardware& hw = graphicPlane(0).displayHardware(); + + int what = android_atomic_and(0, &mConsoleSignals); + if (what & eConsoleAcquired) { + hw.acquireScreen(); + } + + if (mDeferReleaseConsole && hw.canDraw()) { + // We got the release signal before the aquire signal + mDeferReleaseConsole = false; + revokeGPU(); + hw.releaseScreen(); + } + + if (what & eConsoleReleased) { + if (hw.canDraw()) { + revokeGPU(); + hw.releaseScreen(); + } else { + mDeferReleaseConsole = true; + } + } + + mDirtyRegion.set(hw.bounds()); +} + +void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) +{ + Mutex::Autolock _l(mStateLock); + + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + + /* + * Traversal of the children + * (perform the transaction for each of them if needed) + */ + + const bool layersNeedTransaction = transactionFlags & eTraversalNeeded; + if (layersNeedTransaction) { + for (size_t i=0 ; igetTransactionFlags(eTransactionNeeded); + if (!trFlags) continue; + + const uint32_t flags = layer->doTransaction(0); + if (flags & Layer::eVisibleRegion) + mVisibleRegionsDirty = true; + + if (flags & Layer::eRestartTransaction) { + // restart the transaction, but back-off a little + layer->setTransactionFlags(eTransactionNeeded); + setTransactionFlags(eTraversalNeeded, ms2ns(8)); + } + } + } + + /* + * Perform our own transaction if needed + */ + + if (transactionFlags & eTransactionNeeded) { + if (mCurrentState.orientation != mDrawingState.orientation) { + // the orientation has changed, recompute all visible regions + // and invalidate everything. + + const int dpy = 0; + const int orientation = mCurrentState.orientation; + GraphicPlane& plane(graphicPlane(dpy)); + plane.setOrientation(orientation); + + // update the shared control block + const DisplayHardware& hw(plane.displayHardware()); + volatile display_cblk_t* dcblk = mServerCblk->displays + dpy; + dcblk->orientation = orientation; + if (orientation & eOrientationSwapMask) { + // 90 or 270 degrees orientation + dcblk->w = hw.getHeight(); + dcblk->h = hw.getWidth(); + } else { + dcblk->w = hw.getWidth(); + dcblk->h = hw.getHeight(); + } + + mVisibleRegionsDirty = true; + mDirtyRegion.set(hw.bounds()); + + mOrientationAnimation->onOrientationChanged(); + } + + if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) { + // freezing or unfreezing the display -> trigger animation if needed + mFreezeDisplay = mCurrentState.freezeDisplay; + const nsecs_t now = systemTime(); + if (mFreezeDisplay) { + mFreezeDisplayTime = now; + } else { + //LOGD("Screen was frozen for %llu us", + // ns2us(now-mFreezeDisplayTime)); + } + } + + // some layers might have been removed, so + // we need to update the regions they're exposing. + size_t c = mRemovedLayers.size(); + if (c) { + mVisibleRegionsDirty = true; + } + + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) { + // layers have been added + mVisibleRegionsDirty = true; + } + + // get rid of all resources we don't need anymore + // (layers and clients) + free_resources_l(); + } + + commitTransaction(); +} + +sp SurfaceFlinger::getFreezeLock() const +{ + return new FreezeLock(const_cast(this)); +} + +void SurfaceFlinger::computeVisibleRegions( + LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion) +{ + const GraphicPlane& plane(graphicPlane(0)); + const Transform& planeTransform(plane.transform()); + + Region aboveOpaqueLayers; + Region aboveCoveredLayers; + Region dirty; + + bool secureFrameBuffer = false; + + size_t i = currentLayers.size(); + while (i--) { + LayerBase* const layer = currentLayers[i]; + layer->validateVisibility(planeTransform); + + // start with the whole surface at its current location + const Layer::State& s = layer->drawingState(); + const Rect bounds(layer->visibleBounds()); + + // handle hidden surfaces by setting the visible region to empty + Region opaqueRegion; + Region visibleRegion; + Region coveredRegion; + if (UNLIKELY((s.flags & ISurfaceComposer::eLayerHidden) || !s.alpha)) { + visibleRegion.clear(); + } else { + const bool translucent = layer->needsBlending(); + visibleRegion.set(bounds); + coveredRegion = visibleRegion; + + // Remove the transparent area from the visible region + if (translucent) { + visibleRegion.subtractSelf(layer->transparentRegionScreen); + } + + // compute the opaque region + if (s.alpha==255 && !translucent && layer->getOrientation()>=0) { + // the opaque region is the visible region + opaqueRegion = visibleRegion; + } + } + + // subtract the opaque region covered by the layers above us + visibleRegion.subtractSelf(aboveOpaqueLayers); + coveredRegion.andSelf(aboveCoveredLayers); + + // compute this layer's dirty region + if (layer->contentDirty) { + // we need to invalidate the whole region + dirty = visibleRegion; + // as well, as the old visible region + dirty.orSelf(layer->visibleRegionScreen); + layer->contentDirty = false; + } else { + // compute the exposed region + // dirty = what's visible now - what's wasn't covered before + // = what's visible now & what's was covered before + dirty = visibleRegion.intersect(layer->coveredRegionScreen); + } + dirty.subtractSelf(aboveOpaqueLayers); + + // accumulate to the screen dirty region + dirtyRegion.orSelf(dirty); + + // updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer + aboveOpaqueLayers.orSelf(opaqueRegion); + aboveCoveredLayers.orSelf(bounds); + + // Store the visible region is screen space + layer->setVisibleRegion(visibleRegion); + layer->setCoveredRegion(coveredRegion); + + // If a secure layer is partially visible, lockdown the screen! + if (layer->isSecure() && !visibleRegion.isEmpty()) { + secureFrameBuffer = true; + } + } + + mSecureFrameBuffer = secureFrameBuffer; + opaqueRegion = aboveOpaqueLayers; +} + + +void SurfaceFlinger::commitTransaction() +{ + mDrawingState = mCurrentState; + mTransactionCV.signal(); +} + +void SurfaceFlinger::handlePageFlip() +{ + bool visibleRegions = mVisibleRegionsDirty; + LayerVector& currentLayers = const_cast(mDrawingState.layersSortedByZ); + visibleRegions |= lockPageFlip(currentLayers); + + const DisplayHardware& hw = graphicPlane(0).displayHardware(); + const Region screenRegion(hw.bounds()); + if (visibleRegions) { + Region opaqueRegion; + computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion); + mWormholeRegion = screenRegion.subtract(opaqueRegion); + mVisibleRegionsDirty = false; + } + + unlockPageFlip(currentLayers); + mDirtyRegion.andSelf(screenRegion); +} + +bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers) +{ + bool recomputeVisibleRegions = false; + size_t count = currentLayers.size(); + LayerBase* const* layers = currentLayers.array(); + for (size_t i=0 ; ilockPageFlip(recomputeVisibleRegions); + } + return recomputeVisibleRegions; +} + +void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers) +{ + const GraphicPlane& plane(graphicPlane(0)); + const Transform& planeTransform(plane.transform()); + size_t count = currentLayers.size(); + LayerBase* const* layers = currentLayers.array(); + for (size_t i=0 ; iunlockPageFlip(planeTransform, mDirtyRegion); + } +} + +void SurfaceFlinger::handleRepaint() +{ + // set the frame buffer + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + if (UNLIKELY(mDebugRegion)) { + debugFlashRegions(); + } + + // compute the invalid region + mInvalidRegion.orSelf(mDirtyRegion); + + uint32_t flags = hw.getFlags(); + if (flags & DisplayHardware::BUFFER_PRESERVED) { + // here we assume DisplayHardware::flip()'s implementation + // performs the copy-back optimization. + } else { + if (flags & DisplayHardware::UPDATE_ON_DEMAND) { + // we need to fully redraw the part that will be updated + mDirtyRegion.set(mInvalidRegion.bounds()); + } else { + // we need to redraw everything + mDirtyRegion.set(hw.bounds()); + mInvalidRegion = mDirtyRegion; + } + } + + // compose all surfaces + composeSurfaces(mDirtyRegion); + + // clear the dirty regions + mDirtyRegion.clear(); +} + +void SurfaceFlinger::composeSurfaces(const Region& dirty) +{ + if (UNLIKELY(!mWormholeRegion.isEmpty())) { + // should never happen unless the window manager has a bug + // draw something... + drawWormhole(); + } + const SurfaceFlinger& flinger(*this); + const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); + const size_t count = drawingLayers.size(); + LayerBase const* const* const layers = drawingLayers.array(); + for (size_t i=0 ; ivisibleRegionScreen); + if (!visibleRegion.isEmpty()) { + const Region clip(dirty.intersect(visibleRegion)); + if (!clip.isEmpty()) { + layer->draw(clip); + } + } + } +} + +void SurfaceFlinger::unlockClients() +{ + const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); + const size_t count = drawingLayers.size(); + LayerBase* const* const layers = drawingLayers.array(); + for (size_t i=0 ; ifinishPageFlip(); + } +} + +void SurfaceFlinger::scheduleBroadcast(Client* client) +{ + if (mLastScheduledBroadcast != client) { + mLastScheduledBroadcast = client; + mScheduledBroadcasts.add(client); + } +} + +void SurfaceFlinger::executeScheduledBroadcasts() +{ + SortedVector& list = mScheduledBroadcasts; + size_t count = list.size(); + while (count--) { + per_client_cblk_t* const cblk = list[count]->ctrlblk; + if (cblk->lock.tryLock() == NO_ERROR) { + cblk->cv.broadcast(); + list.removeAt(count); + cblk->lock.unlock(); + } else { + // schedule another round + LOGW("executeScheduledBroadcasts() skipped, " + "contention on the client. We'll try again later..."); + signalDelayedEvent(ms2ns(4)); + } + } + mLastScheduledBroadcast = 0; +} + +void SurfaceFlinger::handleDebugCpu() +{ + Mutex::Autolock _l(mDebugLock); + if (mCpuGauge != 0) + mCpuGauge->sample(); +} + +void SurfaceFlinger::debugFlashRegions() +{ + if (UNLIKELY(!mDirtyRegion.isRect())) { + // TODO: do this only if we don't have preserving + // swapBuffer. If we don't have update-on-demand, + // redraw everything. + composeSurfaces(Region(mDirtyRegion.bounds())); + } + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + + glColor4x(0x10000, 0, 0x10000, 0x10000); + + Rect r; + Region::iterator iterator(mDirtyRegion); + while (iterator.iterate(&r)) { + GLfloat vertices[][2] = { + { r.left, r.top }, + { r.left, r.bottom }, + { r.right, r.bottom }, + { r.right, r.top } + }; + glVertexPointer(2, GL_FLOAT, 0, vertices); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + hw.flip(mDirtyRegion.merge(mInvalidRegion)); + mInvalidRegion.clear(); + + if (mDebugRegion > 1) + usleep(mDebugRegion * 1000); + + glEnable(GL_SCISSOR_TEST); + //mDirtyRegion.dump("mDirtyRegion"); +} + +void SurfaceFlinger::drawWormhole() const +{ + const Region region(mWormholeRegion.intersect(mDirtyRegion)); + if (region.isEmpty()) + return; + + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const int32_t width = hw.getWidth(); + const int32_t height = hw.getHeight(); + + glDisable(GL_BLEND); + glDisable(GL_DITHER); + + if (LIKELY(!mDebugBackground)) { + glClearColorx(0,0,0,0); + Rect r; + Region::iterator iterator(region); + while (iterator.iterate(&r)) { + const GLint sy = height - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glClear(GL_COLOR_BUFFER_BIT); + } + } else { + const GLshort vertices[][2] = { { 0, 0 }, { width, 0 }, + { width, height }, { 0, height } }; + const GLshort tcoords[][2] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 } }; + glVertexPointer(2, GL_SHORT, 0, vertices); + glTexCoordPointer(2, GL_SHORT, 0, tcoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, mWormholeTexName); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(width*(1.0f/32.0f), height*(1.0f/32.0f), 1); + Rect r; + Region::iterator iterator(region); + while (iterator.iterate(&r)) { + const GLint sy = height - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + +void SurfaceFlinger::debugShowFPS() const +{ + static int mFrameCount; + static int mLastFrameCount = 0; + static nsecs_t mLastFpsTime = 0; + static float mFps = 0; + mFrameCount++; + nsecs_t now = systemTime(); + nsecs_t diff = now - mLastFpsTime; + if (diff > ms2ns(250)) { + mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff; + mLastFpsTime = now; + mLastFrameCount = mFrameCount; + } + // XXX: mFPS has the value we want + } + +status_t SurfaceFlinger::addLayer(LayerBase* layer) +{ + Mutex::Autolock _l(mStateLock); + addLayer_l(layer); + setTransactionFlags(eTransactionNeeded|eTraversalNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::removeLayer(LayerBase* layer) +{ + Mutex::Autolock _l(mStateLock); + removeLayer_l(layer); + setTransactionFlags(eTransactionNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::invalidateLayerVisibility(LayerBase* layer) +{ + layer->forceVisibilityTransaction(); + setTransactionFlags(eTraversalNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::addLayer_l(LayerBase* layer) +{ + ssize_t i = mCurrentState.layersSortedByZ.add( + layer, &LayerBase::compareCurrentStateZ); + LayerBaseClient* lbc = LayerBase::dynamicCast(layer); + if (lbc) { + mLayerMap.add(lbc->serverIndex(), lbc); + } + mRemovedLayers.remove(layer); + return NO_ERROR; +} + +status_t SurfaceFlinger::removeLayer_l(LayerBase* layerBase) +{ + ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase); + if (index >= 0) { + mRemovedLayers.add(layerBase); + LayerBaseClient* layer = LayerBase::dynamicCast(layerBase); + if (layer) { + mLayerMap.removeItem(layer->serverIndex()); + } + return NO_ERROR; + } + // it's possible that we don't find a layer, because it might + // have been destroyed already -- this is not technically an error + // from the user because there is a race between destroySurface, + // destroyclient and destroySurface-from-a-transaction. + return (index == NAME_NOT_FOUND) ? status_t(NO_ERROR) : index; +} + +void SurfaceFlinger::free_resources_l() +{ + // Destroy layers that were removed + destroy_all_removed_layers_l(); + + // free resources associated with disconnected clients + SortedVector& scheduledBroadcasts(mScheduledBroadcasts); + Vector& disconnectedClients(mDisconnectedClients); + const size_t count = disconnectedClients.size(); + for (size_t i=0 ; i= 0) { + scheduledBroadcasts.removeItemsAt(index); + } + mTokens.release(client->cid); + delete client; + } + disconnectedClients.clear(); +} + +void SurfaceFlinger::destroy_all_removed_layers_l() +{ + size_t c = mRemovedLayers.size(); + while (c--) { + LayerBase* const removed_layer = mRemovedLayers[c]; + + LOGE_IF(mCurrentState.layersSortedByZ.indexOf(removed_layer) >= 0, + "layer %p removed but still in the current state list", + removed_layer); + + delete removed_layer; + } + mRemovedLayers.clear(); +} + + +uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) +{ + return android_atomic_and(~flags, &mTransactionFlags) & flags; +} + +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, nsecs_t delay) +{ + uint32_t old = android_atomic_or(flags, &mTransactionFlags); + if ((old & flags)==0) { // wake the server up + if (delay > 0) { + signalDelayedEvent(delay); + } else { + signalEvent(); + } + } + return old; +} + +void SurfaceFlinger::openGlobalTransaction() +{ + android_atomic_inc(&mTransactionCount); +} + +void SurfaceFlinger::closeGlobalTransaction() +{ + if (android_atomic_dec(&mTransactionCount) == 1) { + signalEvent(); + } +} + +status_t SurfaceFlinger::freezeDisplay(DisplayID dpy, uint32_t flags) +{ + if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + return BAD_VALUE; + + Mutex::Autolock _l(mStateLock); + mCurrentState.freezeDisplay = 1; + setTransactionFlags(eTransactionNeeded); + + // flags is intended to communicate some sort of animation behavior + // (for instance fadding) + return NO_ERROR; +} + +status_t SurfaceFlinger::unfreezeDisplay(DisplayID dpy, uint32_t flags) +{ + if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + return BAD_VALUE; + + Mutex::Autolock _l(mStateLock); + mCurrentState.freezeDisplay = 0; + setTransactionFlags(eTransactionNeeded); + + // flags is intended to communicate some sort of animation behavior + // (for instance fadding) + return NO_ERROR; +} + +int SurfaceFlinger::setOrientation(DisplayID dpy, int orientation) +{ + if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + return BAD_VALUE; + + Mutex::Autolock _l(mStateLock); + if (mCurrentState.orientation != orientation) { + if (uint32_t(orientation)<=eOrientation270 || orientation==42) { + mCurrentState.orientation = orientation; + setTransactionFlags(eTransactionNeeded); + mTransactionCV.wait(mStateLock); + } else { + orientation = BAD_VALUE; + } + } + return orientation; +} + +sp SurfaceFlinger::createSurface(ClientID clientId, int pid, + ISurfaceFlingerClient::surface_data_t* params, + DisplayID d, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) +{ + LayerBaseClient* layer = 0; + sp surfaceHandle; + Mutex::Autolock _l(mStateLock); + Client* const c = mClientsMap.valueFor(clientId); + if (UNLIKELY(!c)) { + LOGE("createSurface() failed, client not found (id=%d)", clientId); + return surfaceHandle; + } + + //LOGD("createSurface for pid %d (%d x %d)", pid, w, h); + int32_t id = c->generateId(pid); + if (uint32_t(id) >= NUM_LAYERS_MAX) { + LOGE("createSurface() failed, generateId = %d", id); + return surfaceHandle; + } + + switch (flags & eFXSurfaceMask) { + case eFXSurfaceNormal: + if (UNLIKELY(flags & ePushBuffers)) { + layer = createPushBuffersSurfaceLocked(c, d, id, w, h, flags); + } else { + layer = createNormalSurfaceLocked(c, d, id, w, h, format, flags); + } + break; + case eFXSurfaceBlur: + layer = createBlurSurfaceLocked(c, d, id, w, h, flags); + break; + case eFXSurfaceDim: + layer = createDimSurfaceLocked(c, d, id, w, h, flags); + break; + } + + if (layer) { + setTransactionFlags(eTransactionNeeded); + surfaceHandle = layer->getSurface(); + if (surfaceHandle != 0) + surfaceHandle->getSurfaceData(params); + } + + return surfaceHandle; +} + +LayerBaseClient* SurfaceFlinger::createNormalSurfaceLocked( + Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) +{ + // initialize the surfaces + switch (format) { // TODO: take h/w into account + case PIXEL_FORMAT_TRANSPARENT: + case PIXEL_FORMAT_TRANSLUCENT: + format = PIXEL_FORMAT_RGBA_8888; + break; + case PIXEL_FORMAT_OPAQUE: + format = PIXEL_FORMAT_RGB_565; + break; + } + + Layer* layer = new Layer(this, display, client, id); + status_t err = layer->setBuffers(client, w, h, format, flags); + if (LIKELY(err == NO_ERROR)) { + layer->initStates(w, h, flags); + addLayer_l(layer); + } else { + LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err)); + delete layer; + return 0; + } + return layer; +} + +LayerBaseClient* SurfaceFlinger::createBlurSurfaceLocked( + Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + LayerBlur* layer = new LayerBlur(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +LayerBaseClient* SurfaceFlinger::createDimSurfaceLocked( + Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + LayerDim* layer = new LayerDim(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +LayerBaseClient* SurfaceFlinger::createPushBuffersSurfaceLocked( + Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + LayerBuffer* layer = new LayerBuffer(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +status_t SurfaceFlinger::destroySurface(SurfaceID index) +{ + Mutex::Autolock _l(mStateLock); + LayerBaseClient* const layer = getLayerUser_l(index); + status_t err = removeLayer_l(layer); + if (err < 0) + return err; + setTransactionFlags(eTransactionNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::setClientState( + ClientID cid, + int32_t count, + const layer_state_t* states) +{ + Mutex::Autolock _l(mStateLock); + uint32_t flags = 0; + cid <<= 16; + for (int i=0 ; isetPosition(s.x, s.y)) + flags |= eTraversalNeeded; + } + if (what & eLayerChanged) { + if (layer->setLayer(s.z)) { + mCurrentState.layersSortedByZ.reorder( + layer, &Layer::compareCurrentStateZ); + // we need traversal (state changed) + // AND transaction (list changed) + flags |= eTransactionNeeded|eTraversalNeeded; + } + } + if (what & eSizeChanged) { + if (layer->setSize(s.w, s.h)) + flags |= eTraversalNeeded; + } + if (what & eAlphaChanged) { + if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f))) + flags |= eTraversalNeeded; + } + if (what & eMatrixChanged) { + if (layer->setMatrix(s.matrix)) + flags |= eTraversalNeeded; + } + if (what & eTransparentRegionChanged) { + if (layer->setTransparentRegionHint(s.transparentRegion)) + flags |= eTraversalNeeded; + } + if (what & eVisibilityChanged) { + if (layer->setFlags(s.flags, s.mask)) + flags |= eTraversalNeeded; + } + } + } + if (flags) { + setTransactionFlags(flags); + } + return NO_ERROR; +} + +LayerBaseClient* SurfaceFlinger::getLayerUser_l(SurfaceID s) const +{ + return mLayerMap.valueFor(s); +} + +void SurfaceFlinger::screenReleased(int dpy) +{ + // this may be called by a signal handler, we can't do too much in here + android_atomic_or(eConsoleReleased, &mConsoleSignals); + signalEvent(); +} + +void SurfaceFlinger::screenAcquired(int dpy) +{ + // this may be called by a signal handler, we can't do too much in here + android_atomic_or(eConsoleAcquired, &mConsoleSignals); + signalEvent(); +} + +status_t SurfaceFlinger::dump(int fd, const Vector& args) +{ + const size_t SIZE = 1024; + char buffer[SIZE]; + String8 result; + if (checkCallingPermission( + String16("android.permission.DUMP")) == false) + { // not allowed + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump SurfaceFlinger from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + } else { + Mutex::Autolock _l(mStateLock); + size_t s = mClientsMap.size(); + char name[64]; + for (size_t i=0 ; icid); + client->dump(name); + } + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; idrawingState(); + snprintf(buffer, SIZE, + "+ %s %p\n" + " " + "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " + "needsBlending=%1d, invalidate=%1d, " + "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", + layer->getTypeID(), layer, + s.z, layer->tx(), layer->ty(), s.w, s.h, + layer->needsBlending(), layer->contentDirty, + s.alpha, s.flags, + s.transform[0], s.transform[1], + s.transform[2], s.transform[3]); + result.append(buffer); + buffer[0] = 0; + /*** LayerBaseClient ***/ + LayerBaseClient* const lbc = + LayerBase::dynamicCast((LayerBase*)layer); + if (lbc) { + snprintf(buffer, SIZE, + " " + "id=0x%08x, client=0x%08x, identity=%u\n", + lbc->clientIndex(), lbc->client ? lbc->client->cid : 0, + lbc->getIdentity()); + } + result.append(buffer); + buffer[0] = 0; + /*** Layer ***/ + Layer* const l = LayerBase::dynamicCast((LayerBase*)layer); + if (l) { + const LayerBitmap& buf0(l->getBuffer(0)); + const LayerBitmap& buf1(l->getBuffer(1)); + snprintf(buffer, SIZE, + " " + "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u], mTextureName=%d," + " freezeLock=%p, swapState=0x%08x\n", + l->pixelFormat(), + buf0.width(), buf0.height(), buf0.stride(), + buf1.width(), buf1.height(), buf1.stride(), + l->getTextureName(), l->getFreezeLock().get(), + l->lcblk->swapState); + } + result.append(buffer); + buffer[0] = 0; + s.transparentRegion.dump(result, "transparentRegion"); + layer->transparentRegionScreen.dump(result, "transparentRegionScreen"); + layer->visibleRegionScreen.dump(result, "visibleRegionScreen"); + } + mWormholeRegion.dump(result, "WormholeRegion"); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + snprintf(buffer, SIZE, + " display frozen: %s, freezeCount=%d, orientation=%d, canDraw=%d\n", + mFreezeDisplay?"yes":"no", mFreezeCount, + mCurrentState.orientation, hw.canDraw()); + result.append(buffer); + + sp allocator; + if (mGPU != 0) { + snprintf(buffer, SIZE, " GPU owner: %d\n", mGPU->getOwner()); + result.append(buffer); + allocator = mGPU->getAllocator(); + if (allocator != 0) { + allocator->dump(result, "GPU Allocator"); + } + } + allocator = mSurfaceHeapManager->getAllocator(NATIVE_MEMORY_TYPE_PMEM); + if (allocator != 0) { + allocator->dump(result, "PMEM Allocator"); + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t SurfaceFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case CREATE_CONNECTION: + case OPEN_GLOBAL_TRANSACTION: + case CLOSE_GLOBAL_TRANSACTION: + case SET_ORIENTATION: + case FREEZE_DISPLAY: + case UNFREEZE_DISPLAY: + case BOOT_FINISHED: + case REVOKE_GPU: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int self_pid = getpid(); + if (UNLIKELY(pid != self_pid)) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.ACCESS_SURFACE_FLINGER"))) + { + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + } + } + + status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags); + if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { + // HARDWARE_TEST stuff... + if (UNLIKELY(checkCallingPermission( + String16("android.permission.HARDWARE_TEST")) == false)) + { // not allowed + LOGE("Permission Denial: pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + return PERMISSION_DENIED; + } + int n; + switch (code) { + case 1000: // SHOW_CPU + n = data.readInt32(); + mDebugCpu = n ? 1 : 0; + if (mDebugCpu) { + if (mCpuGauge == 0) { + mCpuGauge = new CPUGauge(this, ms2ns(500)); + } + } else { + if (mCpuGauge != 0) { + mCpuGauge->requestExitAndWait(); + Mutex::Autolock _l(mDebugLock); + mCpuGauge.clear(); + } + } + return NO_ERROR; + case 1001: // SHOW_FPS + n = data.readInt32(); + mDebugFps = n ? 1 : 0; + return NO_ERROR; + case 1002: // SHOW_UPDATES + n = data.readInt32(); + mDebugRegion = n ? n : (mDebugRegion ? 0 : 1); + return NO_ERROR; + case 1003: // SHOW_BACKGROUND + n = data.readInt32(); + mDebugBackground = n ? 1 : 0; + return NO_ERROR; + case 1004:{ // repaint everything + Mutex::Autolock _l(mStateLock); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe + signalEvent(); + } + return NO_ERROR; + case 1005: // ask GPU revoke + mGPU->friendlyRevoke(); + return NO_ERROR; + case 1006: // revoke GPU + mGPU->unconditionalRevoke(); + return NO_ERROR; + case 1007: // set mFreezeCount + mFreezeCount = data.readInt32(); + return NO_ERROR; + case 1010: // interrogate. + reply->writeInt32(mDebugCpu); + reply->writeInt32(0); + reply->writeInt32(mDebugRegion); + reply->writeInt32(mDebugBackground); + return NO_ERROR; + case 1013: { + Mutex::Autolock _l(mStateLock); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + reply->writeInt32(hw.getPageFlipCount()); + } + return NO_ERROR; + } + } + return err; +} + +// --------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +Client::Client(ClientID clientID, const sp& flinger) + : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger) +{ + mSharedHeapAllocator = getSurfaceHeapManager()->createHeap(); + const int pgsize = getpagesize(); + const int cblksize=((sizeof(per_client_cblk_t)+(pgsize-1))&~(pgsize-1)); + mCblkHeap = new MemoryDealer(cblksize); + mCblkMemory = mCblkHeap->allocate(cblksize); + if (mCblkMemory != 0) { + ctrlblk = static_cast(mCblkMemory->pointer()); + if (ctrlblk) { // construct the shared structure in-place. + new(ctrlblk) per_client_cblk_t; + } + } +} + +Client::~Client() { + if (ctrlblk) { + const int pgsize = getpagesize(); + ctrlblk->~per_client_cblk_t(); // destroy our shared-structure. + } +} + +const sp& Client::getSurfaceHeapManager() const { + return mFlinger->getSurfaceHeapManager(); +} + +int32_t Client::generateId(int pid) +{ + const uint32_t i = clz( ~mBitmap ); + if (i >= NUM_LAYERS_MAX) { + return NO_MEMORY; + } + mPid = pid; + mInUse.add(uint8_t(i)); + mBitmap |= 1<<(31-i); + return i; +} +status_t Client::bindLayer(LayerBaseClient* layer, int32_t id) +{ + ssize_t idx = mInUse.indexOf(id); + if (idx < 0) + return NAME_NOT_FOUND; + return mLayers.insertAt(layer, idx); +} +void Client::free(int32_t id) +{ + ssize_t idx = mInUse.remove(uint8_t(id)); + if (idx >= 0) { + mBitmap &= ~(1<<(31-id)); + mLayers.removeItemsAt(idx); + } +} + +sp Client::createAllocator(uint32_t flags) +{ + sp allocator; + allocator = getSurfaceHeapManager()->createHeap( + flags, getClientPid(), mSharedHeapAllocator); + return allocator; +} + +bool Client::isValid(int32_t i) const { + return (uint32_t(i)& cblk) + : mId(cid), mFlinger(flinger), mCblk(cblk) +{ +} + +BClient::~BClient() { + // destroy all resources attached to this client + mFlinger->destroyConnection(mId); +} + +void BClient::getControlBlocks(sp* ctrl) const { + *ctrl = mCblk; +} + +sp BClient::createSurface( + ISurfaceFlingerClient::surface_data_t* params, int pid, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) +{ + return mFlinger->createSurface(mId, pid, params, display, w, h, format, flags); +} + +status_t BClient::destroySurface(SurfaceID sid) +{ + sid |= (mId << 16); // add the client-part to id + return mFlinger->destroySurface(sid); +} + +status_t BClient::setState(int32_t count, const layer_state_t* states) +{ + return mFlinger->setClientState(mId, count, states); +} + +// --------------------------------------------------------------------------- + +GraphicPlane::GraphicPlane() + : mHw(0) +{ +} + +GraphicPlane::~GraphicPlane() { + delete mHw; +} + +bool GraphicPlane::initialized() const { + return mHw ? true : false; +} + +void GraphicPlane::setDisplayHardware(DisplayHardware *hw) { + mHw = hw; +} + +void GraphicPlane::setTransform(const Transform& tr) { + mTransform = tr; + mGlobalTransform = mOrientationTransform * mTransform; +} + +status_t GraphicPlane::orientationToTransfrom( + int orientation, int w, int h, Transform* tr) +{ + float a, b, c, d, x, y; + switch (orientation) { + case ISurfaceComposer::eOrientationDefault: + a=1; b=0; c=0; d=1; x=0; y=0; + break; + case ISurfaceComposer::eOrientation90: + a=0; b=-1; c=1; d=0; x=w; y=0; + break; + case ISurfaceComposer::eOrientation180: + a=-1; b=0; c=0; d=-1; x=w; y=h; + break; + case ISurfaceComposer::eOrientation270: + a=0; b=1; c=-1; d=0; x=0; y=h; + break; + default: + return BAD_VALUE; + } + tr->set(a, b, c, d); + tr->set(x, y); + return NO_ERROR; +} + +status_t GraphicPlane::setOrientation(int orientation) +{ + const DisplayHardware& hw(displayHardware()); + const float w = hw.getWidth(); + const float h = hw.getHeight(); + + if (orientation == ISurfaceComposer::eOrientationDefault) { + // make sure the default orientation is optimal + mOrientationTransform.reset(); + mGlobalTransform = mTransform; + return NO_ERROR; + } + + // If the rotation can be handled in hardware, this is where + // the magic should happen. + if (UNLIKELY(orientation == 42)) { + float a, b, c, d, x, y; + const float r = (3.14159265f / 180.0f) * 42.0f; + const float si = sinf(r); + const float co = cosf(r); + a=co; b=-si; c=si; d=co; + x = si*(h*0.5f) + (1-co)*(w*0.5f); + y =-si*(w*0.5f) + (1-co)*(h*0.5f); + mOrientationTransform.set(a, b, c, d); + mOrientationTransform.set(x, y); + } else { + GraphicPlane::orientationToTransfrom(orientation, w, h, + &mOrientationTransform); + } + + mGlobalTransform = mOrientationTransform * mTransform; + return NO_ERROR; +} + +const DisplayHardware& GraphicPlane::displayHardware() const { + return *mHw; +} + +const Transform& GraphicPlane::transform() const { + return mGlobalTransform; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h new file mode 100644 index 000000000..f7d77640a --- /dev/null +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_SURFACE_FLINGER_H +#define ANDROID_SURFACE_FLINGER_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "Barrier.h" +#include "BootAnimation.h" +#include "CPUGauge.h" +#include "Layer.h" +#include "Tokenizer.h" + +struct copybit_device_t; +struct overlay_device_t; + +namespace android { + +// --------------------------------------------------------------------------- + +class Client; +class BClient; +class DisplayHardware; +class FreezeLock; +class GPUHardwareInterface; +class IGPUCallback; +class Layer; +class LayerBuffer; +class LayerOrientationAnim; +class OrientationAnimation; +class SurfaceHeapManager; + +typedef int32_t ClientID; + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +class Client +{ +public: + Client(ClientID cid, const sp& flinger); + ~Client(); + + int32_t generateId(int pid); + void free(int32_t id); + status_t bindLayer(LayerBaseClient* layer, int32_t id); + sp createAllocator(uint32_t memory_type); + + inline bool isValid(int32_t i) const; + inline const uint8_t* inUseArray() const; + inline size_t numActiveLayers() const; + LayerBaseClient* getLayerUser(int32_t i) const; + const Vector& getLayers() const { return mLayers; } + const sp& controlBlockMemory() const { return mCblkMemory; } + void dump(const char* what); + const sp& getSurfaceHeapManager() const; + + // pointer to this client's control block + per_client_cblk_t* ctrlblk; + ClientID cid; + + +private: + int getClientPid() const { return mPid; } + + int mPid; + uint32_t mBitmap; + SortedVector mInUse; + Vector mLayers; + sp mCblkHeap; + sp mFlinger; + sp mSharedHeapAllocator; + sp mPMemAllocator; + sp mCblkMemory; +}; + +// --------------------------------------------------------------------------- + +class GraphicPlane +{ +public: + static status_t orientationToTransfrom(int orientation, int w, int h, + Transform* tr); + + GraphicPlane(); + ~GraphicPlane(); + + bool initialized() const; + + void setDisplayHardware(DisplayHardware *); + void setTransform(const Transform& tr); + status_t setOrientation(int orientation); + + const DisplayHardware& displayHardware() const; + const Transform& transform() const; +private: + GraphicPlane(const GraphicPlane&); + GraphicPlane operator = (const GraphicPlane&); + + DisplayHardware* mHw; + Transform mTransform; + Transform mOrientationTransform; + Transform mGlobalTransform; +}; + +// --------------------------------------------------------------------------- + +enum { + eTransactionNeeded = 0x01, + eTraversalNeeded = 0x02 +}; + +class SurfaceFlinger : public BnSurfaceComposer, protected Thread +{ +public: + static void instantiate(); + static void shutdown(); + + SurfaceFlinger(); + virtual ~SurfaceFlinger(); + void init(); + + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + + virtual status_t dump(int fd, const Vector& args); + + // ISurfaceComposer interface + virtual sp createConnection(); + virtual sp getCblk() const; + virtual void bootFinished(); + virtual void openGlobalTransaction(); + virtual void closeGlobalTransaction(); + virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags); + virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags); + virtual int setOrientation(DisplayID dpy, int orientation); + virtual void signal() const; + virtual status_t requestGPU(const sp& callback, + gpu_info_t* gpu); + virtual status_t revokeGPU(); + + void screenReleased(DisplayID dpy); + void screenAcquired(DisplayID dpy); + + const sp& getSurfaceHeapManager() const { + return mSurfaceHeapManager; + } + + const sp& getGPU() const { + return mGPU; + } + + copybit_device_t* getBlitEngine() const; + overlay_control_device_t* getOverlayEngine() const; + + + status_t removeLayer(LayerBase* layer); + status_t addLayer(LayerBase* layer); + status_t invalidateLayerVisibility(LayerBase* layer); + +private: + friend class BClient; + friend class LayerBase; + friend class LayerBuffer; + friend class LayerBaseClient; + friend class Layer; + friend class LayerBlur; + + sp createSurface(ClientID client, int pid, + ISurfaceFlingerClient::surface_data_t* params, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags); + + LayerBaseClient* createNormalSurfaceLocked(Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags); + + LayerBaseClient* createBlurSurfaceLocked(Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + LayerBaseClient* createDimSurfaceLocked(Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + LayerBaseClient* createPushBuffersSurfaceLocked(Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + status_t destroySurface(SurfaceID surface_id); + status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states); + + + class LayerVector { + public: + inline LayerVector() { } + LayerVector(const LayerVector&); + inline size_t size() const { return layers.size(); } + inline LayerBase*const* array() const { return layers.array(); } + ssize_t add(LayerBase*, Vector::compar_t); + ssize_t remove(LayerBase*); + ssize_t reorder(LayerBase*, Vector::compar_t); + ssize_t indexOf(LayerBase* key, size_t guess=0) const; + inline LayerBase* operator [] (size_t i) const { return layers[i]; } + private: + KeyedVector lookup; + Vector layers; + }; + + struct State { + State() { + orientation = ISurfaceComposer::eOrientationDefault; + freezeDisplay = 0; + } + LayerVector layersSortedByZ; + uint8_t orientation; + uint8_t freezeDisplay; + }; + + class DelayedTransaction : public Thread + { + friend class SurfaceFlinger; + sp mFlinger; + nsecs_t mDelay; + public: + DelayedTransaction(const sp& flinger, nsecs_t delay) + : Thread(false), mFlinger(flinger), mDelay(delay) { + } + virtual bool threadLoop() { + usleep(mDelay / 1000); + if (android_atomic_and(~1, + &mFlinger->mDeplayedTransactionPending) == 1) { + mFlinger->signalEvent(); + } + return false; + } + }; + + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + + const GraphicPlane& graphicPlane(int dpy) const; + GraphicPlane& graphicPlane(int dpy); + + void waitForEvent(); + void signalEvent(); + void signalDelayedEvent(nsecs_t delay); + + void handleConsoleEvents(); + void handleTransaction(uint32_t transactionFlags); + + void computeVisibleRegions( + LayerVector& currentLayers, + Region& dirtyRegion, + Region& wormholeRegion); + + void handlePageFlip(); + bool lockPageFlip(const LayerVector& currentLayers); + void unlockPageFlip(const LayerVector& currentLayers); + void handleRepaint(); + void handleDebugCpu(); + void scheduleBroadcast(Client* client); + void executeScheduledBroadcasts(); + void postFramebuffer(); + void composeSurfaces(const Region& dirty); + void unlockClients(); + + + void destroyConnection(ClientID cid); + LayerBaseClient* getLayerUser_l(SurfaceID index) const; + status_t addLayer_l(LayerBase* layer); + status_t removeLayer_l(LayerBase* layer); + void destroy_all_removed_layers_l(); + void free_resources_l(); + + uint32_t getTransactionFlags(uint32_t flags); + uint32_t setTransactionFlags(uint32_t flags, nsecs_t delay = 0); + void commitTransaction(); + + + friend class FreezeLock; + sp getFreezeLock() const; + inline void incFreezeCount() { mFreezeCount++; } + inline void decFreezeCount() { if (mFreezeCount > 0) mFreezeCount--; } + inline bool hasFreezeRequest() const { return mFreezeDisplay; } + inline bool isFrozen() const { + return mFreezeDisplay || mFreezeCount>0; + } + + + void debugFlashRegions(); + void debugShowFPS() const; + void drawWormhole() const; + + // access must be protected by mStateLock + mutable Mutex mStateLock; + State mCurrentState; + State mDrawingState; + volatile int32_t mTransactionFlags; + volatile int32_t mTransactionCount; + Condition mTransactionCV; + + // protected by mStateLock (but we could use another lock) + Tokenizer mTokens; + DefaultKeyedVector mClientsMap; + DefaultKeyedVector mLayerMap; + GraphicPlane mGraphicPlanes[1]; + SortedVector mRemovedLayers; + Vector mDisconnectedClients; + + // constant members (no synchronization needed for access) + sp mServerHeap; + sp mServerCblkMemory; + surface_flinger_cblk_t* mServerCblk; + sp mSurfaceHeapManager; + sp mGPU; + GLuint mWormholeTexName; + sp mBootAnimation; + nsecs_t mBootTime; + + // Can only accessed from the main thread, these members + // don't need synchronization + Region mDirtyRegion; + Region mInvalidRegion; + Region mWormholeRegion; + Client* mLastScheduledBroadcast; + SortedVector mScheduledBroadcasts; + bool mVisibleRegionsDirty; + bool mDeferReleaseConsole; + bool mFreezeDisplay; + int32_t mFreezeCount; + nsecs_t mFreezeDisplayTime; + friend class OrientationAnimation; + OrientationAnimation* mOrientationAnimation; + + // access protected by mDebugLock + mutable Mutex mDebugLock; + sp mCpuGauge; + + // don't use a lock for these, we don't care + int mDebugRegion; + int mDebugCpu; + int mDebugFps; + int mDebugBackground; + int mDebugNoBootAnimation; + + // these are thread safe + mutable Barrier mReadyToRunBarrier; + mutable SurfaceFlingerSynchro mSyncObject; + volatile int32_t mDeplayedTransactionPending; + + // atomic variables + enum { + eConsoleReleased = 1, + eConsoleAcquired = 2 + }; + volatile int32_t mConsoleSignals; + + // only written in the main thread, only read in other threads + volatile int32_t mSecureFrameBuffer; +}; + +// --------------------------------------------------------------------------- + +class FreezeLock : public LightRefBase { + SurfaceFlinger* mFlinger; +public: + FreezeLock(SurfaceFlinger* flinger) + : mFlinger(flinger) { + mFlinger->incFreezeCount(); + } + ~FreezeLock() { + mFlinger->decFreezeCount(); + } +}; + +// --------------------------------------------------------------------------- + +class BClient : public BnSurfaceFlingerClient +{ +public: + BClient(SurfaceFlinger *flinger, ClientID cid, + const sp& cblk); + ~BClient(); + + // ISurfaceFlingerClient interface + virtual void getControlBlocks(sp* ctrl) const; + + virtual sp createSurface( + surface_data_t* params, int pid, + DisplayID display, uint32_t w, uint32_t h,PixelFormat format, + uint32_t flags); + + virtual status_t destroySurface(SurfaceID surfaceId); + virtual status_t setState(int32_t count, const layer_state_t* states); + +private: + ClientID mId; + SurfaceFlinger* mFlinger; + sp mCblk; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SURFACE_FLINGER_H diff --git a/libs/surfaceflinger/Tokenizer.cpp b/libs/surfaceflinger/Tokenizer.cpp new file mode 100644 index 000000000..ef51d6abc --- /dev/null +++ b/libs/surfaceflinger/Tokenizer.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2007 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 "Tokenizer.h" + +// ---------------------------------------------------------------------------- + +namespace android { + +ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t) + +Tokenizer::Tokenizer() +{ +} + +Tokenizer::Tokenizer(const Tokenizer& other) + : mRanges(other.mRanges) +{ +} + +Tokenizer::~Tokenizer() +{ +} + +uint32_t Tokenizer::acquire() +{ + if (!mRanges.size() || mRanges[0].first) { + _insertTokenAt(0,0); + return 0; + } + + // just extend the first run + const run_t& run = mRanges[0]; + uint32_t token = run.first + run.length; + _insertTokenAt(token, 1); + return token; +} + +bool Tokenizer::isAcquired(uint32_t token) const +{ + return (_indexOrderOf(token) >= 0); +} + +status_t Tokenizer::reserve(uint32_t token) +{ + size_t o; + const ssize_t i = _indexOrderOf(token, &o); + if (i >= 0) { + return BAD_VALUE; // this token is already taken + } + ssize_t err = _insertTokenAt(token, o); + return (err<0) ? err : status_t(NO_ERROR); +} + +status_t Tokenizer::release(uint32_t token) +{ + const ssize_t i = _indexOrderOf(token); + if (i >= 0) { + const run_t& run = mRanges[i]; + if ((token >= run.first) && (token < run.first+run.length)) { + // token in this range, we need to split + run_t& run = mRanges.editItemAt(i); + if ((token == run.first) || (token == run.first+run.length-1)) { + if (token == run.first) { + run.first += 1; + } + run.length -= 1; + if (run.length == 0) { + // XXX: should we systematically remove a run that's empty? + mRanges.removeItemsAt(i); + } + } else { + // split the run + run_t new_run; + new_run.first = token+1; + new_run.length = run.first+run.length - new_run.first; + run.length = token - run.first; + mRanges.insertAt(new_run, i+1); + } + return NO_ERROR; + } + } + return NAME_NOT_FOUND; +} + +ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = mRanges.size()-1; + ssize_t mid; + const run_t* a = mRanges.array(); + while (l <= h) { + mid = l + (h - l)/2; + const run_t* const curr = a + mid; + int c = 0; + if (token < curr->first) c = 1; + else if (token >= curr->first+curr->length) c = -1; + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index) +{ + const size_t c = mRanges.size(); + + if (index >= 1) { + // do we need to merge with the previous run? + run_t& p = mRanges.editItemAt(index-1); + if (p.first+p.length == token) { + p.length += 1; + if (index < c) { + const run_t& n = mRanges[index]; + if (token+1 == n.first) { + p.length += n.length; + mRanges.removeItemsAt(index); + } + } + return index; + } + } + + if (index < c) { + // do we need to merge with the next run? + run_t& n = mRanges.editItemAt(index); + if (token+1 == n.first) { + n.first -= 1; + n.length += 1; + return index; + } + } + + return mRanges.insertAt(run_t(token,1), index); +} + +void Tokenizer::dump() const +{ + const run_t* ranges = mRanges.array(); + const size_t c = mRanges.size(); + printf("Tokenizer (%p, size = %lu)\n", this, c); + for (size_t i=0 ; i +#include + +// ---------------------------------------------------------------------------- + +namespace android { + +class Tokenizer +{ +public: + Tokenizer(); + Tokenizer(const Tokenizer& other); + ~Tokenizer(); + + uint32_t acquire(); + status_t reserve(uint32_t token); + status_t release(uint32_t token); + bool isAcquired(uint32_t token) const; + + void dump() const; + + struct run_t { + run_t() {}; + run_t(uint32_t f, uint32_t l) : first(f), length(l) {} + uint32_t first; + uint32_t length; + }; +private: + ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const; + ssize_t _insertTokenAt(uint32_t token, size_t index); + Vector mRanges; +}; + +}; // namespace android + +// ---------------------------------------------------------------------------- + +#endif // ANDROID_TOKENIZER_H diff --git a/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp new file mode 100644 index 000000000..bec7a6403 --- /dev/null +++ b/libs/surfaceflinger/Transform.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2007 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 "Transform.h" + +// --------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +namespace android { + +// --------------------------------------------------------------------------- + +Transform::Transform() + : mType(0) +{ + mTransform.reset(); +} + +Transform::Transform(const Transform& other) + : mTransform(other.mTransform), mType(other.mType) +{ +} + +Transform::~Transform() { +} + +Transform Transform::operator * (const Transform& rhs) const +{ + if (LIKELY(mType == 0)) + return rhs; + + Transform r(*this); + r.mTransform.preConcat(rhs.mTransform); + r.mType |= rhs.mType; + return r; +} + +float Transform::operator [] (int i) const +{ + float r = 0; + switch(i) { + case 0: r = SkScalarToFloat( mTransform[SkMatrix::kMScaleX] ); break; + case 1: r = SkScalarToFloat( mTransform[SkMatrix::kMSkewX] ); break; + case 2: r = SkScalarToFloat( mTransform[SkMatrix::kMSkewY] ); break; + case 3: r = SkScalarToFloat( mTransform[SkMatrix::kMScaleY] ); break; + } + return r; +} + +uint8_t Transform::type() const +{ + if (UNLIKELY(mType & 0x80000000)) { + mType = mTransform.getType(); + } + return uint8_t(mType & 0xFF); +} + +bool Transform::transformed() const { + return type() > SkMatrix::kTranslate_Mask; +} + +int Transform::tx() const { + return SkScalarRound( mTransform[SkMatrix::kMTransX] ); +} + +int Transform::ty() const { + return SkScalarRound( mTransform[SkMatrix::kMTransY] ); +} + +void Transform::reset() { + mTransform.reset(); + mType = 0; +} + +void Transform::set( float xx, float xy, + float yx, float yy) +{ + mTransform.set(SkMatrix::kMScaleX, SkFloatToScalar(xx)); + mTransform.set(SkMatrix::kMSkewX, SkFloatToScalar(xy)); + mTransform.set(SkMatrix::kMSkewY, SkFloatToScalar(yx)); + mTransform.set(SkMatrix::kMScaleY, SkFloatToScalar(yy)); + mType |= 0x80000000; +} + +void Transform::set(int tx, int ty) +{ + if (tx | ty) { + mTransform.set(SkMatrix::kMTransX, SkIntToScalar(tx)); + mTransform.set(SkMatrix::kMTransY, SkIntToScalar(ty)); + mType |= SkMatrix::kTranslate_Mask; + } else { + mTransform.set(SkMatrix::kMTransX, 0); + mTransform.set(SkMatrix::kMTransY, 0); + mType &= ~SkMatrix::kTranslate_Mask; + } +} + +void Transform::transform(GLfixed* point, int x, int y) const +{ + SkPoint s; + mTransform.mapXY(SkIntToScalar(x), SkIntToScalar(y), &s); + point[0] = SkScalarToFixed(s.fX); + point[1] = SkScalarToFixed(s.fY); +} + +Rect Transform::makeBounds(int w, int h) const +{ + Rect r; + SkRect d, s; + s.set(0, 0, SkIntToScalar(w), SkIntToScalar(h)); + mTransform.mapRect(&d, s); + r.left = SkScalarRound( d.fLeft ); + r.top = SkScalarRound( d.fTop ); + r.right = SkScalarRound( d.fRight ); + r.bottom = SkScalarRound( d.fBottom ); + return r; +} + +Rect Transform::transform(const Rect& bounds) const +{ + Rect r; + SkRect d, s; + s.set( SkIntToScalar( bounds.left ), + SkIntToScalar( bounds.top ), + SkIntToScalar( bounds.right ), + SkIntToScalar( bounds.bottom )); + mTransform.mapRect(&d, s); + r.left = SkScalarRound( d.fLeft ); + r.top = SkScalarRound( d.fTop ); + r.right = SkScalarRound( d.fRight ); + r.bottom = SkScalarRound( d.fBottom ); + return r; +} + +Region Transform::transform(const Region& reg) const +{ + Region out; + if (UNLIKELY(transformed())) { + if (LIKELY(preserveRects())) { + Rect r; + Region::iterator iterator(reg); + while (iterator.iterate(&r)) { + out.orSelf(transform(r)); + } + } else { + out.set(transform(reg.bounds())); + } + } else { + out = reg.translate(tx(), ty()); + } + return out; +} + +int32_t Transform::getOrientation() const +{ + uint32_t flags = 0; + if (UNLIKELY(transformed())) { + SkScalar a = mTransform[SkMatrix::kMScaleX]; + SkScalar b = mTransform[SkMatrix::kMSkewX]; + SkScalar c = mTransform[SkMatrix::kMSkewY]; + SkScalar d = mTransform[SkMatrix::kMScaleY]; + if (b==0 && c==0 && a && d) { + if (a<0) flags |= FLIP_H; + if (d<0) flags |= FLIP_V; + } else if (b && c && a==0 && d==0) { + flags |= ROT_90; + if (b>0) flags |= FLIP_H; + if (c<0) flags |= FLIP_V; + } else { + flags = 0x80000000; + } + } + return flags; +} + +bool Transform::preserveRects() const +{ + return mTransform.rectStaysRect(); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/Transform.h b/libs/surfaceflinger/Transform.h new file mode 100644 index 000000000..0b4835eff --- /dev/null +++ b/libs/surfaceflinger/Transform.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_TRANSFORM_H +#define ANDROID_TRANSFORM_H + +#include +#include + +#include +#include + +#include + +#include + +namespace android { + +class Region; + +// --------------------------------------------------------------------------- + +class Transform +{ +public: + Transform(); + Transform(const Transform& other); + ~Transform(); + + enum orientation_flags { + ROT_0 = 0x00000000, + FLIP_H = 0x00000001, + FLIP_V = 0x00000002, + ROT_90 = 0x00000004, + ROT_180 = FLIP_H|FLIP_V, + ROT_270 = ROT_180|ROT_90, + ROT_INVALID = 0x80000000 + }; + + bool transformed() const; + int32_t getOrientation() const; + bool preserveRects() const; + + int tx() const; + int ty() const; + + void reset(); + void set(float xx, float xy, float yx, float yy); + void set(int tx, int ty); + + Rect makeBounds(int w, int h) const; + void transform(GLfixed* point, int x, int y) const; + Region transform(const Region& reg) const; + Rect transform(const Rect& bounds) const; + + Transform operator * (const Transform& rhs) const; + float operator [] (int i) const; + + inline uint32_t getType() const { return type(); } + + inline Transform(bool) : mType(0xFF) { }; + +private: + uint8_t type() const; + +private: + SkMatrix mTransform; + mutable uint32_t mType; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif /* ANDROID_TRANSFORM_H */ diff --git a/libs/surfaceflinger/VRamHeap.cpp b/libs/surfaceflinger/VRamHeap.cpp new file mode 100644 index 000000000..0ccd71f0b --- /dev/null +++ b/libs/surfaceflinger/VRamHeap.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2008 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. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "GPUHardware/GPUHardware.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" + +#if HAVE_ANDROID_OS +#include +#endif + + +namespace android { + +// --------------------------------------------------------------------------- + +/* + * Amount of memory we reserve for surface, per client in PMEM + * (PMEM is used for 2D acceleration) + * 8 MB of address space per client should be enough. + */ +static const int PMEM_SIZE = int(8 * 1024 * 1024); + +int SurfaceHeapManager::global_pmem_heap = 0; + +// --------------------------------------------------------------------------- + +SurfaceHeapManager::SurfaceHeapManager(const sp& flinger, + size_t clientHeapSize) + : mFlinger(flinger), mClientHeapSize(clientHeapSize) +{ + SurfaceHeapManager::global_pmem_heap = 1; +} + +SurfaceHeapManager::~SurfaceHeapManager() +{ +} + +void SurfaceHeapManager::onFirstRef() +{ + if (global_pmem_heap) { + const char* device = "/dev/pmem"; + mPMemHeap = new PMemHeap(device, PMEM_SIZE); + if (mPMemHeap->base() == MAP_FAILED) { + mPMemHeap.clear(); + global_pmem_heap = 0; + } + } +} + +sp SurfaceHeapManager::createHeap( + uint32_t flags, + pid_t client_pid, + const sp& defaultAllocator) +{ + sp dealer; + + if (flags & ISurfaceComposer::eGPU) { + // don't grant GPU memory if GPU is disabled + char value[PROPERTY_VALUE_MAX]; + property_get("debug.egl.hw", value, "1"); + if (atoi(value) == 0) { + flags &= ~ISurfaceComposer::eGPU; + } + } + + if (flags & ISurfaceComposer::eGPU) { + // FIXME: this is msm7201A specific, where gpu surfaces may not be secure + if (!(flags & ISurfaceComposer::eSecure)) { + // if GPU doesn't work, we try eHardware + flags |= ISurfaceComposer::eHardware; + // asked for GPU memory, try that first + dealer = mFlinger->getGPU()->request(client_pid); + } + } + + if (dealer == NULL) { + if (defaultAllocator != NULL) + // if a default allocator is given, use that + dealer = defaultAllocator; + } + + if (dealer == NULL) { + // always try h/w accelerated memory first + if (global_pmem_heap) { + const sp& heap(mPMemHeap); + if (dealer == NULL && heap != NULL) { + dealer = new MemoryDealer( + heap->createClientHeap(), + heap->getAllocator()); + } + } + } + + if (dealer == NULL) { + // return the ashmem allocator (software rendering) + dealer = new MemoryDealer(mClientHeapSize, 0, "SFNativeHeap"); + } + return dealer; +} + +sp SurfaceHeapManager::getAllocator(int type) const +{ + Mutex::Autolock _l(mLock); + sp allocator; + + // this is only used for debugging + switch (type) { + case NATIVE_MEMORY_TYPE_PMEM: + if (mPMemHeap != 0) { + allocator = mPMemHeap->getAllocator(); + } + break; + } + return allocator; +} + +// --------------------------------------------------------------------------- + +PMemHeap::PMemHeap(const char* const device, size_t size, size_t reserved) + : MemoryHeapBase(device, size) +{ + //LOGD("%s, %p, mFD=%d", __PRETTY_FUNCTION__, this, heapID()); + if (base() != MAP_FAILED) { + //LOGD("%s, %u bytes", device, virtualSize()); + if (reserved == 0) + reserved = virtualSize(); + mAllocator = new SimpleBestFitAllocator(reserved); + } +} + +PMemHeap::~PMemHeap() { + //LOGD("%s, %p, mFD=%d", __PRETTY_FUNCTION__, this, heapID()); +} + +sp PMemHeap::createClientHeap() { + sp parentHeap(this); + return new MemoryHeapPmem(parentHeap); +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/surfaceflinger/VRamHeap.h b/libs/surfaceflinger/VRamHeap.h new file mode 100644 index 000000000..91401679c --- /dev/null +++ b/libs/surfaceflinger/VRamHeap.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_VRAM_HEAP_H +#define ANDROID_VRAM_HEAP_H + +#include +#include +#include + +namespace android { + +// --------------------------------------------------------------------------- + +class PMemHeap; +class MemoryHeapPmem; +class SurfaceFlinger; + +// --------------------------------------------------------------------------- + +class SurfaceHeapManager : public RefBase +{ +public: + SurfaceHeapManager(const sp& flinger, size_t clientHeapSize); + virtual ~SurfaceHeapManager(); + virtual void onFirstRef(); + /* use ISurfaceComposer flags eGPU|eHArdware|eSecure */ + sp createHeap(uint32_t flags=0, pid_t client_pid = 0, + const sp& defaultAllocator = 0); + + // used for debugging only... + sp getAllocator(int type) const; + +private: + sp getHeap(int type) const; + + sp mFlinger; + mutable Mutex mLock; + size_t mClientHeapSize; + sp mPMemHeap; + static int global_pmem_heap; +}; + +// --------------------------------------------------------------------------- + +class PMemHeap : public MemoryHeapBase +{ +public: + PMemHeap(const char* const vram, + size_t size=0, size_t reserved=0); + virtual ~PMemHeap(); + + virtual const sp& getAllocator() const { + return mAllocator; + } + virtual sp createClientHeap(); + +private: + sp mAllocator; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_VRAM_HEAP_H diff --git a/libs/surfaceflinger/clz.cpp b/libs/surfaceflinger/clz.cpp new file mode 100644 index 000000000..2456b86ac --- /dev/null +++ b/libs/surfaceflinger/clz.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2007 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 "clz.h" + +namespace android { + +int clz_impl(int32_t x) +{ +#if defined(__arm__) && !defined(__thumb__) + return __builtin_clz(x); +#else + if (!x) return 32; + int e = 31; + if (x&0xFFFF0000) { e -=16; x >>=16; } + if (x&0x0000FF00) { e -= 8; x >>= 8; } + if (x&0x000000F0) { e -= 4; x >>= 4; } + if (x&0x0000000C) { e -= 2; x >>= 2; } + if (x&0x00000002) { e -= 1; } + return e; +#endif +} + +}; // namespace android diff --git a/libs/surfaceflinger/clz.h b/libs/surfaceflinger/clz.h new file mode 100644 index 000000000..0ddf986bf --- /dev/null +++ b/libs/surfaceflinger/clz.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_SURFACE_FLINGER_CLZ_H + +#include + +namespace android { + +int clz_impl(int32_t x); + +int inline clz(int32_t x) +{ +#if defined(__arm__) && !defined(__thumb__) + return __builtin_clz(x); +#else + return clz_impl(x); +#endif +} + + +}; // namespace android + +#endif /* ANDROID_SURFACE_FLINGER_CLZ_H */ diff --git a/libs/surfaceflinger/tests/Android.mk b/libs/surfaceflinger/tests/Android.mk new file mode 100644 index 000000000..5053e7d64 --- /dev/null +++ b/libs/surfaceflinger/tests/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/libs/surfaceflinger/tests/overlays/Android.mk b/libs/surfaceflinger/tests/overlays/Android.mk new file mode 100644 index 000000000..dc47e455a --- /dev/null +++ b/libs/surfaceflinger/tests/overlays/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + overlays.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui + +LOCAL_MODULE:= test-overlays + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libs/surfaceflinger/tests/overlays/overlays.cpp b/libs/surfaceflinger/tests/overlays/overlays.cpp new file mode 100644 index 000000000..f3c046fce --- /dev/null +++ b/libs/surfaceflinger/tests/overlays/overlays.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace android; + +namespace android { +class Test { +public: + static const sp& getISurface(const sp& s) { + return s->getISurface(); + } +}; +}; + +int main(int argc, char** argv) +{ + // set up the thread-pool + sp proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + // create a client to surfaceflinger + sp client = new SurfaceComposerClient(); + + // create pushbuffer surface + sp surface = client->createSurface(getpid(), 0, 320, 240, + PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers); + + // get to the isurface + sp isurface = Test::getISurface(surface); + printf("isurface = %p\n", isurface.get()); + + // now request an overlay + sp ref = isurface->createOverlay(320, 240, PIXEL_FORMAT_RGB_565); + sp overlay = new Overlay(ref); + + + /* + * here we can use the overlay API + */ + + overlay_buffer_t buffer; + overlay->dequeueBuffer(&buffer); + printf("buffer = %p\n", buffer); + + void* address = overlay->getBufferAddress(buffer); + printf("address = %p\n", address); + + overlay->queueBuffer(buffer); + + return 0; +} diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk new file mode 100644 index 000000000..f9443579a --- /dev/null +++ b/libs/ui/Android.mk @@ -0,0 +1,41 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + Camera.cpp \ + CameraParameters.cpp \ + EGLDisplaySurface.cpp \ + EGLNativeWindowSurface.cpp \ + EventHub.cpp \ + EventRecurrence.cpp \ + KeyLayoutMap.cpp \ + KeyCharacterMap.cpp \ + ICamera.cpp \ + ICameraClient.cpp \ + ICameraService.cpp \ + IOverlay.cpp \ + ISurfaceComposer.cpp \ + ISurface.cpp \ + ISurfaceFlingerClient.cpp \ + LayerState.cpp \ + Overlay.cpp \ + PixelFormat.cpp \ + Point.cpp \ + Rect.cpp \ + Region.cpp \ + Surface.cpp \ + SurfaceComposerClient.cpp \ + SurfaceFlingerSynchro.cpp \ + Time.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcorecg \ + libcutils \ + libutils \ + libpixelflinger \ + libhardware \ + libhardware_legacy + +LOCAL_MODULE:= libui + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp new file mode 100644 index 000000000..b3cbda14f --- /dev/null +++ b/libs/ui/Camera.cpp @@ -0,0 +1,408 @@ +/* +** +** Copyright (C) 2008, The Android Open Source Project +** Copyright (C) 2008 HTC Inc. +** +** 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. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Camera" +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +// client singleton for camera service binder interface +Mutex Camera::mLock; +sp Camera::mCameraService; +sp Camera::mDeathNotifier; + +// establish binder interface to camera service +const sp& Camera::getCameraService() +{ + Mutex::Autolock _l(mLock); + if (mCameraService.get() == 0) { + sp sm = defaultServiceManager(); + sp binder; + do { + binder = sm->getService(String16("media.camera")); + if (binder != 0) + break; + LOGW("CameraService not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + if (mDeathNotifier == NULL) { + mDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(mDeathNotifier); + mCameraService = interface_cast(binder); + } + LOGE_IF(mCameraService==0, "no CameraService!?"); + return mCameraService; +} + +// --------------------------------------------------------------------------- + +Camera::Camera() +{ + init(); +} + +Camera::Camera(const sp& camera) +{ + init(); + // connect this client to existing camera remote + if (camera->connect(this) == NO_ERROR) { + mStatus = NO_ERROR; + mCamera = camera; + camera->asBinder()->linkToDeath(this); + } +} + +void Camera::init() +{ + mStatus = UNKNOWN_ERROR; + mShutterCallback = 0; + mShutterCallbackCookie = 0; + mRawCallback = 0; + mRawCallbackCookie = 0; + mJpegCallback = 0; + mJpegCallbackCookie = 0; + mPreviewCallback = 0; + mPreviewCallbackCookie = 0; + mRecordingCallback = 0; + mRecordingCallbackCookie = 0; + mErrorCallback = 0; + mErrorCallbackCookie = 0; + mAutoFocusCallback = 0; + mAutoFocusCallbackCookie = 0; +} + +Camera::~Camera() +{ + disconnect(); +} + +sp Camera::connect() +{ + LOGV("connect"); + sp c = new Camera(); + const sp& cs = getCameraService(); + if (cs != 0) { + c->mCamera = cs->connect(c); + } + if (c->mCamera != 0) { + c->mCamera->asBinder()->linkToDeath(c); + c->mStatus = NO_ERROR; + } else { + c.clear(); + } + return c; +} + +void Camera::disconnect() +{ + LOGV("disconnect"); + if (mCamera != 0) { + mErrorCallback = 0; + mCamera->disconnect(); + mCamera = 0; + } +} + +status_t Camera::reconnect() +{ + LOGV("reconnect"); + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->connect(this); +} + +sp Camera::remote() +{ + return mCamera; +} + +status_t Camera::lock() +{ + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->lock(); +} + +status_t Camera::unlock() +{ + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->unlock(); +} + +// pass the buffered ISurface to the camera service +status_t Camera::setPreviewDisplay(const sp& surface) +{ + LOGV("setPreviewDisplay"); + if (surface == 0) { + LOGE("app passed NULL surface"); + return NO_INIT; + } + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->setPreviewDisplay(surface->getISurface()); +} + +status_t Camera::setPreviewDisplay(const sp& surface) +{ + LOGV("setPreviewDisplay"); + if (surface == 0) { + LOGE("app passed NULL surface"); + return NO_INIT; + } + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->setPreviewDisplay(surface); +} + + +// start preview mode, must call setPreviewDisplay first +status_t Camera::startPreview() +{ + LOGV("startPreview"); + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->startPreview(); +} + +// start recording mode, must call setPreviewDisplay first +status_t Camera::startRecording() +{ + LOGV("startRecording"); + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->startRecording(); +} + +// stop preview mode +void Camera::stopPreview() +{ + LOGV("stopPreview"); + sp c = mCamera; + if (c == 0) return; + c->stopPreview(); +} + +// stop recording mode +void Camera::stopRecording() +{ + LOGV("stopRecording"); + sp c = mCamera; + if (c == 0) return; + c->stopRecording(); +} + +// release a recording frame +void Camera::releaseRecordingFrame(const sp& mem) +{ + LOGV("releaseRecordingFrame"); + sp c = mCamera; + if (c == 0) return; + c->releaseRecordingFrame(mem); +} + +// get preview state +bool Camera::previewEnabled() +{ + LOGV("previewEnabled"); + sp c = mCamera; + if (c == 0) return false; + return c->previewEnabled(); +} + +// get recording state +bool Camera::recordingEnabled() +{ + LOGV("recordingEnabled"); + sp c = mCamera; + if (c == 0) return false; + return c->recordingEnabled(); +} + +status_t Camera::autoFocus() +{ + LOGV("autoFocus"); + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->autoFocus(); +} + +// take a picture +status_t Camera::takePicture() +{ + LOGV("takePicture"); + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->takePicture(); +} + +// set preview/capture parameters - key/value pairs +status_t Camera::setParameters(const String8& params) +{ + LOGV("setParameters"); + sp c = mCamera; + if (c == 0) return NO_INIT; + return c->setParameters(params); +} + +// get preview/capture parameters - key/value pairs +String8 Camera::getParameters() const +{ + LOGV("getParameters"); + String8 params; + sp c = mCamera; + if (c != 0) params = mCamera->getParameters(); + return params; +} + +void Camera::setAutoFocusCallback(autofocus_callback cb, void *cookie) +{ + LOGV("setAutoFocusCallback"); + mAutoFocusCallback = cb; + mAutoFocusCallbackCookie = cookie; +} + +void Camera::setShutterCallback(shutter_callback cb, void *cookie) +{ + LOGV("setShutterCallback"); + mShutterCallback = cb; + mShutterCallbackCookie = cookie; +} + +void Camera::setRawCallback(frame_callback cb, void *cookie) +{ + LOGV("setRawCallback"); + mRawCallback = cb; + mRawCallbackCookie = cookie; +} + +void Camera::setJpegCallback(frame_callback cb, void *cookie) +{ + LOGV("setJpegCallback"); + mJpegCallback = cb; + mJpegCallbackCookie = cookie; +} + +void Camera::setPreviewCallback(frame_callback cb, void *cookie, int flag) +{ + LOGV("setPreviewCallback"); + mPreviewCallback = cb; + mPreviewCallbackCookie = cookie; + sp c = mCamera; + if (c == 0) return; + mCamera->setPreviewCallbackFlag(flag); +} + +void Camera::setRecordingCallback(frame_callback cb, void *cookie) +{ + LOGV("setRecordingCallback"); + mRecordingCallback = cb; + mRecordingCallbackCookie = cookie; +} + +void Camera::setErrorCallback(error_callback cb, void *cookie) +{ + LOGV("setErrorCallback"); + mErrorCallback = cb; + mErrorCallbackCookie = cookie; +} + +void Camera::autoFocusCallback(bool focused) +{ + LOGV("autoFocusCallback"); + if (mAutoFocusCallback) { + mAutoFocusCallback(focused, mAutoFocusCallbackCookie); + } +} + +void Camera::shutterCallback() +{ + LOGV("shutterCallback"); + if (mShutterCallback) { + mShutterCallback(mShutterCallbackCookie); + } +} + +void Camera::rawCallback(const sp& picture) +{ + LOGV("rawCallback"); + if (mRawCallback) { + mRawCallback(picture, mRawCallbackCookie); + } +} + +// callback from camera service when image is ready +void Camera::jpegCallback(const sp& picture) +{ + LOGV("jpegCallback"); + if (mJpegCallback) { + mJpegCallback(picture, mJpegCallbackCookie); + } +} + +// callback from camera service when preview frame is ready +void Camera::previewCallback(const sp& frame) +{ + LOGV("frameCallback"); + if (mPreviewCallback) { + mPreviewCallback(frame, mPreviewCallbackCookie); + } +} + +// callback from camera service when a recording frame is ready +void Camera::recordingCallback(const sp& frame) +{ + LOGV("recordingCallback"); + if (mRecordingCallback) { + mRecordingCallback(frame, mRecordingCallbackCookie); + } +} + +// callback from camera service when an error occurs in preview or takePicture +void Camera::errorCallback(status_t error) +{ + LOGV("errorCallback"); + if (mErrorCallback) { + mErrorCallback(error, mErrorCallbackCookie); + } +} + +void Camera::binderDied(const wp& who) { + LOGW("ICamera died"); + if (mErrorCallback) { + mErrorCallback(DEAD_OBJECT, mErrorCallbackCookie); + } +} + +void Camera::DeathNotifier::binderDied(const wp& who) { + LOGV("binderDied"); + Mutex::Autolock _l(Camera::mLock); + Camera::mCameraService.clear(); + LOGW("Camera server died!"); +} + +}; // namespace android + diff --git a/libs/ui/CameraParameters.cpp b/libs/ui/CameraParameters.cpp new file mode 100644 index 000000000..6c258366f --- /dev/null +++ b/libs/ui/CameraParameters.cpp @@ -0,0 +1,273 @@ +/* +** +** Copyright 2008, 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. +*/ + +#define LOG_TAG "CameraParams" +#include + +#include +#include +#include + +namespace android { + +static const char* portrait = "portrait"; +static const char* landscape = "landscape"; + +CameraParameters::CameraParameters() + : mMap() +{ +} + +CameraParameters::~CameraParameters() +{ +} + +String8 CameraParameters::flatten() const +{ + String8 flattened(""); + size_t size = mMap.size(); + + for (size_t i = 0; i < size; i++) { + String8 k, v; + k = mMap.keyAt(i); + v = mMap.valueAt(i); + + flattened += k; + flattened += "="; + flattened += v; + if (i != size-1) + flattened += ";"; + } + + return flattened; +} + +void CameraParameters::unflatten(const String8 ¶ms) +{ + const char *a = params.string(); + const char *b; + + mMap.clear(); + + for (;;) { + // Find the bounds of the key name. + b = strchr(a, '='); + if (b == 0) + break; + + // Create the key string. + String8 k(a, (size_t)(b-a)); + + // Find the value. + a = b+1; + b = strchr(a, ';'); + if (b == 0) { + // If there's no semicolon, this is the last item. + String8 v(a); + mMap.add(k, v); + break; + } + + String8 v(a, (size_t)(b-a)); + mMap.add(k, v); + a = b+1; + } +} + + +void CameraParameters::set(const char *key, const char *value) +{ + // XXX i think i can do this with strspn() + if (strchr(key, '=') || strchr(key, ';')) { + //XXX LOGE("Key \"%s\"contains invalid character (= or ;)", key); + return; + } + + if (strchr(value, '=') || strchr(key, ';')) { + //XXX LOGE("Value \"%s\"contains invalid character (= or ;)", value); + return; + } + + mMap.replaceValueFor(String8(key), String8(value)); +} + +void CameraParameters::set(const char *key, int value) +{ + char str[16]; + sprintf(str, "%d", value); + set(key, str); +} + +const char *CameraParameters::get(const char *key) const +{ + String8 v = mMap.valueFor(String8(key)); + if (v.length() == 0) + return 0; + return v.string(); +} + +int CameraParameters::getInt(const char *key) const +{ + const char *v = get(key); + if (v == 0) + return -1; + return strtol(v, 0, 0); +} + +static int parse_size(const char *str, int &width, int &height) +{ + // Find the width. + char *end; + int w = (int)strtol(str, &end, 10); + // If an 'x' does not immediately follow, give up. + if (*end != 'x') + return -1; + + // Find the height, immediately after the 'x'. + int h = (int)strtol(end+1, 0, 10); + + width = w; + height = h; + + return 0; +} + +void CameraParameters::setPreviewSize(int width, int height) +{ + char str[32]; + sprintf(str, "%dx%d", width, height); + set("preview-size", str); +} + +void CameraParameters::getPreviewSize(int *width, int *height) const +{ + *width = -1; + *height = -1; + + // Get the current string, if it doesn't exist, leave the -1x-1 + const char *p = get("preview-size"); + if (p == 0) + return; + + int w, h; + if (parse_size(p, w, h) == 0) { + *width = w; + *height = h; + } +} + +void CameraParameters::setPreviewFrameRate(int fps) +{ + set("preview-frame-rate", fps); +} + +int CameraParameters::getPreviewFrameRate() const +{ + return getInt("preview-frame-rate"); +} + +void CameraParameters::setPreviewFormat(const char *format) +{ + set("preview-format", format); +} + +int CameraParameters::getOrientation() const +{ + const char* orientation = get("orientation"); + if (orientation && !strcmp(orientation, portrait)) + return CAMERA_ORIENTATION_PORTRAIT; + return CAMERA_ORIENTATION_LANDSCAPE; +} + +void CameraParameters::setOrientation(int orientation) +{ + if (orientation == CAMERA_ORIENTATION_PORTRAIT) { + set("preview-format", portrait); + } else { + set("preview-format", landscape); + } +} + +const char *CameraParameters::getPreviewFormat() const +{ + return get("preview-format"); +} + +void CameraParameters::setPictureSize(int width, int height) +{ + char str[32]; + sprintf(str, "%dx%d", width, height); + set("picture-size", str); +} + +void CameraParameters::getPictureSize(int *width, int *height) const +{ + *width = -1; + *height = -1; + + // Get the current string, if it doesn't exist, leave the -1x-1 + const char *p = get("picture-size"); + if (p == 0) + return; + + int w, h; + if (parse_size(p, w, h) == 0) { + *width = w; + *height = h; + } +} + +void CameraParameters::setPictureFormat(const char *format) +{ + set("picture-format", format); +} + +const char *CameraParameters::getPictureFormat() const +{ + return get("picture-format"); +} + +void CameraParameters::dump() const +{ + LOGD("dump: mMap.size = %d", mMap.size()); + for (size_t i = 0; i < mMap.size(); i++) { + String8 k, v; + k = mMap.keyAt(i); + v = mMap.valueAt(i); + LOGD("%s: %s\n", k.string(), v.string()); + } +} + +status_t CameraParameters::dump(int fd, const Vector& args) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, 255, "CameraParameters::dump: mMap.size = %d\n", mMap.size()); + result.append(buffer); + for (size_t i = 0; i < mMap.size(); i++) { + String8 k, v; + k = mMap.keyAt(i); + v = mMap.valueAt(i); + snprintf(buffer, 255, "\t%s: %s\n", k.string(), v.string()); + result.append(buffer); + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +}; // namespace android diff --git a/libs/ui/EGLDisplaySurface.cpp b/libs/ui/EGLDisplaySurface.cpp new file mode 100644 index 000000000..d06c98b9b --- /dev/null +++ b/libs/ui/EGLDisplaySurface.cpp @@ -0,0 +1,519 @@ +/* + ** + ** Copyright 2007 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. + */ + +#define LOG_TAG "EGLDisplaySurface" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#if HAVE_ANDROID_OS +#include +#endif + +#include + +#include + + +// ---------------------------------------------------------------------------- + +egl_native_window_t* android_createDisplaySurface() +{ + egl_native_window_t* s = new android::EGLDisplaySurface(); + s->memory_type = NATIVE_MEMORY_TYPE_GPU; + return s; +} + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +EGLDisplaySurface::EGLDisplaySurface() + : EGLNativeSurface() +{ + egl_native_window_t::version = sizeof(egl_native_window_t); + egl_native_window_t::ident = 0; + egl_native_window_t::incRef = &EGLDisplaySurface::hook_incRef; + egl_native_window_t::decRef = &EGLDisplaySurface::hook_decRef; + egl_native_window_t::swapBuffers = &EGLDisplaySurface::hook_swapBuffers; + egl_native_window_t::connect = 0; + egl_native_window_t::disconnect = 0; + + mFb[0].data = 0; + mFb[1].data = 0; + mBlitEngine = 0; + egl_native_window_t::fd = mapFrameBuffer(); + if (egl_native_window_t::fd >= 0) { + + hw_module_t const* module; + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &mBlitEngine); + } + + const float in2mm = 25.4f; + float refreshRate = 1000000000000000LLU / ( + float( mInfo.upper_margin + mInfo.lower_margin + mInfo.yres ) + * ( mInfo.left_margin + mInfo.right_margin + mInfo.xres ) + * mInfo.pixclock); + + const GGLSurface& buffer = mFb[1 - mIndex]; + egl_native_window_t::width = buffer.width; + egl_native_window_t::height = buffer.height; + egl_native_window_t::stride = buffer.stride; + egl_native_window_t::format = buffer.format; + egl_native_window_t::base = intptr_t(mFb[0].data); + egl_native_window_t::offset = + intptr_t(buffer.data) - egl_native_window_t::base; + egl_native_window_t::flags = 0; + egl_native_window_t::xdpi = (mInfo.xres * in2mm) / mInfo.width; + egl_native_window_t::ydpi = (mInfo.yres * in2mm) / mInfo.height; + egl_native_window_t::fps = refreshRate; + egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_FB; + // no error, set the magic word + egl_native_window_t::magic = 0x600913; + } + mSwapCount = -1; + mPageFlipCount = 0; +} + +EGLDisplaySurface::~EGLDisplaySurface() +{ + magic = 0; + copybit_close(mBlitEngine); + mBlitEngine = 0; + close(egl_native_window_t::fd); + munmap(mFb[0].data, mSize); + if (!(mFlags & PAGE_FLIP)) + free((void*)mFb[1].data); +} + +void EGLDisplaySurface::hook_incRef(NativeWindowType window) { + EGLDisplaySurface* that = static_cast(window); + that->incStrong(that); +} +void EGLDisplaySurface::hook_decRef(NativeWindowType window) { + EGLDisplaySurface* that = static_cast(window); + that->decStrong(that); +} +uint32_t EGLDisplaySurface::hook_swapBuffers(NativeWindowType window) { + EGLDisplaySurface* that = static_cast(window); + return that->swapBuffers(); +} + +void EGLDisplaySurface::setSwapRectangle(int l, int t, int w, int h) +{ + mInfo.reserved[0] = 0x54445055; // "UPDT"; + mInfo.reserved[1] = (uint16_t)l | ((uint32_t)t << 16); + mInfo.reserved[2] = (uint16_t)(l+w) | ((uint32_t)(t+h) << 16); +} + +uint32_t EGLDisplaySurface::swapBuffers() +{ +#define SHOW_FPS 0 +#if SHOW_FPS + nsecs_t now = systemTime(); + if (mSwapCount == -1) { + mTime = now; + mSwapCount = 0; + mSleep = 0; + } else { + nsecs_t d = now-mTime; + if (d >= seconds(1)) { + double fps = (mSwapCount * double(seconds(1))) / double(d); + LOGD("%f fps, sleep=%d / frame", + fps, (int)ns2us(mSleep / mSwapCount)); + mSwapCount = 0; + mTime = now; + mSleep = 0; + } else { + mSwapCount++; + } + } +#endif + /* If we can't do the page_flip, just copy the back buffer to the front */ + if (!(mFlags & PAGE_FLIP)) { + memcpy(mFb[0].data, mFb[1].data, mInfo.xres*mInfo.yres*2); + return 0; + } + + // do the actual flip + mIndex = 1 - mIndex; + mInfo.activate = FB_ACTIVATE_VBL; + mInfo.yoffset = mIndex ? mInfo.yres : 0; + if (ioctl(egl_native_window_t::fd, FBIOPUT_VSCREENINFO, &mInfo) == -1) { + LOGE("FBIOPUT_VSCREENINFO failed"); + return 0; + } + + /* + * this is a monstrous hack: Because the h/w accelerator is not able + * to render directly into the framebuffer, we need to copy its + * internal framebuffer out to the fb. + * oem[0] is used to access the fd of internal fb. + * All this is needed only in standalone mode, in SurfaceFlinger mode + * we control where the GPU renders. + * We do this only if we have copybit, since this hack is needed only + * with msm7k. + */ + if (egl_native_window_t::memory_type == NATIVE_MEMORY_TYPE_GPU && oem[0] && mBlitEngine) { + copybit_device_t *copybit = mBlitEngine; + copybit_rect_t sdrect = { 0, 0, + egl_native_window_t::width, egl_native_window_t::height }; + copybit_image_t dst = { + egl_native_window_t::width, + egl_native_window_t::height, + egl_native_window_t::format, + egl_native_window_t::offset, + (void*)egl_native_window_t::base, + egl_native_window_t::fd + }; + copybit_image_t src = { + egl_native_window_t::width, + egl_native_window_t::height, + egl_native_window_t::format, // XXX: use proper format + egl_native_window_t::offset, + (void*)egl_native_window_t::base, // XXX: use proper base + egl_native_window_t::oem[0] + }; + region_iterator it(Region(Rect( + egl_native_window_t::width, egl_native_window_t::height))); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); + copybit->stretch(copybit, &dst, &src, &sdrect, &sdrect, &it); + } + + // update the address of the buffer to draw to next + const GGLSurface& buffer = mFb[1 - mIndex]; + egl_native_window_t::offset = + intptr_t(buffer.data) - egl_native_window_t::base; + +#if SHOW_FPS + mSleep += systemTime()-now; +#endif + + mPageFlipCount++; + + // We don't support screen-size changes for now + return 0; +} + +int32_t EGLDisplaySurface::getPageFlipCount() const +{ + return mPageFlipCount; +} + +void EGLDisplaySurface::copyFrontToBack(const Region& copyback) +{ +#if HAVE_ANDROID_OS + if (mBlitEngine) { + copybit_image_t dst = { + w: egl_native_window_t::stride, + h: egl_native_window_t::height, + format: egl_native_window_t::format, + offset: mFb[1-mIndex].data - mFb[0].data, + base: (void*)egl_native_window_t::base, + fd: egl_native_window_t::fd + }; + copybit_image_t src = { + w: egl_native_window_t::stride, + h: egl_native_window_t::height, + format: egl_native_window_t::format, + offset: mFb[mIndex].data - mFb[0].data, + base: (void*)egl_native_window_t::base, + fd: egl_native_window_t::fd + }; + region_iterator it(copyback); + mBlitEngine->blit(mBlitEngine, &dst, &src, &it); + } else +#endif + { + /* no extra copy needed since we copied back to front instead of + * flipping */ + if (!(mFlags & PAGE_FLIP)) { + return; + } + + Region::iterator iterator(copyback); + if (iterator) { + Rect r; + uint8_t* const screen_src = mFb[ mIndex].data; + uint8_t* const screen_dst = mFb[1-mIndex].data; + const size_t bpp = bytesPerPixel(egl_native_window_t::format); + const size_t bpr = egl_native_window_t::stride * bpp; + while (iterator.iterate(&r)) { + ssize_t h = r.bottom - r.top; + if (h) { + size_t size = (r.right - r.left) * bpp; + size_t o = (r.left + egl_native_window_t::stride * r.top) * bpp; + uint8_t* s = screen_src + o; + uint8_t* d = screen_dst + o; + if (size == bpr) { + size *= h; + h = 1; + } + do { + memcpy(d, s, size); + d += bpr; + s += bpr; + } while (--h > 0); + } + } + } + } +} + +void EGLDisplaySurface::copyFrontToImage(const copybit_image_t& dst) +{ +#if HAVE_ANDROID_OS + if (mBlitEngine) { + copybit_image_t src = { + w: egl_native_window_t::stride, + h: egl_native_window_t::height, + format: egl_native_window_t::format, + offset: mFb[mIndex].data - mFb[0].data, + base: (void*)egl_native_window_t::base, + fd: egl_native_window_t::fd + }; + region_iterator it(Region(Rect( + egl_native_window_t::width, egl_native_window_t::height))); + mBlitEngine->blit(mBlitEngine, &dst, &src, &it); + } else +#endif + { + uint8_t* const screen_src = mFb[ mIndex].data; + const size_t bpp = bytesPerPixel(egl_native_window_t::format); + const size_t bpr = egl_native_window_t::stride * bpp; + memcpy((char*)dst.base + dst.offset, screen_src, + bpr*egl_native_window_t::height); + } +} + +void EGLDisplaySurface::copyBackToImage(const copybit_image_t& dst) +{ +#if HAVE_ANDROID_OS + if (mBlitEngine) { + copybit_image_t src = { + w: egl_native_window_t::stride, + h: egl_native_window_t::height, + format: egl_native_window_t::format, + offset: mFb[1-mIndex].data - mFb[0].data, + base: (void*)egl_native_window_t::base, + fd: egl_native_window_t::fd + }; + region_iterator it(Region(Rect( + egl_native_window_t::width, egl_native_window_t::height))); + mBlitEngine->blit(mBlitEngine, &dst, &src, &it); + } else +#endif + { + uint8_t* const screen_src = mFb[1-mIndex].data; + const size_t bpp = bytesPerPixel(egl_native_window_t::format); + const size_t bpr = egl_native_window_t::stride * bpp; + memcpy((char*)dst.base + dst.offset, screen_src, + bpr*egl_native_window_t::height); + } +} + + +status_t EGLDisplaySurface::mapFrameBuffer() +{ + char const * const device_template[] = { + "/dev/graphics/fb%u", + "/dev/fb%u", + 0 }; + int fd = -1; + int i=0; + char name[64]; + while ((fd==-1) && device_template[i]) { + snprintf(name, 64, device_template[i], 0); + fd = open(name, O_RDWR, 0); + i++; + } + if (fd < 0) + return -errno; + + struct fb_fix_screeninfo finfo; + if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) + return -errno; + + struct fb_var_screeninfo info; + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) + return -errno; + + info.reserved[0] = 0; + info.reserved[1] = 0; + info.reserved[2] = 0; + info.xoffset = 0; + info.yoffset = 0; + info.yres_virtual = info.yres * 2; + info.bits_per_pixel = 16; + /* Explicitly request 5/6/5 */ + info.red.offset = 11; + info.red.length = 5; + info.green.offset = 5; + info.green.length = 6; + info.blue.offset = 0; + info.blue.length = 5; + info.transp.offset = 0; + info.transp.length = 0; + info.activate = FB_ACTIVATE_NOW; + + uint32_t flags = PAGE_FLIP; + if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { + info.yres_virtual = info.yres; + flags &= ~PAGE_FLIP; + LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); + } + + if (info.yres_virtual < info.yres * 2) { + info.yres_virtual = info.yres; + flags &= ~PAGE_FLIP; + LOGW("page flipping not supported (yres_virtual=%d, requested=%d)", + info.yres_virtual, info.yres*2); + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) + return -errno; + + int refreshRate = 1000000000000000LLU / + ( + uint64_t( info.upper_margin + info.lower_margin + info.yres ) + * ( info.left_margin + info.right_margin + info.xres ) + * info.pixclock + ); + + if (refreshRate == 0) { + // bleagh, bad info from the driver + refreshRate = 60*1000; // 60 Hz + } + if (int(info.width) <= 0 || int(info.height) <= 0) { + // the driver doesn't return that information + // default to 160 dpi + info.width = ((info.xres * 25.4f)/160.0f + 0.5f); + info.height = ((info.yres * 25.4f)/160.0f + 0.5f); + } + + float xdpi = (info.xres * 25.4f) / info.width; + float ydpi = (info.yres * 25.4f) / info.height; + float fps = refreshRate / 1000.0f; + + LOGI( "using (fd=%d)\n" + "id = %s\n" + "xres = %d px\n" + "yres = %d px\n" + "xres_virtual = %d px\n" + "yres_virtual = %d px\n" + "bpp = %d\n" + "r = %2u:%u\n" + "g = %2u:%u\n" + "b = %2u:%u\n", + fd, + finfo.id, + info.xres, + info.yres, + info.xres_virtual, + info.yres_virtual, + info.bits_per_pixel, + info.red.offset, info.red.length, + info.green.offset, info.green.length, + info.blue.offset, info.blue.length + ); + + LOGI( "width = %d mm (%f dpi)\n" + "height = %d mm (%f dpi)\n" + "refresh rate = %.2f Hz\n", + info.width, xdpi, + info.height, ydpi, + fps + ); + + + if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) + return -errno; + + if (finfo.smem_len <= 0) + return -errno; + + /* + * Open and map the display. + */ + + void* buffer = (uint16_t*) mmap( + 0, finfo.smem_len, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, 0); + + if (buffer == MAP_FAILED) + return -errno; + + // at least for now, always clear the fb + memset(buffer, 0, finfo.smem_len); + + uint8_t* offscreen[2]; + offscreen[0] = (uint8_t*)buffer; + if (flags & PAGE_FLIP) { + offscreen[1] = (uint8_t*)buffer + finfo.line_length*info.yres; + } else { + offscreen[1] = (uint8_t*)malloc(finfo.smem_len); + if (offscreen[1] == 0) { + munmap(buffer, finfo.smem_len); + return NO_MEMORY; + } + } + + mFlags = flags; + mInfo = info; + mFinfo = finfo; + mSize = finfo.smem_len; + mIndex = 0; + for (int i=0 ; i<2 ; i++) { + mFb[i].version = sizeof(GGLSurface); + mFb[i].width = info.xres; + mFb[i].height = info.yres; + mFb[i].stride = finfo.line_length / (info.bits_per_pixel >> 3); + mFb[i].data = (GGLubyte*)(offscreen[i]); + mFb[i].format = GGL_PIXEL_FORMAT_RGB_565; + } + return fd; +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- diff --git a/libs/ui/EGLNativeWindowSurface.cpp b/libs/ui/EGLNativeWindowSurface.cpp new file mode 100644 index 000000000..f1071cf90 --- /dev/null +++ b/libs/ui/EGLNativeWindowSurface.cpp @@ -0,0 +1,161 @@ +/* +** +** Copyright 2007 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. +*/ + +#define LOG_TAG "EGLNativeWindowSurface" + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +#include + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +EGLNativeWindowSurface::EGLNativeWindowSurface(const sp& surface) + : EGLNativeSurface(), + mSurface(surface), mConnected(false) +{ + egl_native_window_t::magic = 0x600913; + egl_native_window_t::version = sizeof(egl_native_window_t); + egl_native_window_t::ident = 0; + egl_native_window_t::incRef = &EGLNativeWindowSurface::hook_incRef; + egl_native_window_t::decRef = &EGLNativeWindowSurface::hook_decRef; + egl_native_window_t::swapBuffers = &EGLNativeWindowSurface::hook_swapBuffers; + egl_native_window_t::connect = &EGLNativeWindowSurface::hook_connect; + egl_native_window_t::disconnect = &EGLNativeWindowSurface::hook_disconnect; + + DisplayInfo dinfo; + SurfaceComposerClient::getDisplayInfo(0, &dinfo); + egl_native_window_t::xdpi = dinfo.xdpi; + egl_native_window_t::ydpi = dinfo.ydpi; + egl_native_window_t::fps = dinfo.fps; + egl_native_window_t::flags= EGL_NATIVES_FLAG_DESTROY_BACKBUFFER; +} + +EGLNativeWindowSurface::~EGLNativeWindowSurface() +{ + disconnect(); + mSurface.clear(); + magic = 0; +} + +void EGLNativeWindowSurface::hook_incRef(NativeWindowType window) +{ + EGLNativeWindowSurface* that = static_cast(window); + that->incStrong(that); +} + +void EGLNativeWindowSurface::hook_decRef(NativeWindowType window) +{ + EGLNativeWindowSurface* that = static_cast(window); + that->decStrong(that); +} + +void EGLNativeWindowSurface::hook_connect(NativeWindowType window) +{ + EGLNativeWindowSurface* that = static_cast(window); + that->connect(); +} + +void EGLNativeWindowSurface::hook_disconnect(NativeWindowType window) +{ + EGLNativeWindowSurface* that = static_cast(window); + that->disconnect(); +} + +uint32_t EGLNativeWindowSurface::hook_swapBuffers(NativeWindowType window) +{ + EGLNativeWindowSurface* that = static_cast(window); + return that->swapBuffers(); +} + +void EGLNativeWindowSurface::setSwapRectangle(int l, int t, int w, int h) +{ + mSurface->setSwapRectangle(Rect(l, t, l+w, t+h)); +} + +uint32_t EGLNativeWindowSurface::swapBuffers() +{ + const int w = egl_native_window_t::width; + const int h = egl_native_window_t::height; + const sp& surface(mSurface); + Surface::SurfaceInfo info; + surface->unlockAndPost(); + surface->lock(&info); + // update the address of the buffer to draw to next + egl_native_window_t::base = intptr_t(info.base); + egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base); + + // update size if it changed + if (w != int(info.w) || h != int(info.h)) { + egl_native_window_t::width = info.w; + egl_native_window_t::height = info.h; + egl_native_window_t::stride = info.bpr / bytesPerPixel(info.format); + egl_native_window_t::format = info.format; + return EGL_NATIVES_FLAG_SIZE_CHANGED; + } + return 0; +} + +void EGLNativeWindowSurface::connect() +{ + if (!mConnected) { + Surface::SurfaceInfo info; + mSurface->lock(&info); + mSurface->setSwapRectangle(Rect(info.w, info.h)); + mConnected = true; + + egl_native_window_t::width = info.w; + egl_native_window_t::height = info.h; + egl_native_window_t::stride = info.bpr / bytesPerPixel(info.format); + egl_native_window_t::format = info.format; + egl_native_window_t::base = intptr_t(info.base); + egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base); + // FIXME: egl_native_window_t::memory_type used to be set from + // mSurface, but we wanted to break this dependency. We set it to + // GPU because the software rendered doesn't care, but the h/w + // accelerator needs it. Eventually, this value should go away + // completely, since memory will be managed by OpenGL. + egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_GPU; + egl_native_window_t::fd = 0; + } +} + +void EGLNativeWindowSurface::disconnect() +{ + if (mConnected) { + mSurface->unlock(); + mConnected = false; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp new file mode 100644 index 000000000..3b29b094d --- /dev/null +++ b/libs/ui/EventHub.cpp @@ -0,0 +1,793 @@ +// +// Copyright 2005 The Android Open Source Project +// +// Handle events, like key input and vsync. +// +// The goal is to provide an optimized solution for Linux, not an +// implementation that works well across all platforms. We expect +// events to arrive on file descriptors, so that we can use a select() +// select() call to sleep. +// +// We can't select() on anything but network sockets in Windows, so we +// provide an alternative implementation of waitEvent for that platform. +// +#define LOG_TAG "EventHub" + +//#define LOG_NDEBUG 0 + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "KeyLayoutMap.h" + +#include +#include +#include +#ifdef HAVE_INOTIFY +# include +#endif +#ifdef HAVE_ANDROID_OS +# include /* not part of Linux */ +#endif +#include +#include + +/* this macro is used to tell if "bit" is set in "array" + * it selects a byte from the array, and does a boolean AND + * operation with a byte that only has the relevant bit set. + * eg. to check for the 12th bit, we do (array[1] & 1<<4) + */ +#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) + +#define ID_MASK 0x0000ffff +#define SEQ_MASK 0x7fff0000 +#define SEQ_SHIFT 16 +#define id_to_index(id) ((id&ID_MASK)+1) + +namespace android { + +static const char *WAKE_LOCK_ID = "KeyEvents"; +static const char *device_path = "/dev/input"; + +/* return the larger integer */ +static inline int max(int v1, int v2) +{ + return (v1 > v2) ? v1 : v2; +} + +EventHub::device_t::device_t(int32_t _id, const char* _path) + : id(_id), path(_path), classes(0) + , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) { +} + +EventHub::device_t::~device_t() { + delete [] keyBitmask; + delete layoutMap; +} + +EventHub::EventHub(void) + : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0) + , mDevicesById(0), mNumDevicesById(0) + , mOpeningDevices(0), mClosingDevices(0) + , mDevices(0), mFDs(0), mFDCount(0) +{ + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); +#ifdef EV_SW + memset(mSwitches, 0, sizeof(mSwitches)); +#endif +} + +/* + * Clean up. + */ +EventHub::~EventHub(void) +{ + release_wake_lock(WAKE_LOCK_ID); + // we should free stuff here... +} + +void EventHub::onFirstRef() +{ + mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; +} + +status_t EventHub::errorCheck() const +{ + return mError; +} + +String8 EventHub::getDeviceName(int32_t deviceId) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return String8(); + return device->name; +} + +uint32_t EventHub::getDeviceClasses(int32_t deviceId) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return 0; + return device->classes; +} + +int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, + int* outMaxValue, int* outFlat, int* outFuzz) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return -1; + + struct input_absinfo info; + + if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) { + LOGE("Error reading absolute controller %d for device %s fd %d\n", + axis, device->name.string(), mFDs[id_to_index(device->id)].fd); + return -1; + } + *outMinValue = info.minimum; + *outMaxValue = info.maximum; + *outFlat = info.flat; + *outFuzz = info.fuzz; + return 0; +} + +int EventHub::getSwitchState(int sw) const +{ +#ifdef EV_SW + if (sw >= 0 && sw <= SW_MAX) { + int32_t devid = mSwitches[sw]; + if (devid != 0) { + return getSwitchState(devid, sw); + } + } +#endif + return -1; +} + +int EventHub::getSwitchState(int32_t deviceId, int sw) const +{ +#ifdef EV_SW + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return -1; + + if (sw >= 0 && sw <= SW_MAX) { + uint8_t sw_bitmask[(SW_MAX+1)/8]; + memset(sw_bitmask, 0, sizeof(sw_bitmask)); + if (ioctl(mFDs[id_to_index(device->id)].fd, + EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) { + return test_bit(sw, sw_bitmask) ? 1 : 0; + } + } +#endif + + return -1; +} + +int EventHub::getScancodeState(int code) const +{ + return getScancodeState(mFirstKeyboardId, code); +} + +int EventHub::getScancodeState(int32_t deviceId, int code) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return -1; + + if (code >= 0 && code <= KEY_MAX) { + uint8_t key_bitmask[(KEY_MAX+1)/8]; + memset(key_bitmask, 0, sizeof(key_bitmask)); + if (ioctl(mFDs[id_to_index(device->id)].fd, + EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { + return test_bit(code, key_bitmask) ? 1 : 0; + } + } + + return -1; +} + +int EventHub::getKeycodeState(int code) const +{ + return getKeycodeState(mFirstKeyboardId, code); +} + +int EventHub::getKeycodeState(int32_t deviceId, int code) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL || device->layoutMap == NULL) return -1; + + Vector scanCodes; + device->layoutMap->findScancodes(code, &scanCodes); + + uint8_t key_bitmask[(KEY_MAX+1)/8]; + memset(key_bitmask, 0, sizeof(key_bitmask)); + if (ioctl(mFDs[id_to_index(device->id)].fd, + EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { + #if 0 + for (size_t i=0; i<=KEY_MAX; i++) { + LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask)); + } + #endif + const size_t N = scanCodes.size(); + for (size_t i=0; i= 0 && sc <= KEY_MAX && test_bit(sc, key_bitmask)) { + return 1; + } + } + } + + return 0; +} + +EventHub::device_t* EventHub::getDevice(int32_t deviceId) const +{ + if (deviceId == 0) deviceId = mFirstKeyboardId; + int32_t id = deviceId & ID_MASK; + if (id >= mNumDevicesById || id < 0) return NULL; + device_t* dev = mDevicesById[id].device; + if (dev->id == deviceId) { + return dev; + } + return NULL; +} + +bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, + int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, + int32_t* outValue, nsecs_t* outWhen) +{ + *outDeviceId = 0; + *outType = 0; + *outScancode = 0; + *outKeycode = 0; + *outFlags = 0; + *outValue = 0; + *outWhen = 0; + + status_t err; + + fd_set readfds; + int maxFd = -1; + int cc; + int i; + int res; + int pollres; + struct input_event iev; + + // Note that we only allow one caller to getEvent(), so don't need + // to do locking here... only when adding/removing devices. + + while(1) { + + // First, report any devices that had last been added/removed. + if (mClosingDevices != NULL) { + device_t* device = mClosingDevices; + LOGV("Reporting device closed: id=0x%x, name=%s\n", + device->id, device->path.string()); + mClosingDevices = device->next; + *outDeviceId = device->id; + if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; + *outType = DEVICE_REMOVED; + delete device; + return true; + } + if (mOpeningDevices != NULL) { + device_t* device = mOpeningDevices; + LOGV("Reporting device opened: id=0x%x, name=%s\n", + device->id, device->path.string()); + mOpeningDevices = device->next; + *outDeviceId = device->id; + if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; + *outType = DEVICE_ADDED; + return true; + } + + release_wake_lock(WAKE_LOCK_ID); + + pollres = poll(mFDs, mFDCount, -1); + + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + + if (pollres <= 0) { + if (errno != EINTR) { + LOGW("select failed (errno=%d)\n", errno); + usleep(100000); + } + continue; + } + + //printf("poll %d, returned %d\n", mFDCount, pollres); + + // mFDs[0] is used for inotify, so process regular events starting at mFDs[1] + for(i = 1; i < mFDCount; i++) { + if(mFDs[i].revents) { + LOGV("revents for %d = 0x%08x", i, mFDs[i].revents); + if(mFDs[i].revents & POLLIN) { + res = read(mFDs[i].fd, &iev, sizeof(iev)); + if (res == sizeof(iev)) { + LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", + mDevices[i]->path.string(), + (int) iev.time.tv_sec, (int) iev.time.tv_usec, + iev.type, iev.code, iev.value); + *outDeviceId = mDevices[i]->id; + if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; + *outType = iev.type; + *outScancode = iev.code; + if (iev.type == EV_KEY) { + err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags); + LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n", + iev.code, *outKeycode, *outFlags, err); + if (err != 0) { + *outKeycode = 0; + *outFlags = 0; + } + } else { + *outKeycode = iev.code; + } + *outValue = iev.value; + *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec); + return true; + } else { + if (res<0) { + LOGW("could not get event (errno=%d)", errno); + } else { + LOGE("could not get event (wrong size: %d)", res); + } + continue; + } + } + } + } + + // read_notify() will modify mFDs and mFDCount, so this must be done after + // processing all other events. + if(mFDs[0].revents & POLLIN) { + read_notify(mFDs[0].fd); + } + } +} + +/* + * Open the platform-specific input device. + */ +bool EventHub::openPlatformInput(void) +{ + /* + * Open platform-specific input device(s). + */ + int res; + + mFDCount = 1; + mFDs = (pollfd *)calloc(1, sizeof(mFDs[0])); + mDevices = (device_t **)calloc(1, sizeof(mDevices[0])); + mFDs[0].events = POLLIN; + mDevices[0] = NULL; +#ifdef HAVE_INOTIFY + mFDs[0].fd = inotify_init(); + res = inotify_add_watch(mFDs[0].fd, device_path, IN_DELETE | IN_CREATE); + if(res < 0) { + LOGE("could not add watch for %s, %s\n", device_path, strerror(errno)); + } +#else + /* + * The code in EventHub::getEvent assumes that mFDs[0] is an inotify fd. + * We allocate space for it and set it to something invalid. + */ + mFDs[0].fd = -1; +#endif + + res = scan_dir(device_path); + if(res < 0) { + LOGE("scan dir failed for %s\n", device_path); + //open_device("/dev/input/event0"); + } + + return true; +} + +/* + * Inspect the known devices to determine whether physical keys exist for the given + * framework-domain key codes. + */ +bool EventHub::hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags) { + for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { + outFlags[codeIndex] = 0; + + // check each available hardware device for support for this keycode + Vector scanCodes; + for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) { + if (mDevices[n]) { + status_t err = mDevices[n]->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes); + if (!err) { + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (test_bit(scanCodes[sc], mDevices[n]->keyBitmask)) { + outFlags[codeIndex] = 1; + break; + } + } + } + } + } + } + + return true; +} + +// ---------------------------------------------------------------------------- + +int EventHub::open_device(const char *deviceName) +{ + int version; + int fd; + struct pollfd *new_mFDs; + device_t **new_devices; + char **new_device_names; + char name[80]; + char location[80]; + char idstr[80]; + struct input_id id; + + LOGV("Opening device: %s", deviceName); + + AutoMutex _l(mLock); + + fd = open(deviceName, O_RDWR); + if(fd < 0) { + LOGE("could not open %s, %s\n", deviceName, strerror(errno)); + return -1; + } + + if(ioctl(fd, EVIOCGVERSION, &version)) { + LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno)); + return -1; + } + if(ioctl(fd, EVIOCGID, &id)) { + LOGE("could not get driver id for %s, %s\n", deviceName, strerror(errno)); + return -1; + } + name[sizeof(name) - 1] = '\0'; + location[sizeof(location) - 1] = '\0'; + idstr[sizeof(idstr) - 1] = '\0'; + if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno)); + name[0] = '\0'; + } + if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { + //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno)); + location[0] = '\0'; + } + if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { + //fprintf(stderr, "could not get idstring for %s, %s\n", deviceName, strerror(errno)); + idstr[0] = '\0'; + } + + int devid = 0; + while (devid < mNumDevicesById) { + if (mDevicesById[devid].device == NULL) { + break; + } + devid++; + } + if (devid >= mNumDevicesById) { + device_ent* new_devids = (device_ent*)realloc(mDevicesById, + sizeof(mDevicesById[0]) * (devid + 1)); + if (new_devids == NULL) { + LOGE("out of memory"); + return -1; + } + mDevicesById = new_devids; + mNumDevicesById = devid+1; + mDevicesById[devid].device = NULL; + mDevicesById[devid].seq = 0; + } + + mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<> 16, (version >> 8) & 0xff, version & 0xff); +#endif + + device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName); + if (device == NULL) { + LOGE("out of memory"); + return -1; + } + + mFDs[mFDCount].fd = fd; + mFDs[mFDCount].events = POLLIN; + + // figure out the kinds of events the device reports + uint8_t key_bitmask[(KEY_MAX+1)/8]; + memset(key_bitmask, 0, sizeof(key_bitmask)); + LOGV("Getting keys..."); + if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) { + //LOGI("MAP\n"); + //for (int i=0; i<((KEY_MAX+1)/8); i++) { + // LOGI("%d: 0x%02x\n", i, key_bitmask[i]); + //} + for (int i=0; i<((BTN_MISC+7)/8); i++) { + if (key_bitmask[i] != 0) { + device->classes |= CLASS_KEYBOARD; + // 'Q' key support = cheap test of whether this is an alpha-capable kbd + if (test_bit(KEY_Q, key_bitmask)) { + device->classes |= CLASS_ALPHAKEY; + } + break; + } + } + if ((device->classes & CLASS_KEYBOARD) != 0) { + device->keyBitmask = new uint8_t[(KEY_MAX+1)/8]; + if (device->keyBitmask != NULL) { + memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); + } else { + delete device; + LOGE("out of memory allocating key bitmask"); + return -1; + } + } + } + if (test_bit(BTN_MOUSE, key_bitmask)) { + uint8_t rel_bitmask[(REL_MAX+1)/8]; + memset(rel_bitmask, 0, sizeof(rel_bitmask)); + LOGV("Getting relative controllers..."); + if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) + { + if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) { + device->classes |= CLASS_TRACKBALL; + } + } + } + if (test_bit(BTN_TOUCH, key_bitmask)) { + uint8_t abs_bitmask[(ABS_MAX+1)/8]; + memset(abs_bitmask, 0, sizeof(abs_bitmask)); + LOGV("Getting absolute controllers..."); + if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) + { + if (test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { + device->classes |= CLASS_TOUCHSCREEN; + } + } + } + +#ifdef EV_SW + // figure out the switches this device reports + uint8_t sw_bitmask[(SW_MAX+1)/8]; + memset(sw_bitmask, 0, sizeof(sw_bitmask)); + if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) { + for (int i=0; iid, i, test_bit(i, sw_bitmask)); + if (test_bit(i, sw_bitmask)) { + if (mSwitches[i] == 0) { + mSwitches[i] = device->id; + } + } + } + } +#endif + + LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", + deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes); + + if ((device->classes&CLASS_KEYBOARD) != 0) { + char devname[101]; + char tmpfn[101]; + char keylayoutFilename[300]; + + // a more descriptive name + ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname); + devname[sizeof(devname)-1] = 0; + device->name = devname; + + // replace all the spaces with underscores + strcpy(tmpfn, devname); + for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) + *p = '_'; + + // find the .kl file we need for this device + const char* root = getenv("ANDROID_ROOT"); + snprintf(keylayoutFilename, sizeof(keylayoutFilename), + "%s/usr/keylayout/%s.kl", root, tmpfn); + bool defaultKeymap = false; + if (access(keylayoutFilename, R_OK)) { + snprintf(keylayoutFilename, sizeof(keylayoutFilename), + "%s/usr/keylayout/%s", root, "qwerty.kl"); + defaultKeymap = true; + } + device->layoutMap->load(keylayoutFilename); + + // tell the world about the devname (the descriptive name) + int32_t publicID; + if (!mHaveFirstKeyboard && !defaultKeymap) { + publicID = 0; + // the built-in keyboard has a well-known device ID of 0, + // this device better not go away. + mHaveFirstKeyboard = true; + mFirstKeyboardId = device->id; + } else { + publicID = device->id; + // ensure mFirstKeyboardId is set to -something-. + if (mFirstKeyboardId == 0) { + mFirstKeyboardId = device->id; + } + } + char propName[100]; + sprintf(propName, "hw.keyboards.%u.devname", publicID); + property_set(propName, devname); + + LOGI("New keyboard: publicID=%d device->id=%d devname='%s' propName='%s' keylayout='%s'\n", + publicID, device->id, devname, propName, keylayoutFilename); + } + + LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n", + deviceName, device, mFDCount, devid, device->classes); + + mDevicesById[devid].device = device; + device->next = mOpeningDevices; + mOpeningDevices = device; + mDevices[mFDCount] = device; + + mFDCount++; + return 0; +} + +int EventHub::close_device(const char *deviceName) +{ + AutoMutex _l(mLock); + + int i; + for(i = 1; i < mFDCount; i++) { + if(strcmp(mDevices[i]->path.string(), deviceName) == 0) { + //LOGD("remove device %d: %s\n", i, deviceName); + device_t* device = mDevices[i]; + int count = mFDCount - i - 1; + int index = (device->id&ID_MASK); + mDevicesById[index].device = NULL; + memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count); + memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count); + +#ifdef EV_SW + for (int j=0; jid) { + mSwitches[j] = 0; + } + } +#endif + + device->next = mClosingDevices; + mClosingDevices = device; + + mFDCount--; + + uint32_t publicID; + if (device->id == mFirstKeyboardId) { + LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", + device->path.string(), mFirstKeyboardId); + mFirstKeyboardId = 0; + publicID = 0; + } else { + publicID = device->id; + } + // clear the property + char propName[100]; + sprintf(propName, "hw.keyboards.%u.devname", publicID); + property_set(propName, NULL); + return 0; + } + } + LOGE("remote device: %s not found\n", deviceName); + return -1; +} + +int EventHub::read_notify(int nfd) +{ +#ifdef HAVE_INOTIFY + int res; + char devname[PATH_MAX]; + char *filename; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event *event; + + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + return 0; + LOGW("could not get event, %s\n", strerror(errno)); + return 1; + } + //printf("got %d bytes of event information\n", res); + + strcpy(devname, device_path); + filename = devname + strlen(devname); + *filename++ = '/'; + + while(res >= (int)sizeof(*event)) { + event = (struct inotify_event *)(event_buf + event_pos); + //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + if(event->len) { + strcpy(filename, event->name); + if(event->mask & IN_CREATE) { + open_device(devname); + } + else { + close_device(devname); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } +#endif + return 0; +} + + +int EventHub::scan_dir(const char *dirname) +{ + char devname[PATH_MAX]; + char *filename; + DIR *dir; + struct dirent *de; + dir = opendir(dirname); + if(dir == NULL) + return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while((de = readdir(dir))) { + if(de->d_name[0] == '.' && + (de->d_name[1] == '\0' || + (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + continue; + strcpy(filename, de->d_name); + open_device(devname); + } + closedir(dir); + return 0; +} + +}; // namespace android diff --git a/libs/ui/EventRecurrence.cpp b/libs/ui/EventRecurrence.cpp new file mode 100644 index 000000000..b436b506f --- /dev/null +++ b/libs/ui/EventRecurrence.cpp @@ -0,0 +1,484 @@ +/* + * Copyright 2006 The Android Open Source Project + */ + +#include +#include +#include +#include + +namespace android { + +#define FAIL_HERE() do { \ + printf("Parsing failed at line %d\n", __LINE__); \ + return UNKNOWN_ERROR; \ + } while(0) + +EventRecurrence::EventRecurrence() + :freq((freq_t)0), + until(), + count(0), + interval(0), + bysecond(0), + bysecondCount(0), + byminute(0), + byminuteCount(0), + byhour(0), + byhourCount(0), + byday(0), + bydayNum(0), + bydayCount(0), + bymonthday(0), + bymonthdayCount(0), + byyearday(0), + byyeardayCount(0), + byweekno(0), + byweeknoCount(0), + bymonth(0), + bymonthCount(0), + bysetpos(0), + bysetposCount(0), + wkst(0) +{ +} + +EventRecurrence::~EventRecurrence() +{ + delete[] bysecond; + delete[] byminute; + delete[] byhour; + delete[] byday; + delete[] bydayNum; + delete[] byyearday; + delete[] bymonthday; + delete[] byweekno; + delete[] bymonth; + delete[] bysetpos; +} + +enum LHS { + NONE_LHS = 0, + FREQ, + UNTIL, + COUNT, + INTERVAL, + BYSECOND, + BYMINUTE, + BYHOUR, + BYDAY, + BYMONTHDAY, + BYYEARDAY, + BYWEEKNO, + BYMONTH, + BYSETPOS, + WKST +}; + +struct LHSProc +{ + const char16_t* text; + size_t textSize; + uint32_t value; +}; + +const char16_t FREQ_text[] = { 'F', 'R', 'E', 'Q' }; +const char16_t UNTIL_text[] = { 'U', 'N', 'T', 'I', 'L' }; +const char16_t COUNT_text[] = { 'C', 'O', 'U', 'N', 'T' }; +const char16_t INTERVAL_text[] = { 'I', 'N', 'T', 'E', 'R', 'V', 'A', 'L'}; +const char16_t BYSECOND_text[] = { 'B', 'Y', 'S', 'E', 'C', 'O', 'N', 'D' }; +const char16_t BYMINUTE_text[] = { 'B', 'Y', 'M', 'I', 'N', 'U', 'T', 'E' }; +const char16_t BYHOUR_text[] = { 'B', 'Y', 'H', 'O', 'U', 'R' }; +const char16_t BYDAY_text[] = { 'B', 'Y', 'D', 'A', 'Y' }; +const char16_t BYMONTHDAY_text[] = { 'B','Y','M','O','N','T','H','D','A','Y' }; +const char16_t BYYEARDAY_text[] = { 'B','Y','Y','E','A','R','D','A','Y' }; +const char16_t BYWEEKNO_text[] = { 'B', 'Y', 'W', 'E', 'E', 'K', 'N', 'O' }; +const char16_t BYMONTH_text[] = { 'B', 'Y', 'M', 'O', 'N', 'T', 'H' }; +const char16_t BYSETPOS_text[] = { 'B', 'Y', 'S', 'E', 'T', 'P', 'O', 'S' }; +const char16_t WKST_text[] = { 'W', 'K', 'S', 'T' }; + +#define SIZ(x) (sizeof(x)/sizeof(x[0])) + +const LHSProc LHSPROC[] = { + { FREQ_text, SIZ(FREQ_text), FREQ }, + { UNTIL_text, SIZ(UNTIL_text), UNTIL }, + { COUNT_text, SIZ(COUNT_text), COUNT }, + { INTERVAL_text, SIZ(INTERVAL_text), INTERVAL }, + { BYSECOND_text, SIZ(BYSECOND_text), BYSECOND }, + { BYMINUTE_text, SIZ(BYMINUTE_text), BYMINUTE }, + { BYHOUR_text, SIZ(BYHOUR_text), BYHOUR }, + { BYDAY_text, SIZ(BYDAY_text), BYDAY }, + { BYMONTHDAY_text, SIZ(BYMONTHDAY_text), BYMONTHDAY }, + { BYYEARDAY_text, SIZ(BYYEARDAY_text), BYYEARDAY }, + { BYWEEKNO_text, SIZ(BYWEEKNO_text), BYWEEKNO }, + { BYMONTH_text, SIZ(BYMONTH_text), BYMONTH }, + { BYSETPOS_text, SIZ(BYSETPOS_text), BYSETPOS }, + { WKST_text, SIZ(WKST_text), WKST }, + { NULL, 0, NONE_LHS }, +}; + +const char16_t SECONDLY_text[] = { 'S','E','C','O','N','D','L','Y' }; +const char16_t MINUTELY_text[] = { 'M','I','N','U','T','E','L','Y' }; +const char16_t HOURLY_text[] = { 'H','O','U','R','L','Y' }; +const char16_t DAILY_text[] = { 'D','A','I','L','Y' }; +const char16_t WEEKLY_text[] = { 'W','E','E','K','L','Y' }; +const char16_t MONTHLY_text[] = { 'M','O','N','T','H','L','Y' }; +const char16_t YEARLY_text[] = { 'Y','E','A','R','L','Y' }; + +typedef LHSProc FreqProc; + +const FreqProc FREQPROC[] = { + { SECONDLY_text, SIZ(SECONDLY_text), EventRecurrence::SECONDLY }, + { MINUTELY_text, SIZ(MINUTELY_text), EventRecurrence::MINUTELY }, + { HOURLY_text, SIZ(HOURLY_text), EventRecurrence::HOURLY }, + { DAILY_text, SIZ(DAILY_text), EventRecurrence::DAILY }, + { WEEKLY_text, SIZ(WEEKLY_text), EventRecurrence::WEEKLY }, + { MONTHLY_text, SIZ(MONTHLY_text), EventRecurrence::MONTHLY }, + { YEARLY_text, SIZ(YEARLY_text), EventRecurrence::YEARLY }, + { NULL, 0, NONE_LHS }, +}; + +const char16_t SU_text[] = { 'S','U' }; +const char16_t MO_text[] = { 'M','O' }; +const char16_t TU_text[] = { 'T','U' }; +const char16_t WE_text[] = { 'W','E' }; +const char16_t TH_text[] = { 'T','H' }; +const char16_t FR_text[] = { 'F','R' }; +const char16_t SA_text[] = { 'S','A' }; + +const FreqProc WEEKDAYPROC[] = { + { SU_text, SIZ(SU_text), EventRecurrence::SU }, + { MO_text, SIZ(MO_text), EventRecurrence::MO }, + { TU_text, SIZ(TU_text), EventRecurrence::TU }, + { WE_text, SIZ(WE_text), EventRecurrence::WE }, + { TH_text, SIZ(TH_text), EventRecurrence::TH }, + { FR_text, SIZ(FR_text), EventRecurrence::FR }, + { SA_text, SIZ(SA_text), EventRecurrence::SA }, + { NULL, 0, NONE_LHS }, +}; + +// returns the index into LHSPROC for the match or -1 if not found +inline static int +match_proc(const LHSProc* p, const char16_t* str, size_t len) +{ + int i = 0; + while (p->text != NULL) { + if (p->textSize == len) { + if (0 == memcmp(p->text, str, len*sizeof(char16_t))) { + return i; + } + } + p++; + i++; + } + return -1; +} + +// rangeMin and rangeMax are inclusive +static status_t +parse_int(const char16_t* str, size_t len, int* out, + int rangeMin, int rangeMax, bool zeroOK) +{ + char16_t c; + size_t i=0; + + if (len == 0) { + FAIL_HERE(); + } + bool negative = false; + c = str[0]; + if (c == '-' ) { + negative = true; + i++; + } + else if (c == '+') { + i++; + } + int n = 0; + for (; i '9') { + FAIL_HERE(); + } + int prev = n; + n *= 10; + // the spec doesn't address how big these numbers can be, + // so we're not going to worry about not being able to represent + // INT_MIN, and if we're going to wrap, we'll just clamp to + // INT_MAX instead + if (n < prev) { + n = INT_MAX; + } else { + n += c - '0'; + } + } + if (negative) { + n = -n; + } + if (n < rangeMin || n > rangeMax) { + FAIL_HERE(); + } + if (!zeroOK && n == 0) { + FAIL_HERE(); + } + *out = n; + return NO_ERROR; +} + +static status_t +parse_int_list(const char16_t* str, size_t len, int* countOut, int** listOut, + int rangeMin, int rangeMax, bool zeroOK, + status_t (*func)(const char16_t*,size_t,int*,int,int,bool)=parse_int) +{ + status_t err; + + if (len == 0) { + *countOut = 0; + *listOut = NULL; + return NO_ERROR; + } + + // make one pass through looking for commas so we know how big to make our + // out array. + int count = 1; + for (size_t i=0; i 0) { + char16_t c = s[0]; + if (c == '-' || c == '+' || (c >= '0' && c <= '9')) { + if (len > 1) { + size_t nlen = 0; + c = s[nlen]; + while (nlen < len + && (c == '-' || c == '+' || (c >= '0' && c <= '9'))) { + c = s[nlen]; + nlen++; + } + if (nlen > 0) { + nlen--; + err = parse_int(s, nlen, &n, rangeMin, rangeMax, zeroOK); + if (err != NO_ERROR) { + FAIL_HERE(); + } + p += nlen; + plen -= nlen; + } + } + } + + int index = match_proc(WEEKDAYPROC, p, plen); + if (index >= 0) { + *out = (0xffff0000 & WEEKDAYPROC[index].value) + | (0x0000ffff & n); + return NO_ERROR; + } + } + return UNKNOWN_ERROR; +} + +static void +postprocess_byday(int count, int* byday, int** bydayNum) +{ + int* bdn = new int[count]; + *bydayNum = bdn; + for (int i=0; i= 0) { + break; + } + } + FAIL_HERE(); + case ';': + { + switch (LHSPROC[lhsIndex].value) + { + case FREQ: + if (this->freq != 0) { + FAIL_HERE(); + } + index = match_proc(FREQPROC, s, slen); + if (index >= 0) { + this->freq = (freq_t)FREQPROC[index].value; + } + break; + case UNTIL: + // XXX should check that this is a valid time + until.setTo(String16(s, slen)); + break; + case COUNT: + if (count != 0 + || NO_ERROR != parse_int(s, slen, + &count, INT_MIN, INT_MAX, true)) { + FAIL_HERE(); + } + break; + case INTERVAL: + if (interval != 0 + || NO_ERROR != parse_int(s, slen, + &interval, INT_MIN, INT_MAX, false)) { + FAIL_HERE(); + } + break; + case BYSECOND: + PARSE_INT_LIST_CHECKED(bysecond, 0, 59, true) + break; + case BYMINUTE: + PARSE_INT_LIST_CHECKED(byminute, 0, 59, true) + break; + case BYHOUR: + PARSE_INT_LIST_CHECKED(byhour, 0, 23, true) + break; + case BYDAY: + if (bydayCount != 0 || NO_ERROR != + parse_int_list(s, slen, &bydayCount, + &byday, -53, 53, false, + parse_byday)) { + FAIL_HERE(); + } + postprocess_byday(bydayCount, byday, &bydayNum); + break; + case BYMONTHDAY: + PARSE_INT_LIST_CHECKED(bymonthday, -31, 31, + false) + break; + case BYYEARDAY: + PARSE_INT_LIST_CHECKED(byyearday, -366, 366, + false) + break; + case BYWEEKNO: + PARSE_INT_LIST_CHECKED(byweekno, -53, 53, + false) + break; + case BYMONTH: + PARSE_INT_LIST_CHECKED(bymonth, 1, 12, false) + break; + case BYSETPOS: + PARSE_INT_LIST_CHECKED(bysetpos, + INT_MIN, INT_MAX, true) + break; + case WKST: + if (this->wkst != 0) { + FAIL_HERE(); + } + index = match_proc(WEEKDAYPROC, s, slen); + if (index >= 0) { + this->wkst = (int)WEEKDAYPROC[index].value; + } + break; + default: + FAIL_HERE(); + } + lhsIndex = NONE_LHS; + break; + } + } + + start = i+1; + } + } + } + + // enforce that there was a FREQ + if (freq == 0) { + FAIL_HERE(); + } + + // default wkst to MO if it wasn't specified + if (wkst == 0) { + wkst = MO; + } + + return NO_ERROR; +} + + +}; // namespace android + + diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp new file mode 100644 index 000000000..ab0fef148 --- /dev/null +++ b/libs/ui/ICamera.cpp @@ -0,0 +1,346 @@ +/* +** +** Copyright 2008, 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. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ICamera" +#include +#include +#include +#include +#include + +namespace android { + +enum { + DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, + SET_PREVIEW_DISPLAY, + SET_PREVIEW_CALLBACK_FLAG, + START_PREVIEW, + STOP_PREVIEW, + AUTO_FOCUS, + TAKE_PICTURE, + SET_PARAMETERS, + GET_PARAMETERS, + CONNECT, + LOCK, + UNLOCK, + PREVIEW_ENABLED, + START_RECORDING, + STOP_RECORDING, + RECORDING_ENABLED, + RELEASE_RECORDING_FRAME, +}; + +class BpCamera: public BpInterface +{ +public: + BpCamera(const sp& impl) + : BpInterface(impl) + { + } + + // disconnect from camera service + void disconnect() + { + LOGV("disconnect"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(DISCONNECT, data, &reply); + } + + // pass the buffered ISurface to the camera service + status_t setPreviewDisplay(const sp& surface) + { + LOGV("setPreviewDisplay"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeStrongBinder(surface->asBinder()); + remote()->transact(SET_PREVIEW_DISPLAY, data, &reply); + return reply.readInt32(); + } + + // set the preview callback flag to affect how the received frames from + // preview are handled. See Camera.h for details. + void setPreviewCallbackFlag(int flag) + { + LOGV("setPreviewCallbackFlag(%d)", flag); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeInt32(flag); + remote()->transact(SET_PREVIEW_CALLBACK_FLAG, data, &reply); + } + + // start preview mode, must call setPreviewDisplay first + status_t startPreview() + { + LOGV("startPreview"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(START_PREVIEW, data, &reply); + return reply.readInt32(); + } + + // start recording mode, must call setPreviewDisplay first + status_t startRecording() + { + LOGV("startRecording"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(START_RECORDING, data, &reply); + return reply.readInt32(); + } + + // stop preview mode + void stopPreview() + { + LOGV("stopPreview"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(STOP_PREVIEW, data, &reply); + } + + // stop recording mode + void stopRecording() + { + LOGV("stopRecording"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(STOP_RECORDING, data, &reply); + } + + void releaseRecordingFrame(const sp& mem) + { + LOGV("releaseRecordingFrame"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeStrongBinder(mem->asBinder()); + remote()->transact(RELEASE_RECORDING_FRAME, data, &reply); + } + + // check preview state + bool previewEnabled() + { + LOGV("previewEnabled"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(PREVIEW_ENABLED, data, &reply); + return reply.readInt32(); + } + + // check recording state + bool recordingEnabled() + { + LOGV("recordingEnabled"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(RECORDING_ENABLED, data, &reply); + return reply.readInt32(); + } + + // auto focus + status_t autoFocus() + { + LOGV("autoFocus"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(AUTO_FOCUS, data, &reply); + status_t ret = reply.readInt32(); + return ret; + } + + // take a picture - returns an IMemory (ref-counted mmap) + status_t takePicture() + { + LOGV("takePicture"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(TAKE_PICTURE, data, &reply); + status_t ret = reply.readInt32(); + return ret; + } + + // set preview/capture parameters - key/value pairs + status_t setParameters(const String8& params) + { + LOGV("setParameters"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeString8(params); + remote()->transact(SET_PARAMETERS, data, &reply); + return reply.readInt32(); + } + + // get preview/capture parameters - key/value pairs + String8 getParameters() const + { + LOGV("getParameters"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(GET_PARAMETERS, data, &reply); + return reply.readString8(); + } + virtual status_t connect(const sp& cameraClient) + { + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeStrongBinder(cameraClient->asBinder()); + remote()->transact(CONNECT, data, &reply); + return reply.readInt32(); + } + virtual status_t lock() + { + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(LOCK, data, &reply); + return reply.readInt32(); + } + virtual status_t unlock() + { + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(UNLOCK, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(Camera, "android.hardware.ICamera"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnCamera::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DISCONNECT: { + LOGV("DISCONNECT"); + CHECK_INTERFACE(ICamera, data, reply); + disconnect(); + return NO_ERROR; + } break; + case SET_PREVIEW_DISPLAY: { + LOGV("SET_PREVIEW_DISPLAY"); + CHECK_INTERFACE(ICamera, data, reply); + sp surface = interface_cast(data.readStrongBinder()); + reply->writeInt32(setPreviewDisplay(surface)); + return NO_ERROR; + } break; + case SET_PREVIEW_CALLBACK_FLAG: { + LOGV("SET_PREVIEW_CALLBACK_TYPE"); + CHECK_INTERFACE(ICamera, data, reply); + int callback_flag = data.readInt32(); + setPreviewCallbackFlag(callback_flag); + return NO_ERROR; + } break; + case START_PREVIEW: { + LOGV("START_PREVIEW"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(startPreview()); + return NO_ERROR; + } break; + case START_RECORDING: { + LOGV("START_RECORDING"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(startRecording()); + return NO_ERROR; + } break; + case STOP_PREVIEW: { + LOGV("STOP_PREVIEW"); + CHECK_INTERFACE(ICamera, data, reply); + stopPreview(); + return NO_ERROR; + } break; + case STOP_RECORDING: { + LOGV("STOP_RECORDING"); + CHECK_INTERFACE(ICamera, data, reply); + stopRecording(); + return NO_ERROR; + } break; + case RELEASE_RECORDING_FRAME: { + LOGV("RELEASE_RECORDING_FRAME"); + CHECK_INTERFACE(ICamera, data, reply); + sp mem = interface_cast(data.readStrongBinder()); + releaseRecordingFrame(mem); + return NO_ERROR; + } break; + case PREVIEW_ENABLED: { + LOGV("PREVIEW_ENABLED"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(previewEnabled()); + return NO_ERROR; + } break; + case RECORDING_ENABLED: { + LOGV("RECORDING_ENABLED"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(recordingEnabled()); + return NO_ERROR; + } break; + case AUTO_FOCUS: { + LOGV("AUTO_FOCUS"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(autoFocus()); + return NO_ERROR; + } break; + case TAKE_PICTURE: { + LOGV("TAKE_PICTURE"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(takePicture()); + return NO_ERROR; + } break; + case SET_PARAMETERS: { + LOGV("SET_PARAMETERS"); + CHECK_INTERFACE(ICamera, data, reply); + String8 params(data.readString8()); + reply->writeInt32(setParameters(params)); + return NO_ERROR; + } break; + case GET_PARAMETERS: { + LOGV("GET_PARAMETERS"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeString8(getParameters()); + return NO_ERROR; + } break; + case CONNECT: { + CHECK_INTERFACE(ICamera, data, reply); + sp cameraClient = interface_cast(data.readStrongBinder()); + reply->writeInt32(connect(cameraClient)); + return NO_ERROR; + } break; + case LOCK: { + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(lock()); + return NO_ERROR; + } break; + case UNLOCK: { + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(unlock()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libs/ui/ICameraClient.cpp b/libs/ui/ICameraClient.cpp new file mode 100644 index 000000000..4bec9d2ae --- /dev/null +++ b/libs/ui/ICameraClient.cpp @@ -0,0 +1,185 @@ +/* +** +** Copyright 2008, 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. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ICameraClient" +#include +#include +#include +#include + +namespace android { + +enum { + SHUTTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, + RAW_CALLBACK, + JPEG_CALLBACK, + PREVIEW_CALLBACK, + ERROR_CALLBACK, + AUTOFOCUS_CALLBACK, + RECORDING_CALLBACK, +}; + +class BpCameraClient: public BpInterface +{ +public: + BpCameraClient(const sp& impl) + : BpInterface(impl) + { + } + + // callback to let the app know the shutter has closed, ideal for playing the shutter sound + void shutterCallback() + { + LOGV("shutterCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + remote()->transact(SHUTTER_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app with picture data + void rawCallback(const sp& picture) + { + LOGV("rawCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeStrongBinder(picture->asBinder()); + remote()->transact(RAW_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app with picture data + void jpegCallback(const sp& picture) + { + LOGV("jpegCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeStrongBinder(picture->asBinder()); + remote()->transact(JPEG_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app with preview frame data + void previewCallback(const sp& frame) + { + LOGV("previewCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeStrongBinder(frame->asBinder()); + remote()->transact(PREVIEW_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app with recording frame data + void recordingCallback(const sp& frame) + { + LOGV("recordingCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeStrongBinder(frame->asBinder()); + remote()->transact(RECORDING_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app to report error + void errorCallback(status_t error) + { + LOGV("errorCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeInt32(error); + remote()->transact(ERROR_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app to report autofocus completion + void autoFocusCallback(bool focused) + { + LOGV("autoFocusCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeInt32(focused); + remote()->transact(AUTOFOCUS_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(CameraClient, "android.hardware.ICameraClient"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnCameraClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case SHUTTER_CALLBACK: { + LOGV("SHUTTER_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + shutterCallback(); + return NO_ERROR; + } break; + case RAW_CALLBACK: { + LOGV("RAW_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + sp picture = interface_cast(data.readStrongBinder()); + rawCallback(picture); + return NO_ERROR; + } break; + case JPEG_CALLBACK: { + LOGV("JPEG_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + sp picture = interface_cast(data.readStrongBinder()); + jpegCallback(picture); + return NO_ERROR; + } break; + case PREVIEW_CALLBACK: { + LOGV("PREVIEW_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + sp frame = interface_cast(data.readStrongBinder()); + previewCallback(frame); + return NO_ERROR; + } break; + case RECORDING_CALLBACK: { + LOGV("RECORDING_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + sp frame = interface_cast(data.readStrongBinder()); + recordingCallback(frame); + return NO_ERROR; + } break; + case ERROR_CALLBACK: { + LOGV("ERROR_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + status_t error = data.readInt32(); + errorCallback(error); + return NO_ERROR; + } break; + case AUTOFOCUS_CALLBACK: { + LOGV("AUTOFOCUS_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + bool focused = (bool)data.readInt32(); + autoFocusCallback(focused); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libs/ui/ICameraService.cpp b/libs/ui/ICameraService.cpp new file mode 100644 index 000000000..e5687fef6 --- /dev/null +++ b/libs/ui/ICameraService.cpp @@ -0,0 +1,77 @@ +/* +** +** Copyright 2008, 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 + +#include + +namespace android { + +class BpCameraService: public BpInterface +{ +public: + BpCameraService(const sp& impl) + : BpInterface(impl) + { + } + + // connect to camera service + virtual sp connect(const sp& cameraClient) + { + Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); + data.writeStrongBinder(cameraClient->asBinder()); + remote()->transact(BnCameraService::CONNECT, data, &reply); + return interface_cast(reply.readStrongBinder()); + } +}; + +IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnCameraService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case CONNECT: { + CHECK_INTERFACE(ICameraService, data, reply); + sp cameraClient = interface_cast(data.readStrongBinder()); + sp camera = connect(cameraClient); + reply->writeStrongBinder(camera->asBinder()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libs/ui/IOverlay.cpp b/libs/ui/IOverlay.cpp new file mode 100644 index 000000000..fed47c27e --- /dev/null +++ b/libs/ui/IOverlay.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007 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 + +#include + +namespace android { + +enum { + DESTROY = IBinder::FIRST_CALL_TRANSACTION, // one-way transaction +}; + +class BpOverlay : public BpInterface +{ +public: + BpOverlay(const sp& impl) + : BpInterface(impl) + { + } + + virtual void destroy() + { + Parcel data, reply; + data.writeInterfaceToken(IOverlay::getInterfaceDescriptor()); + remote()->transact(DESTROY, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(Overlay, "android.ui.IOverlay"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnOverlay::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DESTROY: { + CHECK_INTERFACE(IOverlay, data, reply); + destroy(); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp new file mode 100644 index 000000000..d5e9f812a --- /dev/null +++ b/libs/ui/ISurface.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2007 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 + +#include +#include + + +namespace android { + +ISurface::BufferHeap::BufferHeap() + : w(0), h(0), hor_stride(0), ver_stride(0), format(0), + transform(0), flags(0) +{ +} + +ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, + int32_t hor_stride, int32_t ver_stride, + PixelFormat format, const sp& heap) + : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), + format(format), transform(0), flags(0), heap(heap) +{ +} + +ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, + int32_t hor_stride, int32_t ver_stride, + PixelFormat format, uint32_t transform, uint32_t flags, + const sp& heap) + : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), + format(format), transform(transform), flags(flags), heap(heap) +{ +} + + +ISurface::BufferHeap::~BufferHeap() +{ +} + +class BpSurface : public BpInterface +{ +public: + BpSurface(const sp& impl) + : BpInterface(impl) + { + } + + virtual status_t registerBuffers(const BufferHeap& buffers) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(buffers.w); + data.writeInt32(buffers.h); + data.writeInt32(buffers.hor_stride); + data.writeInt32(buffers.ver_stride); + data.writeInt32(buffers.format); + data.writeInt32(buffers.transform); + data.writeInt32(buffers.flags); + data.writeStrongBinder(buffers.heap->asBinder()); + remote()->transact(REGISTER_BUFFERS, data, &reply); + status_t result = reply.readInt32(); + return result; + } + + virtual void postBuffer(ssize_t offset) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(offset); + remote()->transact(POST_BUFFER, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual void unregisterBuffers() + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + remote()->transact(UNREGISTER_BUFFERS, data, &reply); + } + + virtual sp createOverlay( + uint32_t w, uint32_t h, int32_t format) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + remote()->transact(CREATE_OVERLAY, data, &reply); + return OverlayRef::readFromParcel(reply); + } +}; + +IMPLEMENT_META_INTERFACE(Surface, "android.ui.ISurface"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnSurface::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case REGISTER_BUFFERS: { + CHECK_INTERFACE(ISurface, data, reply); + BufferHeap buffer; + buffer.w = data.readInt32(); + buffer.h = data.readInt32(); + buffer.hor_stride = data.readInt32(); + buffer.ver_stride= data.readInt32(); + buffer.format = data.readInt32(); + buffer.transform = data.readInt32(); + buffer.flags = data.readInt32(); + buffer.heap = interface_cast(data.readStrongBinder()); + status_t err = registerBuffers(buffer); + reply->writeInt32(err); + return NO_ERROR; + } break; + case UNREGISTER_BUFFERS: { + CHECK_INTERFACE(ISurface, data, reply); + unregisterBuffers(); + return NO_ERROR; + } break; + case POST_BUFFER: { + CHECK_INTERFACE(ISurface, data, reply); + ssize_t offset = data.readInt32(); + postBuffer(offset); + return NO_ERROR; + } break; + case CREATE_OVERLAY: { + CHECK_INTERFACE(ISurface, data, reply); + int w = data.readInt32(); + int h = data.readInt32(); + int f = data.readInt32(); + sp o = createOverlay(w, h, f); + return OverlayRef::writeToParcel(reply, o); + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/ui/ISurfaceComposer.cpp b/libs/ui/ISurfaceComposer.cpp new file mode 100644 index 000000000..0fea6f938 --- /dev/null +++ b/libs/ui/ISurfaceComposer.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2007 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. + */ + +// tag as surfaceflinger +#define LOG_TAG "SurfaceFlinger" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +// --------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +namespace android { + +class BpSurfaceComposer : public BpInterface +{ +public: + BpSurfaceComposer(const sp& impl) + : BpInterface(impl) + { + } + + virtual sp createConnection() + { + uint32_t n; + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + + virtual sp getCblk() const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::GET_CBLK, data, &reply); + return interface_cast(reply.readStrongBinder()); + } + + virtual void openGlobalTransaction() + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::OPEN_GLOBAL_TRANSACTION, data, &reply); + } + + virtual void closeGlobalTransaction() + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::CLOSE_GLOBAL_TRANSACTION, data, &reply); + } + + virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(dpy); + data.writeInt32(flags); + remote()->transact(BnSurfaceComposer::FREEZE_DISPLAY, data, &reply); + return reply.readInt32(); + } + + virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(dpy); + data.writeInt32(flags); + remote()->transact(BnSurfaceComposer::UNFREEZE_DISPLAY, data, &reply); + return reply.readInt32(); + } + + virtual int setOrientation(DisplayID dpy, int orientation) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(dpy); + data.writeInt32(orientation); + remote()->transact(BnSurfaceComposer::SET_ORIENTATION, data, &reply); + return reply.readInt32(); + } + + virtual void bootFinished() + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply); + } + + virtual status_t requestGPU( + const sp& callback, gpu_info_t* gpu) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(callback->asBinder()); + remote()->transact(BnSurfaceComposer::REQUEST_GPU, data, &reply); + gpu->regs = interface_cast(reply.readStrongBinder()); + gpu->count = reply.readInt32(); + + // FIXME: for now, we don't dynamically allocate the regions array + size_t maxCount = sizeof(gpu->regions)/sizeof(*gpu->regions); + if (gpu->count > maxCount) + return BAD_VALUE; + + for (size_t i=0 ; icount ; i++) { + gpu->regions[i].region = interface_cast(reply.readStrongBinder()); + gpu->regions[i].reserved = reply.readInt32(); + } + return reply.readInt32(); + } + + virtual status_t revokeGPU() + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::REVOKE_GPU, data, &reply); + return reply.readInt32(); + } + + virtual void signal() const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::SIGNAL, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnSurfaceComposer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + status_t err = BnInterface::onTransact(code, data, reply, flags); + if (err == NO_ERROR) + return err; + + CHECK_INTERFACE(ISurfaceComposer, data, reply); + + switch(code) { + case CREATE_CONNECTION: { + sp b = createConnection()->asBinder(); + reply->writeStrongBinder(b); + } break; + case OPEN_GLOBAL_TRANSACTION: { + openGlobalTransaction(); + } break; + case CLOSE_GLOBAL_TRANSACTION: { + closeGlobalTransaction(); + } break; + case SET_ORIENTATION: { + DisplayID dpy = data.readInt32(); + int orientation = data.readInt32(); + reply->writeInt32( setOrientation(dpy, orientation) ); + } break; + case FREEZE_DISPLAY: { + DisplayID dpy = data.readInt32(); + uint32_t flags = data.readInt32(); + reply->writeInt32( freezeDisplay(dpy, flags) ); + } break; + case UNFREEZE_DISPLAY: { + DisplayID dpy = data.readInt32(); + uint32_t flags = data.readInt32(); + reply->writeInt32( unfreezeDisplay(dpy, flags) ); + } break; + case BOOT_FINISHED: { + bootFinished(); + } break; + case REVOKE_GPU: { + reply->writeInt32( revokeGPU() ); + } break; + case SIGNAL: { + signal(); + } break; + case GET_CBLK: { + sp b = getCblk()->asBinder(); + reply->writeStrongBinder(b); + } break; + case REQUEST_GPU: { + // TODO: this should be protected by a permission + gpu_info_t info; + sp callback + = interface_cast(data.readStrongBinder()); + status_t res = requestGPU(callback, &info); + + // FIXME: for now, we don't dynamically allocate the regions array + size_t maxCount = sizeof(info.regions)/sizeof(*info.regions); + if (info.count > maxCount) + return BAD_VALUE; + + reply->writeStrongBinder(info.regs->asBinder()); + reply->writeInt32(info.count); + for (size_t i=0 ; iwriteStrongBinder(info.regions[i].region->asBinder()); + reply->writeInt32(info.regions[i].reserved); + } + reply->writeInt32(res); + } break; + default: + return UNKNOWN_TRANSACTION; + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +enum { + // Note: BOOT_FINISHED must remain this value, it is called by ActivityManagerService. + GPU_LOST = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpGPUCallback : public BpInterface +{ +public: + BpGPUCallback(const sp& impl) + : BpInterface(impl) + { + } + + virtual void gpuLost() + { + Parcel data, reply; + data.writeInterfaceToken(IGPUCallback::getInterfaceDescriptor()); + remote()->transact(GPU_LOST, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(GPUCallback, "android.ui.IGPUCallback"); + +status_t BnGPUCallback::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GPU_LOST: { + CHECK_INTERFACE(IGPUCallback, data, reply); + gpuLost(); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; diff --git a/libs/ui/ISurfaceFlingerClient.cpp b/libs/ui/ISurfaceFlingerClient.cpp new file mode 100644 index 000000000..dd6a798b9 --- /dev/null +++ b/libs/ui/ISurfaceFlingerClient.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2007 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. + */ + +// tag as surfaceflinger +#define LOG_TAG "SurfaceFlinger" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +// --------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +namespace android { + +enum { + GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, + CREATE_SURFACE, + DESTROY_SURFACE, + SET_STATE +}; + +class BpSurfaceFlingerClient : public BpInterface +{ +public: + BpSurfaceFlingerClient(const sp& impl) + : BpInterface(impl) + { + } + + virtual void getControlBlocks(sp* ctl) const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + remote()->transact(GET_CBLK, data, &reply); + *ctl = interface_cast(reply.readStrongBinder()); + } + + virtual sp createSurface( surface_data_t* params, + int pid, + DisplayID display, + uint32_t w, + uint32_t h, + PixelFormat format, + uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeInt32(display); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + data.writeInt32(flags); + remote()->transact(CREATE_SURFACE, data, &reply); + params->readFromParcel(reply); + return interface_cast(reply.readStrongBinder()); + } + + virtual status_t destroySurface(SurfaceID sid) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInt32(sid); + remote()->transact(DESTROY_SURFACE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setState(int32_t count, const layer_state_t* states) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInt32(count); + for (int i=0 ; itransact(SET_STATE, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(SurfaceFlingerClient, "android.ui.ISurfaceFlingerClient"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnSurfaceFlingerClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // codes that don't require permission check + + switch(code) { + case GET_CBLK: { + CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + sp ctl; + getControlBlocks(&ctl); + reply->writeStrongBinder(ctl->asBinder()); + return NO_ERROR; + } break; + } + + // these must be checked + + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int self_pid = getpid(); + if (UNLIKELY(pid != self_pid)) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.ACCESS_SURFACE_FLINGER"))) + { + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't openGlobalTransaction pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + + switch(code) { + case CREATE_SURFACE: { + CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + surface_data_t params; + int32_t pid = data.readInt32(); + DisplayID display = data.readInt32(); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + PixelFormat format = data.readInt32(); + uint32_t flags = data.readInt32(); + sp s = createSurface(¶ms, pid, display, w, h, format, flags); + params.writeToParcel(reply); + reply->writeStrongBinder(s->asBinder()); + return NO_ERROR; + } break; + case DESTROY_SURFACE: { + CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + reply->writeInt32( destroySurface( data.readInt32() ) ); + return NO_ERROR; + } break; + case SET_STATE: { + CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + int32_t count = data.readInt32(); + layer_state_t* states = new layer_state_t[count]; + for (int i=0 ; iwriteInt32(err); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------- + +status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& parcel) +{ + token = parcel.readInt32(); + identity = parcel.readInt32(); + heap[0] = interface_cast(parcel.readStrongBinder()); + heap[1] = interface_cast(parcel.readStrongBinder()); + return NO_ERROR; +} + +status_t ISurfaceFlingerClient::surface_data_t::writeToParcel(Parcel* parcel) const +{ + parcel->writeInt32(token); + parcel->writeInt32(identity); + parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL); + parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL); + return NO_ERROR; +} + +}; // namespace android diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp new file mode 100644 index 000000000..e891181c8 --- /dev/null +++ b/libs/ui/KeyCharacterMap.cpp @@ -0,0 +1,263 @@ +#define LOG_TAG "KeyCharacterMap" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct Header +{ + char magic[8]; + unsigned int endian; + unsigned int version; + unsigned int keycount; + unsigned char kbdtype; + char padding[11]; +}; + +KeyCharacterMap::KeyCharacterMap() +{ +} + +KeyCharacterMap::~KeyCharacterMap() +{ + free(m_keys); +} + +unsigned short +KeyCharacterMap::get(int keycode, int meta) +{ + Key* k = find_key(keycode); + if (k != NULL) { + return k->data[meta & META_MASK]; + } + return 0; +} + +unsigned short +KeyCharacterMap::getNumber(int keycode) +{ + Key* k = find_key(keycode); + if (k != NULL) { + return k->number; + } + return 0; +} + +unsigned short +KeyCharacterMap::getMatch(int keycode, const unsigned short* chars, + int charsize, uint32_t modifiers) +{ + Key* k = find_key(keycode); + modifiers &= 3; // ignore the SYM key because we don't have keymap entries for it + if (k != NULL) { + const uint16_t* data = k->data; + for (int j=0; jdisplay_label; + } + return 0; +} + +bool +KeyCharacterMap::getKeyData(int keycode, unsigned short *displayLabel, + unsigned short *number, unsigned short* results) +{ + Key* k = find_key(keycode); + if (k != NULL) { + memcpy(results, k->data, sizeof(short)*(META_MASK + 1)); + *number = k->number; + *displayLabel = k->display_label; + return true; + } else { + return false; + } +} + +bool +KeyCharacterMap::find_char(uint16_t c, uint32_t* key, uint32_t* mods) +{ + uint32_t N = m_keyCount; + for (int j=0; j<(META_MASK + 1); j++) { + Key const* keys = m_keys; + for (uint32_t i=0; idata[j] == c) { + *key = keys->keycode; + *mods = j; + return true; + } + keys++; + } + } + return false; +} + +bool +KeyCharacterMap::getEvents(uint16_t* chars, size_t len, + Vector* keys, Vector* modifiers) +{ + for (size_t i=0; iadd(k); + modifiers->add(mods); + } else { + return false; + } + } + return true; +} + +KeyCharacterMap::Key* +KeyCharacterMap::find_key(int keycode) +{ + Key* keys = m_keys; + int low = 0; + int high = m_keyCount - 1; + int mid; + int n; + while (low <= high) { + mid = (low + high) / 2; + n = keys[mid].keycode; + if (keycode < n) { + high = mid - 1; + } else if (keycode > n) { + low = mid + 1; + } else { + return keys + mid; + } + } + return NULL; +} + +KeyCharacterMap* +KeyCharacterMap::load(int id) +{ + KeyCharacterMap* rv = NULL; + char path[PATH_MAX]; + char propName[100]; + char dev[PROPERTY_VALUE_MAX]; + char tmpfn[PROPERTY_VALUE_MAX]; + int err; + const char* root = getenv("ANDROID_ROOT"); + + sprintf(propName, "hw.keyboards.%u.devname", id); + err = property_get(propName, dev, ""); + if (err > 0) { + // replace all the spaces with underscores + strcpy(tmpfn, dev); + for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) + *p = '_'; + snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, tmpfn); + //LOGD("load: dev='%s' path='%s'\n", dev, path); + rv = try_file(path); + if (rv != NULL) { + return rv; + } + LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev); + } else { + LOGW("No keyboard for id %d", id); + } + + snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root); + rv = try_file(path); + if (rv == NULL) { + LOGE("Can't find any keycharmaps (also tried %s)", path); + return NULL; + } + LOGW("Using default keymap: %s", path); + + return rv; +} + +KeyCharacterMap* +KeyCharacterMap::try_file(const char* filename) +{ + KeyCharacterMap* rv = NULL; + Key* keys; + int fd; + off_t filesize; + Header header; + int err; + + fd = open(filename, O_RDONLY); + if (fd == -1) { + LOGW("Can't open keycharmap file"); + return NULL; + } + + filesize = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + + // validate the header + if (filesize <= (off_t)sizeof(header)) { + LOGW("Bad keycharmap - filesize=%d\n", (int)filesize); + goto cleanup1; + } + + err = read(fd, &header, sizeof(header)); + if (err == -1) { + LOGW("Error reading keycharmap file"); + goto cleanup1; + } + + if (0 != memcmp(header.magic, "keychar", 8)) { + LOGW("Bad keycharmap magic token"); + goto cleanup1; + } + if (header.endian != 0x12345678) { + LOGW("Bad keycharmap endians"); + goto cleanup1; + } + if ((header.version & 0xff) != 2) { + LOGW("Only support keycharmap version 2 (got 0x%08x)", header.version); + goto cleanup1; + } + if (filesize < (off_t)(sizeof(Header)+(sizeof(Key)*header.keycount))) { + LOGW("Bad keycharmap file size\n"); + goto cleanup1; + } + + // read the key data + keys = (Key*)malloc(sizeof(Key)*header.keycount); + err = read(fd, keys, sizeof(Key)*header.keycount); + if (err == -1) { + LOGW("Error reading keycharmap file"); + free(keys); + goto cleanup1; + } + + // return the object + rv = new KeyCharacterMap; + rv->m_keyCount = header.keycount; + rv->m_keys = keys; + rv->m_type = header.kbdtype; + +cleanup1: + close(fd); + + return rv; +} diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/ui/KeyLayoutMap.cpp new file mode 100644 index 000000000..15ae54c6f --- /dev/null +++ b/libs/ui/KeyLayoutMap.cpp @@ -0,0 +1,235 @@ +#define LOG_TAG "KeyLayoutMap" + +#include "KeyLayoutMap.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +KeyLayoutMap::KeyLayoutMap() + :m_status(NO_INIT), + m_keys() +{ +} + +KeyLayoutMap::~KeyLayoutMap() +{ +} + +static String8 +next_token(char const** p, int *line) +{ + bool begun = false; + const char* begin = *p; + const char* end = *p; + while (true) { + if (*end == '\n') { + (*line)++; + } + switch (*end) + { + case '#': + if (begun) { + *p = end; + return String8(begin, end-begin); + } else { + do { + begin++; + end++; + } while (*begin != '\0' && *begin != '\n'); + } + case '\0': + case ' ': + case '\n': + case '\r': + case '\t': + if (begun || (*end == '\0')) { + *p = end; + return String8(begin, end-begin); + } else { + begin++; + end++; + break; + } + default: + end++; + begun = true; + } + } +} + +static int32_t +token_to_value(const char *literal, const KeycodeLabel *list) +{ + while (list->literal) { + if (0 == strcmp(literal, list->literal)) { + return list->value; + } + list++; + } + return list->value; +} + +status_t +KeyLayoutMap::load(const char* filename) +{ + int fd = open(filename, O_RDONLY); + if (fd < 0) { + LOGE("error opening file=%s err=%s\n", filename, strerror(errno)); + m_status = errno; + return errno; + } + + off_t len = lseek(fd, 0, SEEK_END); + off_t errlen = lseek(fd, 0, SEEK_SET); + if (len < 0 || errlen < 0) { + close(fd); + LOGE("error seeking file=%s err=%s\n", filename, strerror(errno)); + m_status = errno; + return errno; + } + + char* buf = (char*)malloc(len+1); + if (read(fd, buf, len) != len) { + LOGE("error reading file=%s err=%s\n", filename, strerror(errno)); + m_status = errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); + return errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); + } + errno = 0; + buf[len] = '\0'; + + int32_t scancode = -1; + int32_t keycode = -1; + uint32_t flags = 0; + uint32_t tmp; + char* end; + status_t err = NO_ERROR; + int line = 1; + char const* p = buf; + enum { BEGIN, SCANCODE, KEYCODE, FLAG } state = BEGIN; + while (true) { + String8 token = next_token(&p, &line); + if (*p == '\0') { + break; + } + switch (state) + { + case BEGIN: + if (token == "key") { + state = SCANCODE; + } else { + LOGE("%s:%d: expected key, got '%s'\n", filename, line, + token.string()); + err = BAD_VALUE; + goto done; + } + break; + case SCANCODE: + scancode = strtol(token.string(), &end, 0); + if (*end != '\0') { + LOGE("%s:%d: expected scancode (a number), got '%s'\n", + filename, line, token.string()); + goto done; + } + //LOGI("%s:%d: got scancode %d\n", filename, line, scancode ); + state = KEYCODE; + break; + case KEYCODE: + keycode = token_to_value(token.string(), KEYCODES); + //LOGI("%s:%d: got keycode %d for %s\n", filename, line, keycode, token.string() ); + if (keycode == 0) { + LOGE("%s:%d: expected keycode, got '%s'\n", + filename, line, token.string()); + goto done; + } + state = FLAG; + break; + case FLAG: + if (token == "key") { + if (scancode != -1) { + //LOGI("got key decl scancode=%d keycode=%d" + // " flags=0x%08x\n", scancode, keycode, flags); + Key k = { keycode, flags }; + m_keys.add(scancode, k); + state = SCANCODE; + scancode = -1; + keycode = -1; + flags = 0; + break; + } + } + tmp = token_to_value(token.string(), FLAGS); + //LOGI("%s:%d: got flags %x for %s\n", filename, line, tmp, token.string() ); + if (tmp == 0) { + LOGE("%s:%d: expected flag, got '%s'\n", + filename, line, token.string()); + goto done; + } + flags |= tmp; + break; + } + } + if (state == FLAG && scancode != -1 ) { + //LOGI("got key decl scancode=%d keycode=%d" + // " flags=0x%08x\n", scancode, keycode, flags); + Key k = { keycode, flags }; + m_keys.add(scancode, k); + } + +done: + free(buf); + close(fd); + + m_status = err; + return err; +} + +status_t +KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags) const +{ + if (m_status != NO_ERROR) { + return m_status; + } + + ssize_t index = m_keys.indexOfKey(scancode); + if (index < 0) { + //LOGW("couldn't map scancode=%d\n", scancode); + return NAME_NOT_FOUND; + } + + const Key& k = m_keys.valueAt(index); + + *keycode = k.keycode; + *flags = k.flags; + + //LOGD("mapped scancode=%d to keycode=%d flags=0x%08x\n", scancode, + // keycode, flags); + + return NO_ERROR; +} + +status_t +KeyLayoutMap::findScancodes(int32_t keycode, Vector* outScancodes) const +{ + if (m_status != NO_ERROR) { + return m_status; + } + + const size_t N = m_keys.size(); + for (size_t i=0; iadd(m_keys.keyAt(i)); + } + } + + return NO_ERROR; +} + +}; diff --git a/libs/ui/KeyLayoutMap.h b/libs/ui/KeyLayoutMap.h new file mode 100644 index 000000000..43f84ce49 --- /dev/null +++ b/libs/ui/KeyLayoutMap.h @@ -0,0 +1,31 @@ +#ifndef KEYLAYOUTMAP_H +#define KEYLAYOUTMAP_H + +#include + +namespace android { + +class KeyLayoutMap +{ +public: + KeyLayoutMap(); + ~KeyLayoutMap(); + + status_t load(const char* filename); + + status_t map(int32_t scancode, int32_t *keycode, uint32_t *flags) const; + status_t findScancodes(int32_t keycode, Vector* outScancodes) const; + +private: + struct Key { + int32_t keycode; + uint32_t flags; + }; + + status_t m_status; + KeyedVector m_keys; +}; + +}; + +#endif // KEYLAYOUTMAP_H diff --git a/libs/ui/LayerState.cpp b/libs/ui/LayerState.cpp new file mode 100644 index 000000000..0b6374b1e --- /dev/null +++ b/libs/ui/LayerState.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008 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 + +namespace android { + +status_t layer_state_t::write(Parcel& output) const +{ + size_t size = sizeof(layer_state_t); + + //output.writeStrongBinder(surface->asBinder()); + //size -= sizeof(surface); + + transparentRegion.write(output); + size -= sizeof(transparentRegion); + + output.write(this, size); + + return NO_ERROR; +} + +status_t layer_state_t::read(const Parcel& input) +{ + size_t size = sizeof(layer_state_t); + + //surface = interface_cast(input.readStrongBinder()); + //size -= sizeof(surface); + + transparentRegion.read(input); + size -= sizeof(transparentRegion); + + input.read(this, size); + + return NO_ERROR; +} + +}; // namespace android diff --git a/libs/ui/MODULE_LICENSE_APACHE2 b/libs/ui/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/libs/ui/NOTICE b/libs/ui/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/libs/ui/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp new file mode 100644 index 000000000..b236edc29 --- /dev/null +++ b/libs/ui/Overlay.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2007 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 +#include + +#include + +namespace android { + +Overlay::Overlay(const sp& overlayRef) + : mOverlayRef(overlayRef), mOverlayData(0), mStatus(NO_INIT) +{ + mOverlayData = NULL; + hw_module_t const* module; + if (overlayRef != 0) { + if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { + if (overlay_data_open(module, &mOverlayData) == NO_ERROR) { + mStatus = mOverlayData->initialize(mOverlayData, + overlayRef->mOverlayHandle); + } + } + } +} + +Overlay::~Overlay() { + if (mOverlayData) { + overlay_data_close(mOverlayData); + } +} + +status_t Overlay::dequeueBuffer(overlay_buffer_t* buffer) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->dequeueBuffer(mOverlayData, buffer); +} + +status_t Overlay::queueBuffer(overlay_buffer_t buffer) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->queueBuffer(mOverlayData, buffer); +} + +int32_t Overlay::getBufferCount() const +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->getBufferCount(mOverlayData); +} + +void* Overlay::getBufferAddress(overlay_buffer_t buffer) +{ + if (mStatus != NO_ERROR) return NULL; + return mOverlayData->getBufferAddress(mOverlayData, buffer); +} + +void Overlay::destroy() { + if (mStatus != NO_ERROR) return; + mOverlayRef->mOverlayChannel->destroy(); +} + +status_t Overlay::getStatus() const { + return mStatus; +} + +overlay_handle_t Overlay::getHandleRef() const { + if (mStatus != NO_ERROR) return NULL; + return mOverlayRef->mOverlayHandle; +} + +uint32_t Overlay::getWidth() const { + if (mStatus != NO_ERROR) return 0; + return mOverlayRef->mWidth; +} + +uint32_t Overlay::getHeight() const { + if (mStatus != NO_ERROR) return 0; + return mOverlayRef->mHeight; +} + +int32_t Overlay::getFormat() const { + if (mStatus != NO_ERROR) return -1; + return mOverlayRef->mFormat; +} + +int32_t Overlay::getWidthStride() const { + if (mStatus != NO_ERROR) return 0; + return mOverlayRef->mWidthStride; +} + +int32_t Overlay::getHeightStride() const { + if (mStatus != NO_ERROR) return 0; + return mOverlayRef->mHeightStride; +} +// ---------------------------------------------------------------------------- + +OverlayRef::OverlayRef() + : mOverlayHandle(0), + mWidth(0), mHeight(0), mFormat(0), mWidthStride(0), mHeightStride(0), + mOwnHandle(true) +{ +} + +OverlayRef::OverlayRef(overlay_handle_t handle, const sp& channel, + uint32_t w, uint32_t h, int32_t f, uint32_t ws, uint32_t hs) + : mOverlayHandle(handle), mOverlayChannel(channel), + mWidth(w), mHeight(h), mFormat(f), mWidthStride(ws), mHeightStride(hs), + mOwnHandle(false) +{ +} + +OverlayRef::~OverlayRef() +{ + if (mOwnHandle) { + /* FIXME: handles should be promoted to "real" API and be handled by + * the framework */ + for (int i=0 ; inumFds ; i++) { + close(mOverlayHandle->data[i]); + } + free((void*)mOverlayHandle); + } +} + +sp OverlayRef::readFromParcel(const Parcel& data) { + sp result; + sp overlay = IOverlay::asInterface(data.readStrongBinder()); + if (overlay != NULL) { + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t f = data.readInt32(); + uint32_t ws = data.readInt32(); + uint32_t hs = data.readInt32(); + native_handle* handle = data.readNativeHandle(NULL, NULL); + + result = new OverlayRef(); + result->mOverlayHandle = handle; + result->mOverlayChannel = overlay; + result->mWidth = w; + result->mHeight = h; + result->mFormat = f; + result->mWidthStride = ws; + result->mHeightStride = hs; + } + return result; +} + +status_t OverlayRef::writeToParcel(Parcel* reply, const sp& o) { + if (o != NULL) { + reply->writeStrongBinder(o->mOverlayChannel->asBinder()); + reply->writeInt32(o->mWidth); + reply->writeInt32(o->mHeight); + reply->writeInt32(o->mFormat); + reply->writeInt32(o->mWidthStride); + reply->writeInt32(o->mHeightStride); + reply->writeNativeHandle(*(o->mOverlayHandle)); + } else { + reply->writeStrongBinder(NULL); + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp new file mode 100644 index 000000000..b65ed9736 --- /dev/null +++ b/libs/ui/PixelFormat.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2007 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 + +namespace android { + +size_t PixelFormatInfo::getScanlineSize(unsigned int width) const +{ + size_t size; + if ((components >= 6) && (components <= 8)) { + // YCbCr formats are differents. + size = (width * bitsPerPixel)>>3; + } else { + size = width * bytesPerPixel; + } + return size; +} + +ssize_t bytesPerPixel(PixelFormat format) +{ + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + return (err < 0) ? err : info.bytesPerPixel; +} + +ssize_t bitsPerPixel(PixelFormat format) +{ + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + return (err < 0) ? err : info.bitsPerPixel; +} + +status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) +{ + if (format < 0) + return BAD_VALUE; + + if (info->version != sizeof(PixelFormatInfo)) + return INVALID_OPERATION; + + size_t numEntries; + const GGLFormat *i = gglGetPixelFormatTable(&numEntries) + format; + bool valid = uint32_t(format) < numEntries; + if (!valid) { + return BAD_INDEX; + } + + #define COMPONENT(name) \ + case GGL_##name: info->components = PixelFormatInfo::name; break; + + switch (i->components) { + COMPONENT(ALPHA) + COMPONENT(RGB) + COMPONENT(RGBA) + COMPONENT(LUMINANCE) + COMPONENT(LUMINANCE_ALPHA) + COMPONENT(Y_CB_CR_SP) + COMPONENT(Y_CB_CR_P) + COMPONENT(Y_CB_CR_I) + default: + return BAD_INDEX; + } + + #undef COMPONENT + + info->format = format; + info->bytesPerPixel = i->size; + info->bitsPerPixel = i->bitsPerPixel; + info->h_alpha = i->ah; + info->l_alpha = i->al; + info->h_red = i->rh; + info->l_red = i->rl; + info->h_green = i->gh; + info->l_green = i->gl; + info->h_blue = i->bh; + info->l_blue = i->bl; + + return NO_ERROR; +} + +}; // namespace android + diff --git a/libs/ui/Point.cpp b/libs/ui/Point.cpp new file mode 100644 index 000000000..438d49fa4 --- /dev/null +++ b/libs/ui/Point.cpp @@ -0,0 +1,11 @@ +/* + * Point.cpp + * Android + * + * Created on 11/16/2006. + * Copyright 2005 The Android Open Source Project + * + */ + +#include + diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp new file mode 100644 index 000000000..99e68bb17 --- /dev/null +++ b/libs/ui/Rect.cpp @@ -0,0 +1,86 @@ +/* + * Rect.cpp + * Android + * + * Created on 10/14/05. + * Copyright 2005 The Android Open Source Project + * + */ + +#include + +namespace android { + +inline int min(int a, int b) { + return (ab) ? a : b; +} + +void Rect::makeInvalid() { + left = 0; + top = 0; + right = -1; + bottom = -1; +} + +bool Rect::operator < (const Rect& rhs) const +{ + if (topleft = max(left, with.left); + result->top = max(top, with.top); + result->right = min(right, with.right); + result->bottom = min(bottom, with.bottom); + return !(result->isEmpty()); +} + +}; // namespace android diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp new file mode 100644 index 000000000..26e694a70 --- /dev/null +++ b/libs/ui/Region.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "Region" + +#include +#include +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +Region::Region() +{ +} + +Region::Region(const Region& rhs) + : mRegion(rhs.mRegion) +{ +} + +Region::Region(const SkRegion& rhs) + : mRegion(rhs) +{ +} + +Region::~Region() +{ +} + +Region::Region(const Rect& rhs) +{ + set(rhs); +} + +Region::Region(const Parcel& parcel) +{ + read(parcel); +} + +Region::Region(const void* buffer) +{ + read(buffer); +} + +Region& Region::operator = (const Region& rhs) +{ + mRegion = rhs.mRegion; + return *this; +} + +const SkRegion& Region::toSkRegion() const +{ + return mRegion; +} + +Rect Region::bounds() const +{ + const SkIRect& b(mRegion.getBounds()); + return Rect(b.fLeft, b.fTop, b.fRight, b.fBottom); +} + +void Region::clear() +{ + mRegion.setEmpty(); +} + +void Region::set(const Rect& r) +{ + SkIRect ir; + ir.set(r.left, r.top, r.right, r.bottom); + mRegion.setRect(ir); +} + +// ---------------------------------------------------------------------------- + +Region& Region::orSelf(const Rect& r) +{ + SkIRect ir; + ir.set(r.left, r.top, r.right, r.bottom); + mRegion.op(ir, SkRegion::kUnion_Op); + return *this; +} + +Region& Region::andSelf(const Rect& r) +{ + SkIRect ir; + ir.set(r.left, r.top, r.right, r.bottom); + mRegion.op(ir, SkRegion::kIntersect_Op); + return *this; +} + +// ---------------------------------------------------------------------------- + +Region& Region::orSelf(const Region& rhs) { + mRegion.op(rhs.mRegion, SkRegion::kUnion_Op); + return *this; +} + +Region& Region::andSelf(const Region& rhs) { + mRegion.op(rhs.mRegion, SkRegion::kIntersect_Op); + return *this; +} + +Region& Region::subtractSelf(const Region& rhs) { + mRegion.op(rhs.mRegion, SkRegion::kDifference_Op); + return *this; +} + +Region& Region::translateSelf(int x, int y) { + if (x|y) mRegion.translate(x, y); + return *this; +} + +Region Region::merge(const Region& rhs) const { + Region result; + result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kUnion_Op); + return result; +} + +Region Region::intersect(const Region& rhs) const { + Region result; + result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kIntersect_Op); + return result; +} + +Region Region::subtract(const Region& rhs) const { + Region result; + result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kDifference_Op); + return result; +} + +Region Region::translate(int x, int y) const { + Region result; + mRegion.translate(x, y, &result.mRegion); + return result; +} + +// ---------------------------------------------------------------------------- + +Region& Region::orSelf(const Region& rhs, int dx, int dy) { + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + mRegion.op(r, SkRegion::kUnion_Op); + return *this; +} + +Region& Region::andSelf(const Region& rhs, int dx, int dy) { + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + mRegion.op(r, SkRegion::kIntersect_Op); + return *this; +} + +Region& Region::subtractSelf(const Region& rhs, int dx, int dy) { + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + mRegion.op(r, SkRegion::kDifference_Op); + return *this; +} + +Region Region::merge(const Region& rhs, int dx, int dy) const { + Region result; + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + result.mRegion.op(mRegion, r, SkRegion::kUnion_Op); + return result; +} + +Region Region::intersect(const Region& rhs, int dx, int dy) const { + Region result; + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + result.mRegion.op(mRegion, r, SkRegion::kIntersect_Op); + return result; +} + +Region Region::subtract(const Region& rhs, int dx, int dy) const { + Region result; + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + result.mRegion.op(mRegion, r, SkRegion::kDifference_Op); + return result; +} + +// ---------------------------------------------------------------------------- + +Region::iterator::iterator(const Region& r) + : mIt(r.mRegion) +{ +} + +int Region::iterator::iterate(Rect* rect) +{ + if (mIt.done()) + return 0; + const SkIRect& r(mIt.rect()); + rect->left = r.fLeft; + rect->top = r.fTop; + rect->right = r.fRight; + rect->bottom= r.fBottom; + mIt.next(); + return 1; +} + +// ---------------------------------------------------------------------------- + +// we write a 4byte size ahead of the actual region, so we know how much we'll need for reading + +status_t Region::write(Parcel& parcel) const +{ + int32_t size = mRegion.flatten(NULL); + parcel.writeInt32(size); + mRegion.flatten(parcel.writeInplace(size)); + return NO_ERROR; +} + +status_t Region::read(const Parcel& parcel) +{ + size_t size = parcel.readInt32(); + mRegion.unflatten(parcel.readInplace(size)); + return NO_ERROR; +} + +ssize_t Region::write(void* buffer, size_t size) const +{ + size_t sizeNeeded = mRegion.flatten(NULL); + if (sizeNeeded > size) return NO_MEMORY; + return mRegion.flatten(buffer); +} + +ssize_t Region::read(const void* buffer) +{ + return mRegion.unflatten(buffer); +} + +ssize_t Region::writeEmpty(void* buffer, size_t size) +{ + if (size < 4) return NO_MEMORY; + // this needs to stay in sync with SkRegion + *static_cast(buffer) = -1; + return 4; +} + +bool Region::isEmpty(void* buffer) +{ + // this needs to stay in sync with SkRegion + return *static_cast(buffer) == -1; +} + +size_t Region::rects(Vector& rectList) const +{ + rectList.clear(); + if (!isEmpty()) { + SkRegion::Iterator iterator(mRegion); + while( !iterator.done() ) { + const SkIRect& ir(iterator.rect()); + rectList.push(Rect(ir.fLeft, ir.fTop, ir.fRight, ir.fBottom)); + iterator.next(); + } + } + return rectList.size(); +} + +void Region::dump(String8& out, const char* what, uint32_t flags) const +{ + (void)flags; + Vector r; + rects(r); + + size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, " Region %s (this=%p, count=%d)\n", what, this, r.size()); + out.append(buffer); + for (size_t i=0 ; i r; + rects(r); + LOGD(" Region %s (this=%p, count=%d)\n", what, this, r.size()); + for (size_t i=0 ; i +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace android { + +// --------------------------------------------------------------------------- + +Surface::Surface(const sp& client, + const sp& surface, + const ISurfaceFlingerClient::surface_data_t& data, + uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, + bool owner) + : mClient(client), mSurface(surface), + mToken(data.token), mIdentity(data.identity), + mFormat(format), mFlags(flags), mOwner(owner) +{ + mSwapRectangle.makeInvalid(); + mSurfaceHeapBase[0] = 0; + mSurfaceHeapBase[1] = 0; + mHeap[0] = data.heap[0]; + mHeap[1] = data.heap[1]; +} + +Surface::Surface(Surface const* rhs) + : mOwner(false) +{ + mToken = rhs->mToken; + mIdentity= rhs->mIdentity; + mClient = rhs->mClient; + mSurface = rhs->mSurface; + mHeap[0] = rhs->mHeap[0]; + mHeap[1] = rhs->mHeap[1]; + mFormat = rhs->mFormat; + mFlags = rhs->mFlags; + mSurfaceHeapBase[0] = rhs->mSurfaceHeapBase[0]; + mSurfaceHeapBase[1] = rhs->mSurfaceHeapBase[1]; + mSwapRectangle.makeInvalid(); +} + +Surface::~Surface() +{ + if (mOwner && mToken>=0 && mClient!=0) { + mClient->destroySurface(mToken); + } + mClient.clear(); + mSurface.clear(); + mHeap[0].clear(); + mHeap[1].clear(); + IPCThreadState::self()->flushCommands(); +} + +sp Surface::dup() const +{ + Surface const * r = this; + if (this && mOwner) { + // the only reason we need to do this is because of Java's garbage + // collector: because we're creating a copy of the Surface + // instead of a reference, we can garantee that when our last + // reference goes away, the real surface will be deleted. + // Without this hack (the code is correct too), we'd have to + // wait for a GC for the surface to go away. + r = new Surface(this); + } + return const_cast(r); +} + +status_t Surface::nextBuffer(SurfaceInfo* info) { + return mClient->nextBuffer(this, info); +} + +status_t Surface::lock(SurfaceInfo* info, bool blocking) { + return Surface::lock(info, NULL, blocking); +} + +status_t Surface::lock(SurfaceInfo* info, Region* dirty, bool blocking) { + if (heapBase(0) == 0) return INVALID_OPERATION; + if (heapBase(1) == 0) return INVALID_OPERATION; + return mClient->lockSurface(this, info, dirty, blocking); +} + +status_t Surface::unlockAndPost() { + if (heapBase(0) == 0) return INVALID_OPERATION; + if (heapBase(1) == 0) return INVALID_OPERATION; + return mClient->unlockAndPostSurface(this); +} + +status_t Surface::unlock() { + if (heapBase(0) == 0) return INVALID_OPERATION; + if (heapBase(1) == 0) return INVALID_OPERATION; + return mClient->unlockSurface(this); +} + +status_t Surface::setLayer(int32_t layer) { + return mClient->setLayer(this, layer); +} +status_t Surface::setPosition(int32_t x, int32_t y) { + return mClient->setPosition(this, x, y); +} +status_t Surface::setSize(uint32_t w, uint32_t h) { + return mClient->setSize(this, w, h); +} +status_t Surface::hide() { + return mClient->hide(this); +} +status_t Surface::show(int32_t layer) { + return mClient->show(this, layer); +} +status_t Surface::freeze() { + return mClient->freeze(this); +} +status_t Surface::unfreeze() { + return mClient->unfreeze(this); +} +status_t Surface::setFlags(uint32_t flags, uint32_t mask) { + return mClient->setFlags(this, flags, mask); +} +status_t Surface::setTransparentRegionHint(const Region& transparent) { + return mClient->setTransparentRegionHint(this, transparent); +} +status_t Surface::setAlpha(float alpha) { + return mClient->setAlpha(this, alpha); +} +status_t Surface::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + return mClient->setMatrix(this, dsdx, dtdx, dsdy, dtdy); +} +status_t Surface::setFreezeTint(uint32_t tint) { + return mClient->setFreezeTint(this, tint); +} + +Region Surface::dirtyRegion() const { + return mDirtyRegion; +} +void Surface::setDirtyRegion(const Region& region) const { + mDirtyRegion = region; +} +const Rect& Surface::swapRectangle() const { + return mSwapRectangle; +} +void Surface::setSwapRectangle(const Rect& r) { + mSwapRectangle = r; +} + +sp Surface::readFromParcel(Parcel* parcel) +{ + sp client; + ISurfaceFlingerClient::surface_data_t data; + sp clientBinder= parcel->readStrongBinder(); + sp surface = interface_cast(parcel->readStrongBinder()); + data.heap[0] = interface_cast(parcel->readStrongBinder()); + data.heap[1] = interface_cast(parcel->readStrongBinder()); + data.token = parcel->readInt32(); + data.identity = parcel->readInt32(); + PixelFormat format = parcel->readInt32(); + uint32_t flags = parcel->readInt32(); + + if (clientBinder != NULL) + client = SurfaceComposerClient::clientForConnection(clientBinder); + + return new Surface(client, surface, data, 0, 0, format, flags, false); +} + +status_t Surface::writeToParcel(const sp& surface, Parcel* parcel) +{ + uint32_t flags=0; + uint32_t format=0; + SurfaceID token = -1; + uint32_t identity = 0; + sp client; + sp sur; + sp heap[2]; + if (surface->isValid()) { + token = surface->mToken; + identity = surface->mIdentity; + client = surface->mClient; + sur = surface->mSurface; + heap[0] = surface->mHeap[0]; + heap[1] = surface->mHeap[1]; + format = surface->mFormat; + flags = surface->mFlags; + } + parcel->writeStrongBinder(client!=0 ? client->connection() : NULL); + parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); + parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL); + parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL); + parcel->writeInt32(token); + parcel->writeInt32(identity); + parcel->writeInt32(format); + parcel->writeInt32(flags); + return NO_ERROR; +} + +bool Surface::isSameSurface(const sp& lhs, const sp& rhs) +{ + if (lhs == 0 || rhs == 0) + return false; + return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); +} + +void* Surface::heapBase(int i) const +{ + void* heapBase = mSurfaceHeapBase[i]; + // map lazily so it doesn't get mapped in clients that don't need it + if (heapBase == 0) { + const sp& heap(mHeap[i]); + if (heap != 0) { + heapBase = static_cast(heap->base()); + if (heapBase == MAP_FAILED) { + heapBase = NULL; + LOGE("Couldn't map Surface's heap (binder=%p, heap=%p)", + heap->asBinder().get(), heap.get()); + } + mSurfaceHeapBase[i] = heapBase; + } + } + return heapBase; +} + +}; // namespace android + diff --git a/libs/ui/SurfaceComposerClient.cpp b/libs/ui/SurfaceComposerClient.cpp new file mode 100644 index 000000000..9354a7a40 --- /dev/null +++ b/libs/ui/SurfaceComposerClient.cpp @@ -0,0 +1,1026 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "SurfaceComposerClient" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#define VERBOSE(...) ((void)0) +//#define VERBOSE LOGD + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { + +// --------------------------------------------------------------------------- + +// Must not be holding SurfaceComposerClient::mLock when acquiring gLock here. +static Mutex gLock; +static sp gSurfaceManager; +static DefaultKeyedVector< sp, sp > gActiveConnections; +static SortedVector > gOpenTransactions; +static sp gServerCblkMemory; +static volatile surface_flinger_cblk_t* gServerCblk; + +const sp& _get_surface_manager() +{ + if (gSurfaceManager != 0) { + return gSurfaceManager; + } + + sp binder; + sp sm = defaultServiceManager(); + do { + binder = sm->getService(String16("SurfaceFlinger")); + if (binder == 0) { + LOGW("SurfaceFlinger not published, waiting..."); + usleep(500000); // 0.5 s + } + } while(binder == 0); + sp sc(interface_cast(binder)); + + Mutex::Autolock _l(gLock); + if (gSurfaceManager == 0) { + gSurfaceManager = sc; + } + return gSurfaceManager; +} + +static volatile surface_flinger_cblk_t const * get_cblk() +{ + if (gServerCblk == 0) { + const sp& sm(_get_surface_manager()); + Mutex::Autolock _l(gLock); + if (gServerCblk == 0) { + gServerCblkMemory = sm->getCblk(); + LOGE_IF(gServerCblkMemory==0, "Can't get server control block"); + gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->pointer(); + LOGE_IF(gServerCblk==0, "Can't get server control block address"); + } + } + return gServerCblk; +} + +// --------------------------------------------------------------------------- + +static void copyBlt(const GGLSurface& dst, + const GGLSurface& src, const Region& reg) +{ + Region::iterator iterator(reg); + if (iterator) { + // NOTE: dst and src must be the same format + Rect r; + const size_t bpp = bytesPerPixel(src.format); + const size_t dbpr = dst.stride * bpp; + const size_t sbpr = src.stride * bpp; + while (iterator.iterate(&r)) { + ssize_t h = r.bottom - r.top; + if (h) { + size_t size = (r.right - r.left) * bpp; + uint8_t* s = src.data + (r.left + src.stride * r.top) * bpp; + uint8_t* d = dst.data + (r.left + dst.stride * r.top) * bpp; + if (dbpr==sbpr && size==sbpr) { + size *= h; + h = 1; + } + do { + memcpy(d, s, size); + d += dbpr; + s += sbpr; + } while (--h > 0); + } + } + } +} + +// --------------------------------------------------------------------------- + +surface_flinger_cblk_t::surface_flinger_cblk_t() +{ +} + +// --------------------------------------------------------------------------- + +per_client_cblk_t::per_client_cblk_t() +{ +} + +// these functions are used by the clients +inline status_t per_client_cblk_t::validate(size_t i) const { + if (uint32_t(i) >= NUM_LAYERS_MAX) + return BAD_INDEX; + if (layers[i].swapState & eInvalidSurface) + return NO_MEMORY; + return NO_ERROR; +} + +int32_t per_client_cblk_t::lock_layer(size_t i, uint32_t flags) +{ + int32_t index; + uint32_t state; + int timeout = 0; + status_t result; + layer_cblk_t * const layer = layers + i; + const bool blocking = flags & BLOCKING; + const bool inspect = flags & INSPECT; + + do { + state = layer->swapState; + + if (UNLIKELY((state&(eFlipRequested|eNextFlipPending)) == eNextFlipPending)) { + LOGE("eNextFlipPending set but eFlipRequested not set, " + "layer=%d (lcblk=%p), state=%08x", + int(i), layer, int(state)); + return INVALID_OPERATION; + } + + if (UNLIKELY(state&eLocked)) { + LOGE("eLocked set when entering lock_layer(), " + "layer=%d (lcblk=%p), state=%08x", + int(i), layer, int(state)); + return WOULD_BLOCK; + } + + + if (state & (eFlipRequested | eNextFlipPending | eResizeRequested + | eInvalidSurface)) + { + int32_t resizeIndex; + Mutex::Autolock _l(lock); + // might block for a very short amount of time + // will never cause the server to block (trylock()) + + goto start_loop_here; + + // We block the client if: + // eNextFlipPending: we've used both buffers already, so we need to + // wait for one to become availlable. + // eResizeRequested: the buffer we're going to acquire is being + // resized. Block until it is done. + // eFlipRequested && eBusy: the buffer we're going to acquire is + // currently in use by the server. + // eInvalidSurface: this is a special case, we don't block in this + // case, we just return an error. + + while((state & (eNextFlipPending|eInvalidSurface)) || + (state & ((resizeIndex) ? eResizeBuffer1 : eResizeBuffer0)) || + ((state & (eFlipRequested|eBusy)) == (eFlipRequested|eBusy)) ) + { + if (state & eInvalidSurface) + return NO_MEMORY; + + if (!blocking) + return WOULD_BLOCK; + + timeout = 0; + result = cv.waitRelative(lock, seconds(1)); + if (__builtin_expect(result!=NO_ERROR, false)) { + const int newState = layer->swapState; + LOGW( "lock_layer timed out (is the CPU pegged?) " + "layer=%d, lcblk=%p, state=%08x (was %08x)", + int(i), layer, newState, int(state)); + timeout = newState != int(state); + } + + start_loop_here: + state = layer->swapState; + resizeIndex = (state&eIndex) ^ ((state&eFlipRequested)>>1); + } + + LOGW_IF(timeout, + "lock_layer() timed out but didn't appear to need " + "to be locked and we recovered " + "(layer=%d, lcblk=%p, state=%08x)", + int(i), layer, int(state)); + } + + // eFlipRequested is not set and cannot be set by another thread: it's + // safe to use the first buffer without synchronization. + + // Choose the index depending on eFlipRequested. + // When it's set, choose the 'other' buffer. + index = (state&eIndex) ^ ((state&eFlipRequested)>>1); + + // make sure this buffer is valid + if (layer->surface[index].bits_offset < 0) { + return status_t(layer->surface[index].bits_offset); + } + + if (inspect) { + // we just want to inspect this layer. don't lock it. + goto done; + } + + // last thing before we're done, we need to atomically lock the state + } while (android_atomic_cmpxchg(state, state|eLocked, &(layer->swapState))); + + VERBOSE("locked layer=%d (lcblk=%p), buffer=%d, state=0x%08x", + int(i), layer, int(index), int(state)); + + // store the index of the locked buffer (for client use only) + layer->flags &= ~eBufferIndex; + layer->flags |= ((index << eBufferIndexShift) & eBufferIndex); + +done: + return index; +} + +uint32_t per_client_cblk_t::unlock_layer_and_post(size_t i) +{ + // atomically set eFlipRequested and clear eLocked and optionnaly + // set eNextFlipPending if eFlipRequested was already set + + layer_cblk_t * const layer = layers + i; + int32_t oldvalue, newvalue; + do { + oldvalue = layer->swapState; + // get current value + + newvalue = oldvalue & ~eLocked; + // clear eLocked + + newvalue |= eFlipRequested; + // set eFlipRequested + + if (oldvalue & eFlipRequested) + newvalue |= eNextFlipPending; + // if eFlipRequested was alread set, set eNextFlipPending + + } while (android_atomic_cmpxchg(oldvalue, newvalue, &(layer->swapState))); + + VERBOSE("request pageflip for layer=%d, buffer=%d, state=0x%08x", + int(i), int((layer->flags & eBufferIndex) >> eBufferIndexShift), + int(newvalue)); + + // from this point, the server can kick in at anytime and use the first + // buffer, so we cannot use it anymore, and we must use the 'other' + // buffer instead (or wait if it is not availlable yet, see lock_layer). + + return newvalue; +} + +void per_client_cblk_t::unlock_layer(size_t i) +{ + layer_cblk_t * const layer = layers + i; + android_atomic_and(~eLocked, &layer->swapState); +} + +// --------------------------------------------------------------------------- + +static inline int compare_type( const layer_state_t& lhs, + const layer_state_t& rhs) { + if (lhs.surface < rhs.surface) return -1; + if (lhs.surface > rhs.surface) return 1; + return 0; +} + +SurfaceComposerClient::SurfaceComposerClient() +{ + const sp& sm(_get_surface_manager()); + if (sm == 0) { + _init(0, 0); + return; + } + + _init(sm, sm->createConnection()); + + if (mClient != 0) { + Mutex::Autolock _l(gLock); + VERBOSE("Adding client %p to map", this); + gActiveConnections.add(mClient->asBinder(), this); + } +} + +SurfaceComposerClient::SurfaceComposerClient( + const sp& sm, const sp& conn) +{ + _init(sm, interface_cast(conn)); +} + +void SurfaceComposerClient::_init( + const sp& sm, const sp& conn) +{ + VERBOSE("Creating client %p, conn %p", this, conn.get()); + + mSignalServer = 0; + mPrebuiltLayerState = 0; + mTransactionOpen = 0; + mStatus = NO_ERROR; + mControl = 0; + + mClient = conn; + if (mClient == 0) { + mStatus = NO_INIT; + return; + } + + mClient->getControlBlocks(&mControlMemory); + mSignalServer = new SurfaceFlingerSynchro(sm); + mControl = static_cast(mControlMemory->pointer()); +} + +SurfaceComposerClient::~SurfaceComposerClient() +{ + VERBOSE("Destroying client %p, conn %p", this, mClient.get()); + dispose(); +} + +status_t SurfaceComposerClient::initCheck() const +{ + return mStatus; +} + +status_t SurfaceComposerClient::validateSurface( + per_client_cblk_t const* cblk, Surface const * surface) +{ + SurfaceID index = surface->ID(); + if (cblk == 0) { + LOGE("cblk is null (surface id=%d, identity=%u)", + index, surface->getIdentity()); + return NO_INIT; + } + + status_t err = cblk->validate(index); + if (err != NO_ERROR) { + LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", + index, surface->getIdentity(), err, strerror(-err)); + return err; + } + + if (surface->getIdentity() != uint32_t(cblk->layers[index].identity)) { + LOGE("using an invalid surface id=%d, identity=%u should be %d", + index, surface->getIdentity(), cblk->layers[index].identity); + return NO_INIT; + } + + return NO_ERROR; +} + +sp SurfaceComposerClient::connection() const +{ + return (mClient != 0) ? mClient->asBinder() : 0; +} + +sp +SurfaceComposerClient::clientForConnection(const sp& conn) +{ + sp client; + + { // scope for lock + Mutex::Autolock _l(gLock); + client = gActiveConnections.valueFor(conn); + } + + if (client == 0) { + // Need to make a new client. + const sp& sm(_get_surface_manager()); + client = new SurfaceComposerClient(sm, conn); + if (client != 0 && client->initCheck() == NO_ERROR) { + Mutex::Autolock _l(gLock); + gActiveConnections.add(conn, client); + //LOGD("we have %d connections", gActiveConnections.size()); + } else { + client.clear(); + } + } + + return client; +} + +void SurfaceComposerClient::dispose() +{ + // this can be called more than once. + + sp controlMemory; + sp client; + sp surfaceHeap; + + { + Mutex::Autolock _lg(gLock); + Mutex::Autolock _lm(mLock); + + delete mSignalServer; + mSignalServer = 0; + + if (mClient != 0) { + client = mClient; + mClient.clear(); + + ssize_t i = gActiveConnections.indexOfKey(client->asBinder()); + if (i >= 0 && gActiveConnections.valueAt(i) == this) { + VERBOSE("Removing client %p from map at %d", this, int(i)); + gActiveConnections.removeItemsAt(i); + } + } + + delete mPrebuiltLayerState; + mPrebuiltLayerState = 0; + controlMemory = mControlMemory; + surfaceHeap = mSurfaceHeap; + mControlMemory.clear(); + mSurfaceHeap.clear(); + mControl = 0; + mStatus = NO_INIT; + } +} + +status_t SurfaceComposerClient::getDisplayInfo( + DisplayID dpy, DisplayInfo* info) +{ + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + + info->w = dcblk->w; + info->h = dcblk->h; + info->orientation = dcblk->orientation; + info->xdpi = dcblk->xdpi; + info->ydpi = dcblk->ydpi; + info->fps = dcblk->fps; + info->density = dcblk->density; + return getPixelFormatInfo(dcblk->format, &(info->pixelFormatInfo)); +} + +ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) +{ + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->w; +} + +ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) +{ + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->h; +} + +ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) +{ + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->orientation; +} + +ssize_t SurfaceComposerClient::getNumberOfDisplays() +{ + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + uint32_t connected = cblk->connected; + int n = 0; + while (connected) { + if (connected&1) n++; + connected >>= 1; + } + return n; +} + +sp SurfaceComposerClient::createSurface( + int pid, + DisplayID display, + uint32_t w, + uint32_t h, + PixelFormat format, + uint32_t flags) +{ + sp result; + if (mStatus == NO_ERROR) { + ISurfaceFlingerClient::surface_data_t data; + sp surface = mClient->createSurface(&data, pid, + display, w, h, format, flags); + if (surface != 0) { + if (uint32_t(data.token) < NUM_LAYERS_MAX) { + result = new Surface(this, surface, data, w, h, format, flags); + } + } + } + return result; +} + +status_t SurfaceComposerClient::destroySurface(SurfaceID sid) +{ + if (mStatus != NO_ERROR) + return mStatus; + + // it's okay to destroy a surface while a transaction is open, + // (transactions really are a client-side concept) + // however, this indicates probably a misuse of the API or a bug + // in the client code. + LOGW_IF(mTransactionOpen, + "Destroying surface while a transaction is open. " + "Client %p: destroying surface %d, mTransactionOpen=%d", + this, sid, mTransactionOpen); + + status_t err = mClient->destroySurface(sid); + return err; +} + +status_t SurfaceComposerClient::nextBuffer(Surface* surface, + Surface::SurfaceInfo* info) +{ + SurfaceID index = surface->ID(); + per_client_cblk_t* const cblk = mControl; + status_t err = validateSurface(cblk, surface); + if (err != NO_ERROR) + return err; + + int32_t backIdx = surface->mBackbufferIndex; + layer_cblk_t* const lcblk = &(cblk->layers[index]); + const surface_info_t* const front = lcblk->surface + (1-backIdx); + info->w = front->w; + info->h = front->h; + info->format = front->format; + info->base = surface->heapBase(1-backIdx); + info->bits = reinterpret_cast(intptr_t(info->base) + front->bits_offset); + info->bpr = front->bpr; + + return 0; +} + +status_t SurfaceComposerClient::lockSurface( + Surface* surface, + Surface::SurfaceInfo* other, + Region* dirty, + bool blocking) +{ + Mutex::Autolock _l(surface->getLock()); + + SurfaceID index = surface->ID(); + per_client_cblk_t* const cblk = mControl; + status_t err = validateSurface(cblk, surface); + if (err != NO_ERROR) + return err; + + int32_t backIdx = cblk->lock_layer(size_t(index), + per_client_cblk_t::BLOCKING); + if (backIdx >= 0) { + surface->mBackbufferIndex = backIdx; + layer_cblk_t* const lcblk = &(cblk->layers[index]); + const surface_info_t* const back = lcblk->surface + backIdx; + const surface_info_t* const front = lcblk->surface + (1-backIdx); + other->w = back->w; + other->h = back->h; + other->format = back->format; + other->base = surface->heapBase(backIdx); + other->bits = reinterpret_cast(intptr_t(other->base) + back->bits_offset); + other->bpr = back->bpr; + + const Rect bounds(other->w, other->h); + Region newDirtyRegion; + + if (back->flags & surface_info_t::eBufferDirty) { + /* it is safe to write *back here, because we're guaranteed + * SurfaceFlinger is not touching it (since it just granted + * access to us) */ + const_cast(back)->flags &= + ~surface_info_t::eBufferDirty; + + // content is meaningless in this case and the whole surface + // needs to be redrawn. + + newDirtyRegion.set(bounds); + if (dirty) { + *dirty = newDirtyRegion; + } + + //if (bytesPerPixel(other->format) == 4) { + // android_memset32( + // (uint32_t*)other->bits, 0xFF00FF00, other->h * other->bpr); + //} else { + // android_memset16( // fill with green + // (uint16_t*)other->bits, 0x7E0, other->h * other->bpr); + //} + } + else + { + if (dirty) { + dirty->andSelf(Region(bounds)); + newDirtyRegion = *dirty; + } else { + newDirtyRegion.set(bounds); + } + + Region copyback; + if (!(lcblk->flags & eNoCopyBack)) { + const Region previousDirtyRegion(surface->dirtyRegion()); + copyback = previousDirtyRegion.subtract(newDirtyRegion); + } + + if (!copyback.isEmpty()) { + // copy front to back + GGLSurface cb; + cb.version = sizeof(GGLSurface); + cb.width = back->w; + cb.height = back->h; + cb.stride = back->stride; + cb.data = (GGLubyte*)surface->heapBase(backIdx); + cb.data += back->bits_offset; + cb.format = back->format; + + GGLSurface t; + t.version = sizeof(GGLSurface); + t.width = front->w; + t.height = front->h; + t.stride = front->stride; + t.data = (GGLubyte*)surface->heapBase(1-backIdx); + t.data += front->bits_offset; + t.format = front->format; + + //const Region copyback(lcblk->region + 1-backIdx); + copyBlt(cb, t, copyback); + } + } + + // update dirty region + surface->setDirtyRegion(newDirtyRegion); + } + return (backIdx < 0) ? status_t(backIdx) : status_t(NO_ERROR); +} + +void SurfaceComposerClient::_signal_server() +{ + mSignalServer->signal(); +} + +void SurfaceComposerClient::_send_dirty_region( + layer_cblk_t* lcblk, const Region& dirty) +{ + const int32_t index = (lcblk->flags & eBufferIndex) >> eBufferIndexShift; + flat_region_t* flat_region = lcblk->region + index; + status_t err = dirty.write(flat_region, sizeof(flat_region_t)); + if (err < NO_ERROR) { + // region doesn't fit, use the bounds + const Region reg(dirty.bounds()); + reg.write(flat_region, sizeof(flat_region_t)); + } +} + +status_t SurfaceComposerClient::unlockAndPostSurface(Surface* surface) +{ + Mutex::Autolock _l(surface->getLock()); + + SurfaceID index = surface->ID(); + per_client_cblk_t* const cblk = mControl; + status_t err = validateSurface(cblk, surface); + if (err != NO_ERROR) + return err; + + Region dirty(surface->dirtyRegion()); + const Rect& swapRect(surface->swapRectangle()); + if (swapRect.isValid()) { + dirty.set(swapRect); + } + + // transmit the dirty region + layer_cblk_t* const lcblk = &(cblk->layers[index]); + _send_dirty_region(lcblk, dirty); + uint32_t newstate = cblk->unlock_layer_and_post(size_t(index)); + if (!(newstate & eNextFlipPending)) + _signal_server(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::unlockSurface(Surface* surface) +{ + Mutex::Autolock _l(surface->getLock()); + + SurfaceID index = surface->ID(); + per_client_cblk_t* const cblk = mControl; + status_t err = validateSurface(cblk, surface); + if (err != NO_ERROR) + return err; + + layer_cblk_t* const lcblk = &(cblk->layers[index]); + cblk->unlock_layer(size_t(index)); + return NO_ERROR; +} + +void SurfaceComposerClient::openGlobalTransaction() +{ + Mutex::Autolock _l(gLock); + + if (gOpenTransactions.size()) { + LOGE("openGlobalTransaction() called more than once. skipping."); + return; + } + + const size_t N = gActiveConnections.size(); + VERBOSE("openGlobalTransaction (%ld clients)", N); + for (size_t i=0; i client(gActiveConnections.valueAt(i)); + if (gOpenTransactions.indexOf(client) < 0) { + if (client->openTransaction() == NO_ERROR) { + if (gOpenTransactions.add(client) < 0) { + // Ooops! + LOGE( "Unable to add a SurfaceComposerClient " + "to the global transaction set (out of memory?)"); + client->closeTransaction(); + // let it go, it'll fail later when the user + // tries to do something with the transaction + } + } else { + LOGE("openTransaction on client %p failed", client.get()); + // let it go, it'll fail later when the user + // tries to do something with the transaction + } + } + } +} + +void SurfaceComposerClient::closeGlobalTransaction() +{ + gLock.lock(); + SortedVector< sp > clients(gOpenTransactions); + gOpenTransactions.clear(); + gLock.unlock(); + + const size_t N = clients.size(); + VERBOSE("closeGlobalTransaction (%ld clients)", N); + if (N == 1) { + clients[0]->closeTransaction(); + } else { + const sp& sm(_get_surface_manager()); + sm->openGlobalTransaction(); + for (size_t i=0; icloseTransaction(); + } + sm->closeGlobalTransaction(); + } +} + +status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) +{ + const sp& sm(_get_surface_manager()); + return sm->freezeDisplay(dpy, flags); +} + +status_t SurfaceComposerClient::unfreezeDisplay(DisplayID dpy, uint32_t flags) +{ + const sp& sm(_get_surface_manager()); + return sm->unfreezeDisplay(dpy, flags); +} + +int SurfaceComposerClient::setOrientation(DisplayID dpy, int orientation) +{ + const sp& sm(_get_surface_manager()); + return sm->setOrientation(dpy, orientation); +} + +status_t SurfaceComposerClient::openTransaction() +{ + if (mStatus != NO_ERROR) + return mStatus; + Mutex::Autolock _l(mLock); + VERBOSE( "openTransaction (client %p, mTransactionOpen=%d)", + this, mTransactionOpen); + mTransactionOpen++; + if (mPrebuiltLayerState == 0) { + mPrebuiltLayerState = new layer_state_t; + } + return NO_ERROR; +} + + +status_t SurfaceComposerClient::closeTransaction() +{ + if (mStatus != NO_ERROR) + return mStatus; + + Mutex::Autolock _l(mLock); + + VERBOSE( "closeTransaction (client %p, mTransactionOpen=%d)", + this, mTransactionOpen); + + if (mTransactionOpen <= 0) { + LOGE( "closeTransaction (client %p, mTransactionOpen=%d) " + "called more times than openTransaction()", + this, mTransactionOpen); + return INVALID_OPERATION; + } + + if (mTransactionOpen >= 2) { + mTransactionOpen--; + return NO_ERROR; + } + + mTransactionOpen = 0; + const ssize_t count = mStates.size(); + if (count) { + mClient->setState(count, mStates.array()); + mStates.clear(); + } + return NO_ERROR; +} + +layer_state_t* SurfaceComposerClient::_get_state_l(const sp& surface) +{ + SurfaceID index = surface->ID(); + per_client_cblk_t* const cblk = mControl; + status_t err = validateSurface(cblk, surface.get()); + if (err != NO_ERROR) + return 0; + + // API usage error, do nothing. + if (mTransactionOpen<=0) { + LOGE("Not in transaction (client=%p, SurfaceID=%d, mTransactionOpen=%d", + this, int(index), mTransactionOpen); + return 0; + } + + // use mPrebuiltLayerState just to find out if we already have it + layer_state_t& dummy = *mPrebuiltLayerState; + dummy.surface = index; + ssize_t i = mStates.indexOf(dummy); + if (i < 0) { + // we don't have it, add an initialized layer_state to our list + i = mStates.add(dummy); + } + return mStates.editArray() + i; +} + +layer_state_t* SurfaceComposerClient::_lockLayerState(const sp& surface) +{ + layer_state_t* s; + mLock.lock(); + s = _get_state_l(surface); + if (!s) mLock.unlock(); + return s; +} + +void SurfaceComposerClient::_unlockLayerState() +{ + mLock.unlock(); +} + +status_t SurfaceComposerClient::setPosition(Surface* surface, int32_t x, int32_t y) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::ePositionChanged; + s->x = x; + s->y = y; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setSize(Surface* surface, uint32_t w, uint32_t h) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eSizeChanged; + s->w = w; + s->h = h; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setLayer(Surface* surface, int32_t z) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eLayerChanged; + s->z = z; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::hide(Surface* surface) +{ + return setFlags(surface, ISurfaceComposer::eLayerHidden, + ISurfaceComposer::eLayerHidden); +} + +status_t SurfaceComposerClient::show(Surface* surface, int32_t) +{ + return setFlags(surface, 0, ISurfaceComposer::eLayerHidden); +} + +status_t SurfaceComposerClient::freeze(Surface* surface) +{ + return setFlags(surface, ISurfaceComposer::eLayerFrozen, + ISurfaceComposer::eLayerFrozen); +} + +status_t SurfaceComposerClient::unfreeze(Surface* surface) +{ + return setFlags(surface, 0, ISurfaceComposer::eLayerFrozen); +} + +status_t SurfaceComposerClient::setFlags(Surface* surface, + uint32_t flags, uint32_t mask) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eVisibilityChanged; + s->flags &= ~mask; + s->flags |= (flags & mask); + s->mask |= mask; + _unlockLayerState(); + return NO_ERROR; +} + + +status_t SurfaceComposerClient::setTransparentRegionHint( + Surface* surface, const Region& transparentRegion) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eTransparentRegionChanged; + s->transparentRegion = transparentRegion; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setAlpha(Surface* surface, float alpha) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eAlphaChanged; + s->alpha = alpha; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setMatrix( + Surface* surface, + float dsdx, float dtdx, + float dsdy, float dtdy ) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eMatrixChanged; + layer_state_t::matrix22_t matrix; + matrix.dsdx = dsdx; + matrix.dtdx = dtdx; + matrix.dsdy = dsdy; + matrix.dtdy = dtdy; + s->matrix = matrix; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setFreezeTint(Surface* surface, uint32_t tint) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eFreezeTintChanged; + s->tint = tint; + _unlockLayerState(); + return NO_ERROR; +} + +}; // namespace android + diff --git a/libs/ui/SurfaceFlingerSynchro.cpp b/libs/ui/SurfaceFlingerSynchro.cpp new file mode 100644 index 000000000..5cd9755cc --- /dev/null +++ b/libs/ui/SurfaceFlingerSynchro.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2008 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. + */ + +#define LOG_TAG "SurfaceFlingerSynchro" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +SurfaceFlingerSynchro::Barrier::Barrier() + : state(CLOSED) { +} + +SurfaceFlingerSynchro::Barrier::~Barrier() { +} + +void SurfaceFlingerSynchro::Barrier::open() { + asm volatile ("":::"memory"); + Mutex::Autolock _l(lock); + state = OPENED; + cv.broadcast(); +} + +void SurfaceFlingerSynchro::Barrier::close() { + Mutex::Autolock _l(lock); + state = CLOSED; +} + +void SurfaceFlingerSynchro::Barrier::waitAndClose() +{ + Mutex::Autolock _l(lock); + while (state == CLOSED) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + cv.wait(lock); + } + state = CLOSED; +} + +status_t SurfaceFlingerSynchro::Barrier::waitAndClose(nsecs_t timeout) +{ + Mutex::Autolock _l(lock); + while (state == CLOSED) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + int err = cv.waitRelative(lock, timeout); + if (err != 0) + return err; + } + state = CLOSED; + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + +SurfaceFlingerSynchro::SurfaceFlingerSynchro(const sp& flinger) + : mSurfaceComposer(flinger) +{ +} + +SurfaceFlingerSynchro::SurfaceFlingerSynchro() +{ +} + +SurfaceFlingerSynchro::~SurfaceFlingerSynchro() +{ +} + +status_t SurfaceFlingerSynchro::signal() +{ + mSurfaceComposer->signal(); + return NO_ERROR; +} + +status_t SurfaceFlingerSynchro::wait() +{ + mBarrier.waitAndClose(); + return NO_ERROR; +} + +status_t SurfaceFlingerSynchro::wait(nsecs_t timeout) +{ + if (timeout == 0) + return SurfaceFlingerSynchro::wait(); + return mBarrier.waitAndClose(timeout); +} + +void SurfaceFlingerSynchro::open() +{ + mBarrier.open(); +} + +// --------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libs/ui/Time.cpp b/libs/ui/Time.cpp new file mode 100644 index 000000000..b5539135f --- /dev/null +++ b/libs/ui/Time.cpp @@ -0,0 +1,199 @@ +#include +#include +#include + +namespace android { + +static void +dump(const Time& t) +{ + #ifdef HAVE_TM_GMTOFF + long tm_gmtoff = t.t.tm_gmtoff; + #else + long tm_gmtoff = 0; + #endif + printf("%04d-%02d-%02d %02d:%02d:%02d (%d,%ld,%d,%d)\n", + t.t.tm_year+1900, t.t.tm_mon+1, t.t.tm_mday, + t.t.tm_hour, t.t.tm_min, t.t.tm_sec, + t.t.tm_isdst, tm_gmtoff, t.t.tm_wday, t.t.tm_yday); +} + +Time::Time() +{ + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + t.tm_mday = 0; + t.tm_mon = 0; + t.tm_year = 0; + t.tm_wday = 0; + t.tm_yday = 0; + t.tm_isdst = -1; // we don't know, so let the C library determine + #ifdef HAVE_TM_GMTOFF + t.tm_gmtoff = 0; + #endif +} + + +#define COMPARE_FIELD(field) do { \ + int diff = a.t.field - b.t.field; \ + if (diff != 0) return diff; \ + } while(0) + +int +Time::compare(Time& a, Time& b) +{ + if (0 == strcmp(a.timezone, b.timezone)) { + // if the timezones are the same, we can easily compare the two + // times. Otherwise, convert to milliseconds and compare that. + // This requires that object be normalized. + COMPARE_FIELD(tm_year); + COMPARE_FIELD(tm_mon); + COMPARE_FIELD(tm_mday); + COMPARE_FIELD(tm_hour); + COMPARE_FIELD(tm_min); + COMPARE_FIELD(tm_sec); + return 0; + } else { + int64_t am = a.toMillis(false /* use isDst */); + int64_t bm = b.toMillis(false /* use isDst */); + int64_t diff = am-bm; + return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0); + } +} + +static const int DAYS_PER_MONTH[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + +static inline int days_this_month(int year, int month) +{ + int n = DAYS_PER_MONTH[month]; + if (n != 28) { + return n; + } else { + int y = year; + return ((y%4)==0&&((y%100)!=0||(y%400)==0)) ? 29 : 28; + } +} + +void +Time::switchTimezone(const char* timezone) +{ + time_t seconds = mktime_tz(&(this->t), this->timezone); + localtime_tz(&seconds, &(this->t), timezone); +} + +String8 +Time::format(const char *format, const struct strftime_locale *locale) const +{ + char buf[257]; + int n = strftime_tz(buf, 257, format, &(this->t), locale); + if (n > 0) { + return String8(buf); + } else { + return String8(); + } +} + +static inline short +tochar(int n) +{ + return (n >= 0 && n <= 9) ? ('0'+n) : ' '; +} + +static inline short +next_char(int *m, int k) +{ + int n = *m / k; + *m = *m % k; + return tochar(n); +} + +void +Time::format2445(short* buf, bool hasTime) const +{ + int n; + + n = t.tm_year+1900; + buf[0] = next_char(&n, 1000); + buf[1] = next_char(&n, 100); + buf[2] = next_char(&n, 10); + buf[3] = tochar(n); + + n = t.tm_mon+1; + buf[4] = next_char(&n, 10); + buf[5] = tochar(n); + + n = t.tm_mday; + buf[6] = next_char(&n, 10); + buf[7] = tochar(n); + + if (hasTime) { + buf[8] = 'T'; + + n = t.tm_hour; + buf[9] = next_char(&n, 10); + buf[10] = tochar(n); + + n = t.tm_min; + buf[11] = next_char(&n, 10); + buf[12] = tochar(n); + + n = t.tm_sec; + buf[13] = next_char(&n, 10); + buf[14] = tochar(n); + bool inUtc = strcmp("UTC", timezone) == 0; + if (inUtc) { + buf[15] = 'Z'; + } + } +} + +String8 +Time::toString() const +{ + String8 str; + char* s = str.lockBuffer(150); + #ifdef HAVE_TM_GMTOFF + long tm_gmtoff = t.tm_gmtoff; + #else + long tm_gmtoff = 0; + #endif + sprintf(s, "%04d%02d%02dT%02d%02d%02d%s(%d,%d,%ld,%d,%d)", + t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, + t.tm_sec, timezone, t.tm_wday, t.tm_yday, tm_gmtoff, t.tm_isdst, + (int)(((Time*)this)->toMillis(false /* use isDst */)/1000)); + str.unlockBuffer(); + return str; +} + +void +Time::setToNow() +{ + time_t seconds; + time(&seconds); + localtime_tz(&seconds, &(this->t), this->timezone); +} + +int64_t +Time::toMillis(bool ignoreDst) +{ + if (ignoreDst) { + this->t.tm_isdst = -1; + } + int64_t r = mktime_tz(&(this->t), this->timezone); + if (r == -1) + return -1; + return r * 1000; +} + +void +Time::set(int64_t millis) +{ + time_t seconds = millis / 1000; + localtime_tz(&seconds, &(this->t), this->timezone); +} + +}; // namespace android + diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk new file mode 100644 index 000000000..cdb8ca2d7 --- /dev/null +++ b/libs/utils/Android.mk @@ -0,0 +1,156 @@ +# Copyright (C) 2008 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. + +LOCAL_PATH:= $(call my-dir) + +# libutils is a little unique: It's built twice, once for the host +# and once for the device. + +commonSources:= \ + Asset.cpp \ + AssetDir.cpp \ + AssetManager.cpp \ + BufferedTextOutput.cpp \ + CallStack.cpp \ + Debug.cpp \ + FileMap.cpp \ + RefBase.cpp \ + ResourceTypes.cpp \ + SharedBuffer.cpp \ + Static.cpp \ + StopWatch.cpp \ + String8.cpp \ + String16.cpp \ + SystemClock.cpp \ + TextOutput.cpp \ + Threads.cpp \ + TimerProbe.cpp \ + Timers.cpp \ + VectorImpl.cpp \ + ZipFileCRO.cpp \ + ZipFileRO.cpp \ + ZipUtils.cpp \ + misc.cpp \ + ported.cpp \ + LogSocket.cpp + +# +# The cpp files listed here do not belong in the device +# build. Consult with the swetland before even thinking about +# putting them in commonSources. +# +# They're used by the simulator runtime and by host-side tools like +# aapt and the simulator front-end. +# +hostSources:= \ + InetAddress.cpp \ + Pipe.cpp \ + Socket.cpp \ + ZipEntry.cpp \ + ZipFile.cpp + +# For the host +# ===================================================== + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= $(commonSources) $(hostSources) + +ifeq ($(HOST_OS),linux) +# Use the futex based mutex and condition variable +# implementation from android-arm because it's shared mem safe + LOCAL_SRC_FILES += \ + futex_synchro.c \ + executablepath_linux.cpp +endif +ifeq ($(HOST_OS),darwin) + LOCAL_SRC_FILES += \ + executablepath_darwin.cpp +endif + +LOCAL_MODULE:= libutils + +LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) +LOCAL_C_INCLUDES += external/zlib + +ifeq ($(HOST_OS),windows) +ifeq ($(strip $(USE_CYGWIN),),) +# Under MinGW, ctype.h doesn't need multi-byte support +LOCAL_CFLAGS += -DMB_CUR_MAX=1 +endif +endif + +include $(BUILD_HOST_STATIC_LIBRARY) + + + +# For the device +# ===================================================== +include $(CLEAR_VARS) + + +# we have the common sources, plus some device-specific stuff +LOCAL_SRC_FILES:= \ + $(commonSources) \ + Binder.cpp \ + BpBinder.cpp \ + IInterface.cpp \ + IMemory.cpp \ + IPCThreadState.cpp \ + MemoryDealer.cpp \ + MemoryBase.cpp \ + MemoryHeapBase.cpp \ + MemoryHeapPmem.cpp \ + Parcel.cpp \ + ProcessState.cpp \ + IPermissionController.cpp \ + IServiceManager.cpp \ + Unicode.cpp + +ifeq ($(TARGET_SIMULATOR),true) +LOCAL_SRC_FILES += $(hostSources) +endif + +ifeq ($(TARGET_OS),linux) +# Use the futex based mutex and condition variable +# implementation from android-arm because it's shared mem safe +LOCAL_SRC_FILES += futex_synchro.c +LOCAL_LDLIBS += -lrt -ldl +endif + +LOCAL_C_INCLUDES += \ + external/zlib \ + external/icu4c/common +LOCAL_LDLIBS += -lpthread + +LOCAL_SHARED_LIBRARIES := \ + libz \ + liblog \ + libcutils + +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) +# This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp +LOCAL_SHARED_LIBRARIES += \ + libdl +endif # linux-x86 +endif # sim + +LOCAL_MODULE:= libutils + +#LOCAL_CFLAGS+= +#LOCAL_LDFLAGS:= + +include $(BUILD_SHARED_LIBRARY) + diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp new file mode 100644 index 000000000..91203ddb4 --- /dev/null +++ b/libs/utils/Asset.cpp @@ -0,0 +1,813 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Provide access to a read-only asset. +// + +#define LOG_TAG "asset" +//#define NDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace android; + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +static volatile int32_t gCount = 0; + +int32_t Asset::getGlobalCount() +{ + return gCount; +} + +Asset::Asset(void) + : mAccessMode(ACCESS_UNKNOWN) +{ + int count = android_atomic_inc(&gCount)+1; + //LOGI("Creating Asset %p #%d\n", this, count); +} + +Asset::~Asset(void) +{ + int count = android_atomic_dec(&gCount); + //LOGI("Destroying Asset in %p #%d\n", this, count); +} + +/* + * Create a new Asset from a file on disk. There is a fair chance that + * the file doesn't actually exist. + * + * We can use "mode" to decide how we want to go about it. + */ +/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + off_t length; + int fd; + + fd = open(fileName, O_RDONLY | O_BINARY); + if (fd < 0) + return NULL; + + /* + * Under Linux, the lseek fails if we actually opened a directory. To + * be correct we should test the file type explicitly, but since we + * always open things read-only it doesn't really matter, so there's + * no value in incurring the extra overhead of an fstat() call. + */ + length = lseek(fd, 0, SEEK_END); + if (length < 0) { + ::close(fd); + return NULL; + } + (void) lseek(fd, 0, SEEK_SET); + + pAsset = new _FileAsset; + result = pAsset->openChunk(fileName, fd, 0, length); + if (result != NO_ERROR) { + delete pAsset; + return NULL; + } + + pAsset->mAccessMode = mode; + return pAsset; +} + + +/* + * Create a new Asset from a compressed file on disk. There is a fair chance + * that the file doesn't actually exist. + * + * We currently support gzip files. We might want to handle .bz2 someday. + */ +/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName, + AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + off_t fileLen; + bool scanResult; + long offset; + int method; + long uncompressedLen, compressedLen; + int fd; + + fd = open(fileName, O_RDONLY | O_BINARY); + if (fd < 0) + return NULL; + + fileLen = lseek(fd, 0, SEEK_END); + if (fileLen < 0) { + ::close(fd); + return NULL; + } + (void) lseek(fd, 0, SEEK_SET); + + /* want buffered I/O for the file scan; must dup so fclose() is safe */ + FILE* fp = fdopen(dup(fd), "rb"); + if (fp == NULL) { + ::close(fd); + return NULL; + } + + unsigned long crc32; + scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen, + &compressedLen, &crc32); + offset = ftell(fp); + fclose(fp); + if (!scanResult) { + LOGD("File '%s' is not in gzip format\n", fileName); + ::close(fd); + return NULL; + } + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(fd, offset, method, uncompressedLen, + compressedLen); + if (result != NO_ERROR) { + delete pAsset; + return NULL; + } + + pAsset->mAccessMode = mode; + return pAsset; +} + + +#if 0 +/* + * Create a new Asset from part of an open file. + */ +/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset, + size_t length, AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + + pAsset = new _FileAsset; + result = pAsset->openChunk(NULL, fd, offset, length); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + +/* + * Create a new Asset from compressed data in an open file. + */ +/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset, + int compressionMethod, size_t uncompressedLen, size_t compressedLen, + AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(fd, offset, compressionMethod, + uncompressedLen, compressedLen); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} +#endif + +/* + * Create a new Asset from a memory mapping. + */ +/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, + AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + + pAsset = new _FileAsset; + result = pAsset->openChunk(dataMap); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + +/* + * Create a new Asset from compressed data in a memory mapping. + */ +/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap, + int method, size_t uncompressedLen, AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(dataMap, method, uncompressedLen); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + + +/* + * Do generic seek() housekeeping. Pass in the offset/whence values from + * the seek request, along with the current chunk offset and the chunk + * length. + * + * Returns the new chunk offset, or -1 if the seek is illegal. + */ +off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) +{ + off_t newOffset; + + switch (whence) { + case SEEK_SET: + newOffset = offset; + break; + case SEEK_CUR: + newOffset = curPosn + offset; + break; + case SEEK_END: + newOffset = maxPosn + offset; + break; + default: + LOGW("unexpected whence %d\n", whence); + // this was happening due to an off_t size mismatch + assert(false); + return (off_t) -1; + } + + if (newOffset < 0 || newOffset > maxPosn) { + LOGW("seek out of range: want %ld, end=%ld\n", + (long) newOffset, (long) maxPosn); + return (off_t) -1; + } + + return newOffset; +} + + +/* + * =========================================================================== + * _FileAsset + * =========================================================================== + */ + +/* + * Constructor. + */ +_FileAsset::_FileAsset(void) + : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL) +{ +} + +/* + * Destructor. Release resources. + */ +_FileAsset::~_FileAsset(void) +{ + close(); +} + +/* + * Operate on a chunk of an uncompressed file. + * + * Zero-length chunks are allowed. + */ +status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length) +{ + assert(mFp == NULL); // no reopen + assert(mMap == NULL); + assert(fd >= 0); + assert(offset >= 0); + + /* + * Seek to end to get file length. + */ + off_t fileLength; + fileLength = lseek(fd, 0, SEEK_END); + if (fileLength == (off_t) -1) { + // probably a bad file descriptor + LOGD("failed lseek (errno=%d)\n", errno); + return UNKNOWN_ERROR; + } + + if ((off_t) (offset + length) > fileLength) { + LOGD("start (%ld) + len (%ld) > end (%ld)\n", + (long) offset, (long) length, (long) fileLength); + return BAD_INDEX; + } + + /* after fdopen, the fd will be closed on fclose() */ + mFp = fdopen(fd, "rb"); + if (mFp == NULL) + return UNKNOWN_ERROR; + + mStart = offset; + mLength = length; + assert(mOffset == 0); + + /* seek the FILE* to the start of chunk */ + if (fseek(mFp, mStart, SEEK_SET) != 0) { + assert(false); + } + + mFileName = fileName != NULL ? strdup(fileName) : NULL; + + return NO_ERROR; +} + +/* + * Create the chunk from the map. + */ +status_t _FileAsset::openChunk(FileMap* dataMap) +{ + assert(mFp == NULL); // no reopen + assert(mMap == NULL); + assert(dataMap != NULL); + + mMap = dataMap; + mStart = -1; // not used + mLength = dataMap->getDataLength(); + assert(mOffset == 0); + + return NO_ERROR; +} + +/* + * Read a chunk of data. + */ +ssize_t _FileAsset::read(void* buf, size_t count) +{ + size_t maxLen; + size_t actual; + + assert(mOffset >= 0 && mOffset <= mLength); + + if (getAccessMode() == ACCESS_BUFFER) { + /* + * On first access, read or map the entire file. The caller has + * requested buffer access, either because they're going to be + * using the buffer or because what they're doing has appropriate + * performance needs and access patterns. + */ + if (mBuf == NULL) + getBuffer(false); + } + + /* adjust count if we're near EOF */ + maxLen = mLength - mOffset; + if (count > maxLen) + count = maxLen; + + if (!count) + return 0; + + if (mMap != NULL) { + /* copy from mapped area */ + //printf("map read\n"); + memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count); + actual = count; + } else if (mBuf != NULL) { + /* copy from buffer */ + //printf("buf read\n"); + memcpy(buf, (char*)mBuf + mOffset, count); + actual = count; + } else { + /* read from the file */ + //printf("file read\n"); + if (ftell(mFp) != mStart + mOffset) { + LOGE("Hosed: %ld != %ld+%ld\n", + ftell(mFp), (long) mStart, (long) mOffset); + assert(false); + } + + /* + * This returns 0 on error or eof. We need to use ferror() or feof() + * to tell the difference, but we don't currently have those on the + * device. However, we know how much data is *supposed* to be in the + * file, so if we don't read the full amount we know something is + * hosed. + */ + actual = fread(buf, 1, count, mFp); + if (actual == 0) // something failed -- I/O error? + return -1; + + assert(actual == count); + } + + mOffset += actual; + return actual; +} + +/* + * Seek to a new position. + */ +off_t _FileAsset::seek(off_t offset, int whence) +{ + off_t newPosn; + long actualOffset; + + // compute new position within chunk + newPosn = handleSeek(offset, whence, mOffset, mLength); + if (newPosn == (off_t) -1) + return newPosn; + + actualOffset = (long) (mStart + newPosn); + + if (mFp != NULL) { + if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) + return (off_t) -1; + } + + mOffset = actualOffset - mStart; + return mOffset; +} + +/* + * Close the asset. + */ +void _FileAsset::close(void) +{ + if (mMap != NULL) { + mMap->release(); + mMap = NULL; + } + if (mBuf != NULL) { + delete[] mBuf; + mBuf = NULL; + } + + if (mFileName != NULL) { + free(mFileName); + mFileName = NULL; + } + + if (mFp != NULL) { + // can only be NULL when called from destructor + // (otherwise we would never return this object) + fclose(mFp); + mFp = NULL; + } +} + +/* + * Return a read-only pointer to a buffer. + * + * We can either read the whole thing in or map the relevant piece of + * the source file. Ideally a map would be established at a higher + * level and we'd be using a different object, but we didn't, so we + * deal with it here. + */ +const void* _FileAsset::getBuffer(bool wordAligned) +{ + /* subsequent requests just use what we did previously */ + if (mBuf != NULL) + return mBuf; + if (mMap != NULL) { + if (!wordAligned) { + return mMap->getDataPtr(); + } + return ensureAlignment(mMap); + } + + assert(mFp != NULL); + + if (mLength < kReadVsMapThreshold) { + unsigned char* buf; + long allocLen; + + /* zero-length files are allowed; not sure about zero-len allocs */ + /* (works fine with gcc + x86linux) */ + allocLen = mLength; + if (mLength == 0) + allocLen = 1; + + buf = new unsigned char[allocLen]; + if (buf == NULL) { + LOGE("alloc of %ld bytes failed\n", (long) allocLen); + return NULL; + } + + LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen); + if (mLength > 0) { + long oldPosn = ftell(mFp); + fseek(mFp, mStart, SEEK_SET); + if (fread(buf, 1, mLength, mFp) != (size_t) mLength) { + LOGE("failed reading %ld bytes\n", (long) mLength); + delete[] buf; + return NULL; + } + fseek(mFp, oldPosn, SEEK_SET); + } + + LOGV(" getBuffer: loaded into buffer\n"); + + mBuf = buf; + return mBuf; + } else { + FileMap* map; + + map = new FileMap; + if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { + map->release(); + return NULL; + } + + LOGV(" getBuffer: mapped\n"); + + mMap = map; + if (!wordAligned) { + return mMap->getDataPtr(); + } + return ensureAlignment(mMap); + } +} + +int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const +{ + if (mMap != NULL) { + const char* fname = mMap->getFileName(); + if (fname == NULL) { + fname = mFileName; + } + if (fname == NULL) { + return -1; + } + *outStart = mMap->getDataOffset(); + *outLength = mMap->getDataLength(); + return open(fname, O_RDONLY | O_BINARY); + } + if (mFileName == NULL) { + return -1; + } + *outStart = mStart; + *outLength = mLength; + return open(mFileName, O_RDONLY | O_BINARY); +} + +const void* _FileAsset::ensureAlignment(FileMap* map) +{ + void* data = map->getDataPtr(); + if ((((size_t)data)&0x3) == 0) { + // We can return this directly if it is aligned on a word + // boundary. + return data; + } + // If not aligned on a word boundary, then we need to copy it into + // our own buffer. + LOGV("Copying FileAsset %p to buffer size %d to make it aligned.", this, (int)mLength); + unsigned char* buf = new unsigned char[mLength]; + if (buf == NULL) { + LOGE("alloc of %ld bytes failed\n", (long) mLength); + return NULL; + } + memcpy(buf, data, mLength); + mBuf = buf; + return buf; +} + +/* + * =========================================================================== + * _CompressedAsset + * =========================================================================== + */ + +/* + * Constructor. + */ +_CompressedAsset::_CompressedAsset(void) + : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), + mMap(NULL), mFd(-1), mBuf(NULL) +{ +} + +/* + * Destructor. Release resources. + */ +_CompressedAsset::~_CompressedAsset(void) +{ + close(); +} + +/* + * Open a chunk of compressed data inside a file. + * + * This currently just sets up some values and returns. On the first + * read, we expand the entire file into a buffer and return data from it. + */ +status_t _CompressedAsset::openChunk(int fd, off_t offset, + int compressionMethod, size_t uncompressedLen, size_t compressedLen) +{ + assert(mFd < 0); // no re-open + assert(mMap == NULL); + assert(fd >= 0); + assert(offset >= 0); + assert(compressedLen > 0); + + if (compressionMethod != ZipFileRO::kCompressDeflated) { + assert(false); + return UNKNOWN_ERROR; + } + + mStart = offset; + mCompressedLen = compressedLen; + mUncompressedLen = uncompressedLen; + assert(mOffset == 0); + mFd = fd; + assert(mBuf == NULL); + + return NO_ERROR; +} + +/* + * Open a chunk of compressed data in a mapped region. + * + * Nothing is expanded until the first read call. + */ +status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod, + size_t uncompressedLen) +{ + assert(mFd < 0); // no re-open + assert(mMap == NULL); + assert(dataMap != NULL); + + if (compressionMethod != ZipFileRO::kCompressDeflated) { + assert(false); + return UNKNOWN_ERROR; + } + + mMap = dataMap; + mStart = -1; // not used + mCompressedLen = dataMap->getDataLength(); + mUncompressedLen = uncompressedLen; + assert(mOffset == 0); + + return NO_ERROR; +} + +/* + * Read data from a chunk of compressed data. + * + * [For now, that's just copying data out of a buffer.] + */ +ssize_t _CompressedAsset::read(void* buf, size_t count) +{ + size_t maxLen; + size_t actual; + + assert(mOffset >= 0 && mOffset <= mUncompressedLen); + + // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly + + if (mBuf == NULL) { + if (getBuffer(false) == NULL) + return -1; + } + assert(mBuf != NULL); + + /* adjust count if we're near EOF */ + maxLen = mUncompressedLen - mOffset; + if (count > maxLen) + count = maxLen; + + if (!count) + return 0; + + /* copy from buffer */ + //printf("comp buf read\n"); + memcpy(buf, (char*)mBuf + mOffset, count); + actual = count; + + mOffset += actual; + return actual; +} + +/* + * Handle a seek request. + * + * If we're working in a streaming mode, this is going to be fairly + * expensive, because it requires plowing through a bunch of compressed + * data. + */ +off_t _CompressedAsset::seek(off_t offset, int whence) +{ + off_t newPosn; + + // compute new position within chunk + newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); + if (newPosn == (off_t) -1) + return newPosn; + + mOffset = newPosn; + return mOffset; +} + +/* + * Close the asset. + */ +void _CompressedAsset::close(void) +{ + if (mMap != NULL) { + mMap->release(); + mMap = NULL; + } + if (mBuf != NULL) { + delete[] mBuf; + mBuf = NULL; + } + + if (mFd > 0) { + ::close(mFd); + mFd = -1; + } +} + +/* + * Get a pointer to a read-only buffer of data. + * + * The first time this is called, we expand the compressed data into a + * buffer. + */ +const void* _CompressedAsset::getBuffer(bool wordAligned) +{ + unsigned char* buf = NULL; + + if (mBuf != NULL) + return mBuf; + + if (mUncompressedLen > UNCOMPRESS_DATA_MAX) { + LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n", + (long) mUncompressedLen, UNCOMPRESS_DATA_MAX); + goto bail; + } + + /* + * Allocate a buffer and read the file into it. + */ + buf = new unsigned char[mUncompressedLen]; + if (buf == NULL) { + LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen); + goto bail; + } + + if (mMap != NULL) { + if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(), + mUncompressedLen, mCompressedLen)) + goto bail; + } else { + assert(mFd >= 0); + + /* + * Seek to the start of the compressed data. + */ + if (lseek(mFd, mStart, SEEK_SET) != mStart) + goto bail; + + /* + * Expand the data into it. + */ + if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen, + mCompressedLen)) + goto bail; + } + + /* success! */ + mBuf = buf; + buf = NULL; + +bail: + delete[] buf; + return mBuf; +} + diff --git a/libs/utils/AssetDir.cpp b/libs/utils/AssetDir.cpp new file mode 100644 index 000000000..c5f664ecc --- /dev/null +++ b/libs/utils/AssetDir.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Provide access to a virtual directory in "asset space". Most of the +// implementation is in the header file or in friend functions in +// AssetManager. +// +#include + +using namespace android; + + +/* + * Find a matching entry in a vector of FileInfo. Because it's sorted, we + * can use a binary search. + * + * Assumes the vector is sorted in ascending order. + */ +/*static*/ int AssetDir::FileInfo::findEntry(const SortedVector* pVector, + const String8& fileName) +{ + FileInfo tmpInfo; + + tmpInfo.setFileName(fileName); + return pVector->indexOf(tmpInfo); + +#if 0 // don't need this after all (uses 1/2 compares of SortedVector though) + int lo, hi, cur; + + lo = 0; + hi = pVector->size() -1; + while (lo <= hi) { + int cmp; + + cur = (hi + lo) / 2; + cmp = strcmp(pVector->itemAt(cur).getFileName(), fileName); + if (cmp == 0) { + /* match, bail */ + return cur; + } else if (cmp < 0) { + /* too low */ + lo = cur + 1; + } else { + /* too high */ + hi = cur -1; + } + } + + return -1; +#endif +} + diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp new file mode 100644 index 000000000..447b80193 --- /dev/null +++ b/libs/utils/AssetManager.cpp @@ -0,0 +1,1637 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Provide access to read-only assets. +// + +#define LOG_TAG "asset" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace android; + +/* + * Names for default app, locale, and vendor. We might want to change + * these to be an actual locale, e.g. always use en-US as the default. + */ +static const char* kDefaultLocale = "default"; +static const char* kDefaultVendor = "default"; +static const char* kAssetsRoot = "assets"; +static const char* kAppZipName = NULL; //"classes.jar"; +static const char* kSystemAssets = "framework/framework-res.apk"; + +static const char* kExcludeExtension = ".EXCLUDE"; + +static Asset* const kExcludedAsset = (Asset*) 0xd000000d; + +static volatile int32_t gCount = 0; + + +/* + * =========================================================================== + * AssetManager + * =========================================================================== + */ + +int32_t AssetManager::getGlobalCount() +{ + return gCount; +} + +AssetManager::AssetManager(CacheMode cacheMode) + : mLocale(NULL), mVendor(NULL), + mResources(NULL), mConfig(new ResTable_config), + mCacheMode(cacheMode), mCacheValid(false) +{ + int count = android_atomic_inc(&gCount)+1; + //LOGI("Creating AssetManager %p #%d\n", this, count); + memset(mConfig, 0, sizeof(ResTable_config)); +} + +AssetManager::~AssetManager(void) +{ + int count = android_atomic_dec(&gCount); + //LOGI("Destroying AssetManager in %p #%d\n", this, count); + + delete mConfig; + delete mResources; + + // don't have a String class yet, so make sure we clean up + delete[] mLocale; + delete[] mVendor; +} + +bool AssetManager::addAssetPath(const String8& path, void** cookie) +{ + AutoMutex _l(mLock); + + asset_path ap; + + String8 realPath(path); + if (kAppZipName) { + realPath.appendPath(kAppZipName); + } + ap.type = ::getFileType(realPath.string()); + if (ap.type == kFileTypeRegular) { + ap.path = realPath; + } else { + ap.path = path; + ap.type = ::getFileType(path.string()); + if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) { + LOGW("Asset path %s is neither a directory nor file (type=%d).", + path.string(), (int)ap.type); + return false; + } + } + + // Skip if we have it already. + for (size_t i=0; i mAssetPaths.size() ? NULL : (void*)next; +} + +String8 AssetManager::getAssetPath(void* cookie) const +{ + AutoMutex _l(mLock); + const size_t which = ((size_t)cookie)-1; + if (which < mAssetPaths.size()) { + return mAssetPaths[which].path; + } + return String8(); +} + +/* + * Set the current locale. Use NULL to indicate no locale. + * + * Close and reopen Zip archives as appropriate, and reset cached + * information in the locale-specific sections of the tree. + */ +void AssetManager::setLocale(const char* locale) +{ + AutoMutex _l(mLock); + setLocaleLocked(locale); +} + +void AssetManager::setLocaleLocked(const char* locale) +{ + if (mLocale != NULL) { + /* previously set, purge cached data */ + purgeFileNameCacheLocked(); + //mZipSet.purgeLocale(); + delete[] mLocale; + } + mLocale = strdupNew(locale); + + updateResourceParamsLocked(); +} + +/* + * Set the current vendor. Use NULL to indicate no vendor. + * + * Close and reopen Zip archives as appropriate, and reset cached + * information in the vendor-specific sections of the tree. + */ +void AssetManager::setVendor(const char* vendor) +{ + AutoMutex _l(mLock); + + if (mVendor != NULL) { + /* previously set, purge cached data */ + purgeFileNameCacheLocked(); + //mZipSet.purgeVendor(); + delete[] mVendor; + } + mVendor = strdupNew(vendor); +} + +void AssetManager::setConfiguration(const ResTable_config& config, const char* locale) +{ + AutoMutex _l(mLock); + *mConfig = config; + if (locale) { + setLocaleLocked(locale); + } else if (config.language[0] != 0) { + char spec[9]; + spec[0] = config.language[0]; + spec[1] = config.language[1]; + if (config.country[0] != 0) { + spec[2] = '_'; + spec[3] = config.country[0]; + spec[4] = config.country[1]; + spec[5] = 0; + } else { + spec[3] = 0; + } + setLocaleLocked(spec); + } else { + updateResourceParamsLocked(); + } +} + +/* + * Open an asset. + * + * The data could be; + * - In a file on disk (assetBase + fileName). + * - In a compressed file on disk (assetBase + fileName.gz). + * - In a Zip archive, uncompressed or compressed. + * + * It can be in a number of different directories and Zip archives. + * The search order is: + * - [appname] + * - locale + vendor + * - "default" + vendor + * - locale + "default" + * - "default + "default" + * - "common" + * - (same as above) + * + * To find a particular file, we have to try up to eight paths with + * all three forms of data. + * + * We should probably reject requests for "illegal" filenames, e.g. those + * with illegal characters or "../" backward relative paths. + */ +Asset* AssetManager::open(const char* fileName, AccessMode mode) +{ + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + String8 assetName(kAssetsRoot); + assetName.appendPath(fileName); + + /* + * For each top-level asset path, search for the asset. + */ + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + LOGV("Looking for asset '%s' in '%s'\n", + assetName.string(), mAssetPaths.itemAt(i).path.string()); + Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +/* + * Open a non-asset file as if it were an asset. + * + * The "fileName" is the partial path starting from the application + * name. + */ +Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) +{ + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + /* + * For each top-level asset path, search for the asset. + */ + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + LOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string()); + Asset* pAsset = openNonAssetInPathLocked( + fileName, mode, mAssetPaths.itemAt(i)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +Asset* AssetManager::openNonAsset(void* cookie, const char* fileName, AccessMode mode) +{ + const size_t which = ((size_t)cookie)-1; + + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + if (which < mAssetPaths.size()) { + LOGV("Looking for non-asset '%s' in '%s'\n", fileName, + mAssetPaths.itemAt(which).path.string()); + Asset* pAsset = openNonAssetInPathLocked( + fileName, mode, mAssetPaths.itemAt(which)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +/* + * Get the type of a file in the asset namespace. + * + * This currently only works for regular files. All others (including + * directories) will return kFileTypeNonexistent. + */ +FileType AssetManager::getFileType(const char* fileName) +{ + Asset* pAsset = NULL; + + /* + * Open the asset. This is less efficient than simply finding the + * file, but it's not too bad (we don't uncompress or mmap data until + * the first read() call). + */ + pAsset = open(fileName, Asset::ACCESS_STREAMING); + delete pAsset; + + if (pAsset == NULL) + return kFileTypeNonexistent; + else + return kFileTypeRegular; +} + +const ResTable* AssetManager::getResTable(bool required) const +{ + ResTable* rt = mResources; + if (rt) { + return rt; + } + + // Iterate through all asset packages, collecting resources from each. + + AutoMutex _l(mLock); + + if (mResources != NULL) { + return mResources; + } + + if (required) { + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + } + + if (mCacheMode != CACHE_OFF && !mCacheValid) + const_cast(this)->loadFileNameCacheLocked(); + + const size_t N = mAssetPaths.size(); + for (size_t i=0; i(this)-> + mZipSet.getZipResourceTable(ap.path); + if (ass == NULL) { + LOGV("loading resource table %s\n", ap.path.string()); + ass = const_cast(this)-> + openNonAssetInPathLocked("resources.arsc", + Asset::ACCESS_BUFFER, + ap); + if (ass != NULL && ass != kExcludedAsset) { + ass = const_cast(this)-> + mZipSet.setZipResourceTable(ap.path, ass); + } + } + } else { + LOGV("loading resource table %s\n", ap.path.string()); + Asset* ass = const_cast(this)-> + openNonAssetInPathLocked("resources.arsc", + Asset::ACCESS_BUFFER, + ap); + shared = false; + } + if (ass != NULL && ass != kExcludedAsset) { + if (rt == NULL) { + mResources = rt = new ResTable(); + updateResourceParamsLocked(); + } + LOGV("Installing resource asset %p in to table %p\n", ass, mResources); + rt->add(ass, (void*)(i+1), !shared); + + if (!shared) { + delete ass; + } + } + } + + if (required && !rt) LOGW("Unable to find resources file resources.arsc"); + if (!rt) { + mResources = rt = new ResTable(); + } + return rt; +} + +void AssetManager::updateResourceParamsLocked() const +{ + ResTable* res = mResources; + if (!res) { + return; + } + + size_t llen = mLocale ? strlen(mLocale) : 0; + mConfig->language[0] = 0; + mConfig->language[1] = 0; + mConfig->country[0] = 0; + mConfig->country[1] = 0; + if (llen >= 2) { + mConfig->language[0] = mLocale[0]; + mConfig->language[1] = mLocale[1]; + } + if (llen >= 5) { + mConfig->country[0] = mLocale[3]; + mConfig->country[1] = mLocale[4]; + } + mConfig->size = sizeof(*mConfig); + + res->setParameters(mConfig); +} + +const ResTable& AssetManager::getResources(bool required) const +{ + const ResTable* rt = getResTable(required); + return *rt; +} + +bool AssetManager::isUpToDate() +{ + AutoMutex _l(mLock); + return mZipSet.isUpToDate(); +} + +void AssetManager::getLocales(Vector* locales) const +{ + ResTable* res = mResources; + if (res != NULL) { + res->getLocales(locales); + } +} + +/* + * Open a non-asset file as if it were an asset, searching for it in the + * specified app. + * + * Pass in a NULL values for "appName" if the common app directory should + * be used. + */ +Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode, + const asset_path& ap) +{ + Asset* pAsset = NULL; + + /* look at the filesystem on disk */ + if (ap.type == kFileTypeDirectory) { + String8 path(ap.path); + path.appendPath(fileName); + + pAsset = openAssetFromFileLocked(path, mode); + + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + + if (pAsset != NULL) { + //printf("FOUND NA '%s' on disk\n", fileName); + pAsset->setAssetSource(path); + } + + /* look inside the zip file */ + } else { + String8 path(fileName); + + /* check the appropriate Zip file */ + ZipFileRO* pZip; + ZipEntryRO entry; + + pZip = getZipFileLocked(ap); + if (pZip != NULL) { + //printf("GOT zip, checking NA '%s'\n", (const char*) path); + entry = pZip->findEntryByName(path.string()); + if (entry != NULL) { + //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon); + pAsset = openAssetFromZipLocked(pZip, entry, mode, path); + } + } + + if (pAsset != NULL) { + /* create a "source" name, for debug/display */ + pAsset->setAssetSource( + createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""), + String8(fileName))); + } + } + + return pAsset; +} + +/* + * Open an asset, searching for it in the directory hierarchy for the + * specified app. + * + * Pass in a NULL values for "appName" if the common app directory should + * be used. + */ +Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode, + const asset_path& ap) +{ + Asset* pAsset = NULL; + + /* + * Try various combinations of locale and vendor. + */ + if (mLocale != NULL && mVendor != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor); + if (pAsset == NULL && mVendor != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor); + if (pAsset == NULL && mLocale != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL); + if (pAsset == NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL); + + return pAsset; +} + +/* + * Open an asset, searching for it in the directory hierarchy for the + * specified locale and vendor. + * + * We also search in "app.jar". + * + * Pass in NULL values for "appName", "locale", and "vendor" if the + * defaults should be used. + */ +Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode, + const asset_path& ap, const char* locale, const char* vendor) +{ + Asset* pAsset = NULL; + + if (ap.type == kFileTypeDirectory) { + if (mCacheMode == CACHE_OFF) { + /* look at the filesystem on disk */ + String8 path(createPathNameLocked(ap, locale, vendor)); + path.appendPath(fileName); + + String8 excludeName(path); + excludeName.append(kExcludeExtension); + if (::getFileType(excludeName.string()) != kFileTypeNonexistent) { + /* say no more */ + //printf("+++ excluding '%s'\n", (const char*) excludeName); + return kExcludedAsset; + } + + pAsset = openAssetFromFileLocked(path, mode); + + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + + if (pAsset != NULL) + pAsset->setAssetSource(path); + } else { + /* find in cache */ + String8 path(createPathNameLocked(ap, locale, vendor)); + path.appendPath(fileName); + + AssetDir::FileInfo tmpInfo; + bool found = false; + + String8 excludeName(path); + excludeName.append(kExcludeExtension); + + if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) { + /* go no farther */ + //printf("+++ Excluding '%s'\n", (const char*) excludeName); + return kExcludedAsset; + } + + /* + * File compression extensions (".gz") don't get stored in the + * name cache, so we have to try both here. + */ + if (mCache.indexOf(path) != NAME_NOT_FOUND) { + found = true; + pAsset = openAssetFromFileLocked(path, mode); + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + } + + if (pAsset != NULL) + pAsset->setAssetSource(path); + + /* + * Don't continue the search into the Zip files. Our cached info + * said it was a file on disk; to be consistent with openDir() + * we want to return the loose asset. If the cached file gets + * removed, we fail. + * + * The alternative is to update our cache when files get deleted, + * or make some sort of "best effort" promise, but for now I'm + * taking the hard line. + */ + if (found) { + if (pAsset == NULL) + LOGD("Expected file not found: '%s'\n", path.string()); + return pAsset; + } + } + } + + /* + * Either it wasn't found on disk or on the cached view of the disk. + * Dig through the currently-opened set of Zip files. If caching + * is disabled, the Zip file may get reopened. + */ + if (pAsset == NULL && ap.type == kFileTypeRegular) { + String8 path; + + path.appendPath((locale != NULL) ? locale : kDefaultLocale); + path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); + path.appendPath(fileName); + + /* check the appropriate Zip file */ + ZipFileRO* pZip; + ZipEntryRO entry; + + pZip = getZipFileLocked(ap); + if (pZip != NULL) { + //printf("GOT zip, checking '%s'\n", (const char*) path); + entry = pZip->findEntryByName(path.string()); + if (entry != NULL) { + //printf("FOUND in Zip file for %s/%s-%s\n", + // appName, locale, vendor); + pAsset = openAssetFromZipLocked(pZip, entry, mode, path); + } + } + + if (pAsset != NULL) { + /* create a "source" name, for debug/display */ + pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), + String8(""), String8(fileName))); + } + } + + return pAsset; +} + +/* + * Create a "source name" for a file from a Zip archive. + */ +String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName, + const String8& dirName, const String8& fileName) +{ + String8 sourceName("zip:"); + sourceName.append(zipFileName); + sourceName.append(":"); + if (dirName.length() > 0) { + sourceName.appendPath(dirName); + } + sourceName.appendPath(fileName); + return sourceName; +} + +/* + * Create a path to a loose asset (asset-base/app/locale/vendor). + */ +String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale, + const char* vendor) +{ + String8 path(ap.path); + path.appendPath((locale != NULL) ? locale : kDefaultLocale); + path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); + return path; +} + +/* + * Create a path to a loose asset (asset-base/app/rootDir). + */ +String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir) +{ + String8 path(ap.path); + if (rootDir != NULL) path.appendPath(rootDir); + return path; +} + +/* + * Return a pointer to one of our open Zip archives. Returns NULL if no + * matching Zip file exists. + * + * Right now we have 2 possible Zip files (1 each in app/"common"). + * + * If caching is set to CACHE_OFF, to get the expected behavior we + * need to reopen the Zip file on every request. That would be silly + * and expensive, so instead we just check the file modification date. + * + * Pass in NULL values for "appName", "locale", and "vendor" if the + * generics should be used. + */ +ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) +{ + LOGV("getZipFileLocked() in %p\n", this); + + return mZipSet.getZip(ap.path); +} + +/* + * Try to open an asset from a file on disk. + * + * If the file is compressed with gzip, we seek to the start of the + * deflated data and pass that in (just like we would for a Zip archive). + * + * For uncompressed data, we may already have an mmap()ed version sitting + * around. If so, we want to hand that to the Asset instead. + * + * This returns NULL if the file doesn't exist, couldn't be opened, or + * claims to be a ".gz" but isn't. + */ +Asset* AssetManager::openAssetFromFileLocked(const String8& pathName, + AccessMode mode) +{ + Asset* pAsset = NULL; + + if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) { + //printf("TRYING '%s'\n", (const char*) pathName); + pAsset = Asset::createFromCompressedFile(pathName.string(), mode); + } else { + //printf("TRYING '%s'\n", (const char*) pathName); + pAsset = Asset::createFromFile(pathName.string(), mode); + } + + return pAsset; +} + +/* + * Given an entry in a Zip archive, create a new Asset object. + * + * If the entry is uncompressed, we may want to create or share a + * slice of shared memory. + */ +Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, + const ZipEntryRO entry, AccessMode mode, const String8& entryName) +{ + Asset* pAsset = NULL; + + // TODO: look for previously-created shared memory slice? + int method; + long uncompressedLen; + + //printf("USING Zip '%s'\n", pEntry->getFileName()); + + //pZipFile->getEntryInfo(entry, &method, &uncompressedLen, &compressedLen, + // &offset); + if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL, + NULL, NULL)) + { + LOGW("getEntryInfo failed\n"); + return NULL; + } + + FileMap* dataMap = pZipFile->createEntryFileMap(entry); + if (dataMap == NULL) { + LOGW("create map from entry failed\n"); + return NULL; + } + + if (method == ZipFileRO::kCompressStored) { + pAsset = Asset::createFromUncompressedMap(dataMap, mode); + LOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), + dataMap->getFileName(), mode, pAsset); + } else { + pAsset = Asset::createFromCompressedMap(dataMap, method, + uncompressedLen, mode); + LOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), + dataMap->getFileName(), mode, pAsset); + } + if (pAsset == NULL) { + /* unexpected */ + LOGW("create from segment failed\n"); + } + + return pAsset; +} + + + +/* + * Open a directory in the asset namespace. + * + * An "asset directory" is simply the combination of all files in all + * locations, with ".gz" stripped for loose files. With app, locale, and + * vendor defined, we have 8 directories and 2 Zip archives to scan. + * + * Pass in "" for the root dir. + */ +AssetDir* AssetManager::openDir(const char* dirName) +{ + AutoMutex _l(mLock); + + AssetDir* pDir = NULL; + SortedVector* pMergedInfo = NULL; + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + assert(dirName != NULL); + + //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + pDir = new AssetDir; + + /* + * Scan the various directories, merging what we find into a single + * vector. We want to scan them in reverse priority order so that + * the ".EXCLUDE" processing works correctly. Also, if we decide we + * want to remember where the file is coming from, we'll get the right + * version. + * + * We start with Zip archives, then do loose files. + */ + pMergedInfo = new SortedVector; + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + const asset_path& ap = mAssetPaths.itemAt(i); + if (ap.type == kFileTypeRegular) { + LOGV("Adding directory %s from zip %s", dirName, ap.path.string()); + scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName); + } else { + LOGV("Adding directory %s from dir %s", dirName, ap.path.string()); + scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName); + } + } + +#if 0 + printf("FILE LIST:\n"); + for (i = 0; i < (size_t) pMergedInfo->size(); i++) { + printf(" %d: (%d) '%s'\n", i, + pMergedInfo->itemAt(i).getFileType(), + (const char*) pMergedInfo->itemAt(i).getFileName()); + } +#endif + + pDir->setFileList(pMergedInfo); + return pDir; +} + +/* + * Scan the contents of the specified directory and merge them into the + * "pMergedInfo" vector, removing previous entries if we find "exclude" + * directives. + * + * Returns "false" if we found nothing to contribute. + */ +bool AssetManager::scanAndMergeDirLocked(SortedVector* pMergedInfo, + const asset_path& ap, const char* rootDir, const char* dirName) +{ + SortedVector* pContents; + String8 path; + + assert(pMergedInfo != NULL); + + //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName); + + if (mCacheValid) { + int i, start, count; + + pContents = new SortedVector; + + /* + * Get the basic partial path and find it in the cache. That's + * the start point for the search. + */ + path = createPathNameLocked(ap, rootDir); + if (dirName[0] != '\0') + path.appendPath(dirName); + + start = mCache.indexOf(path); + if (start == NAME_NOT_FOUND) { + //printf("+++ not found in cache: dir '%s'\n", (const char*) path); + delete pContents; + return false; + } + + /* + * The match string looks like "common/default/default/foo/bar/". + * The '/' on the end ensures that we don't match on the directory + * itself or on ".../foo/barfy/". + */ + path.append("/"); + + count = mCache.size(); + + /* + * Pick out the stuff in the current dir by examining the pathname. + * It needs to match the partial pathname prefix, and not have a '/' + * (fssep) anywhere after the prefix. + */ + for (i = start+1; i < count; i++) { + if (mCache[i].getFileName().length() > path.length() && + strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0) + { + const char* name = mCache[i].getFileName().string(); + // XXX THIS IS BROKEN! Looks like we need to store the full + // path prefix separately from the file path. + if (strchr(name + path.length(), '/') == NULL) { + /* grab it, reducing path to just the filename component */ + AssetDir::FileInfo tmp = mCache[i]; + tmp.setFileName(tmp.getFileName().getPathLeaf()); + pContents->add(tmp); + } + } else { + /* no longer in the dir or its subdirs */ + break; + } + + } + } else { + path = createPathNameLocked(ap, rootDir); + if (dirName[0] != '\0') + path.appendPath(dirName); + pContents = scanDirLocked(path); + if (pContents == NULL) + return false; + } + + // if we wanted to do an incremental cache fill, we would do it here + + /* + * Process "exclude" directives. If we find a filename that ends with + * ".EXCLUDE", we look for a matching entry in the "merged" set, and + * remove it if we find it. We also delete the "exclude" entry. + */ + int i, count, exclExtLen; + + count = pContents->size(); + exclExtLen = strlen(kExcludeExtension); + for (i = 0; i < count; i++) { + const char* name; + int nameLen; + + name = pContents->itemAt(i).getFileName().string(); + nameLen = strlen(name); + if (nameLen > exclExtLen && + strcmp(name + (nameLen - exclExtLen), kExcludeExtension) == 0) + { + String8 match(name, nameLen - exclExtLen); + int matchIdx; + + matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match); + if (matchIdx > 0) { + LOGV("Excluding '%s' [%s]\n", + pMergedInfo->itemAt(matchIdx).getFileName().string(), + pMergedInfo->itemAt(matchIdx).getSourceName().string()); + pMergedInfo->removeAt(matchIdx); + } else { + //printf("+++ no match on '%s'\n", (const char*) match); + } + + LOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i); + pContents->removeAt(i); + i--; // adjust "for" loop + count--; // and loop limit + } + } + + mergeInfoLocked(pMergedInfo, pContents); + + delete pContents; + + return true; +} + +/* + * Scan the contents of the specified directory, and stuff what we find + * into a newly-allocated vector. + * + * Files ending in ".gz" will have their extensions removed. + * + * We should probably think about skipping files with "illegal" names, + * e.g. illegal characters (/\:) or excessive length. + * + * Returns NULL if the specified directory doesn't exist. + */ +SortedVector* AssetManager::scanDirLocked(const String8& path) +{ + SortedVector* pContents = NULL; + DIR* dir; + struct dirent* entry; + FileType fileType; + + LOGV("Scanning dir '%s'\n", path.string()); + + dir = opendir(path.string()); + if (dir == NULL) + return NULL; + + pContents = new SortedVector; + + while (1) { + entry = readdir(dir); + if (entry == NULL) + break; + + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) + continue; + +#ifdef _DIRENT_HAVE_D_TYPE + if (entry->d_type == DT_REG) + fileType = kFileTypeRegular; + else if (entry->d_type == DT_DIR) + fileType = kFileTypeDirectory; + else + fileType = kFileTypeUnknown; +#else + // stat the file + fileType = ::getFileType(path.appendPathCopy(entry->d_name).string()); +#endif + + if (fileType != kFileTypeRegular && fileType != kFileTypeDirectory) + continue; + + AssetDir::FileInfo info; + info.set(String8(entry->d_name), fileType); + if (strcasecmp(info.getFileName().getPathExtension().string(), ".gz") == 0) + info.setFileName(info.getFileName().getBasePath()); + info.setSourceName(path.appendPathCopy(info.getFileName())); + pContents->add(info); + } + + closedir(dir); + return pContents; +} + +/* + * Scan the contents out of the specified Zip archive, and merge what we + * find into "pMergedInfo". If the Zip archive in question doesn't exist, + * we return immediately. + * + * Returns "false" if we found nothing to contribute. + */ +bool AssetManager::scanAndMergeZipLocked(SortedVector* pMergedInfo, + const asset_path& ap, const char* rootDir, const char* baseDirName) +{ + ZipFileRO* pZip; + Vector dirs; + AssetDir::FileInfo info; + SortedVector contents; + String8 sourceName, zipName, dirName; + + pZip = mZipSet.getZip(ap.path); + if (pZip == NULL) { + LOGW("Failure opening zip %s\n", ap.path.string()); + return false; + } + + zipName = ZipSet::getPathName(ap.path.string()); + + /* convert "sounds" to "rootDir/sounds" */ + if (rootDir != NULL) dirName = rootDir; + dirName.appendPath(baseDirName); + + /* + * Scan through the list of files, looking for a match. The files in + * the Zip table of contents are not in sorted order, so we have to + * process the entire list. We're looking for a string that begins + * with the characters in "dirName", is followed by a '/', and has no + * subsequent '/' in the stuff that follows. + * + * What makes this especially fun is that directories are not stored + * explicitly in Zip archives, so we have to infer them from context. + * When we see "sounds/foo.wav" we have to leave a note to ourselves + * to insert a directory called "sounds" into the list. We store + * these in temporary vector so that we only return each one once. + * + * Name comparisons are case-sensitive to match UNIX filesystem + * semantics. + */ + int dirNameLen = dirName.length(); + for (int i = 0; i < pZip->getNumEntries(); i++) { + ZipEntryRO entry; + char nameBuf[256]; + + entry = pZip->findEntryByIndex(i); + if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) { + // TODO: fix this if we expect to have long names + LOGE("ARGH: name too long?\n"); + continue; + } + if (dirNameLen == 0 || + (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 && + nameBuf[dirNameLen] == '/')) + { + const char* cp; + const char* nextSlash; + + cp = nameBuf + dirNameLen; + if (dirNameLen != 0) + cp++; // advance past the '/' + + nextSlash = strchr(cp, '/'); +//xxx this may break if there are bare directory entries + if (nextSlash == NULL) { + /* this is a file in the requested directory */ + + info.set(String8(nameBuf).getPathLeaf(), kFileTypeRegular); + + info.setSourceName( + createZipSourceNameLocked(zipName, dirName, info.getFileName())); + + contents.add(info); + //printf("FOUND: file '%s'\n", (const char*) info.mFileName); + } else { + /* this is a subdir; add it if we don't already have it*/ + String8 subdirName(cp, nextSlash - cp); + size_t j; + size_t N = dirs.size(); + + for (j = 0; j < N; j++) { + if (subdirName == dirs[j]) { + break; + } + } + if (j == N) { + dirs.add(subdirName); + } + + //printf("FOUND: dir '%s'\n", (const char*) subdirName); + } + } + } + + /* + * Add the set of unique directories. + */ + for (int i = 0; i < (int) dirs.size(); i++) { + info.set(dirs[i], kFileTypeDirectory); + info.setSourceName( + createZipSourceNameLocked(zipName, dirName, info.getFileName())); + contents.add(info); + } + + mergeInfoLocked(pMergedInfo, &contents); + + return true; +} + + +/* + * Merge two vectors of FileInfo. + * + * The merged contents will be stuffed into *pMergedInfo. + * + * If an entry for a file exists in both "pMergedInfo" and "pContents", + * we use the newer "pContents" entry. + */ +void AssetManager::mergeInfoLocked(SortedVector* pMergedInfo, + const SortedVector* pContents) +{ + /* + * Merge what we found in this directory with what we found in + * other places. + * + * Two basic approaches: + * (1) Create a new array that holds the unique values of the two + * arrays. + * (2) Take the elements from pContents and shove them into pMergedInfo. + * + * Because these are vectors of complex objects, moving elements around + * inside the vector requires constructing new objects and allocating + * storage for members. With approach #1, we're always adding to the + * end, whereas with #2 we could be inserting multiple elements at the + * front of the vector. Approach #1 requires a full copy of the + * contents of pMergedInfo, but approach #2 requires the same copy for + * every insertion at the front of pMergedInfo. + * + * (We should probably use a SortedVector interface that allows us to + * just stuff items in, trusting us to maintain the sort order.) + */ + SortedVector* pNewSorted; + int mergeMax, contMax; + int mergeIdx, contIdx; + + pNewSorted = new SortedVector; + mergeMax = pMergedInfo->size(); + contMax = pContents->size(); + mergeIdx = contIdx = 0; + + while (mergeIdx < mergeMax || contIdx < contMax) { + if (mergeIdx == mergeMax) { + /* hit end of "merge" list, copy rest of "contents" */ + pNewSorted->add(pContents->itemAt(contIdx)); + contIdx++; + } else if (contIdx == contMax) { + /* hit end of "cont" list, copy rest of "merge" */ + pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); + mergeIdx++; + } else if (pMergedInfo->itemAt(mergeIdx) == pContents->itemAt(contIdx)) + { + /* items are identical, add newer and advance both indices */ + pNewSorted->add(pContents->itemAt(contIdx)); + mergeIdx++; + contIdx++; + } else if (pMergedInfo->itemAt(mergeIdx) < pContents->itemAt(contIdx)) + { + /* "merge" is lower, add that one */ + pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); + mergeIdx++; + } else { + /* "cont" is lower, add that one */ + assert(pContents->itemAt(contIdx) < pMergedInfo->itemAt(mergeIdx)); + pNewSorted->add(pContents->itemAt(contIdx)); + contIdx++; + } + } + + /* + * Overwrite the "merged" list with the new stuff. + */ + *pMergedInfo = *pNewSorted; + delete pNewSorted; + +#if 0 // for Vector, rather than SortedVector + int i, j; + for (i = pContents->size() -1; i >= 0; i--) { + bool add = true; + + for (j = pMergedInfo->size() -1; j >= 0; j--) { + /* case-sensitive comparisons, to behave like UNIX fs */ + if (strcmp(pContents->itemAt(i).mFileName, + pMergedInfo->itemAt(j).mFileName) == 0) + { + /* match, don't add this entry */ + add = false; + break; + } + } + + if (add) + pMergedInfo->add(pContents->itemAt(i)); + } +#endif +} + + +/* + * Load all files into the file name cache. We want to do this across + * all combinations of { appname, locale, vendor }, performing a recursive + * directory traversal. + * + * This is not the most efficient data structure. Also, gathering the + * information as we needed it (file-by-file or directory-by-directory) + * would be faster. However, on the actual device, 99% of the files will + * live in Zip archives, so this list will be very small. The trouble + * is that we have to check the "loose" files first, so it's important + * that we don't beat the filesystem silly looking for files that aren't + * there. + * + * Note on thread safety: this is the only function that causes updates + * to mCache, and anybody who tries to use it will call here if !mCacheValid, + * so we need to employ a mutex here. + */ +void AssetManager::loadFileNameCacheLocked(void) +{ + assert(!mCacheValid); + assert(mCache.size() == 0); + +#ifdef DO_TIMINGS // need to link against -lrt for this now + DurationTimer timer; + timer.start(); +#endif + + fncScanLocked(&mCache, ""); + +#ifdef DO_TIMINGS + timer.stop(); + LOGD("Cache scan took %.3fms\n", + timer.durationUsecs() / 1000.0); +#endif + +#if 0 + int i; + printf("CACHED FILE LIST (%d entries):\n", mCache.size()); + for (i = 0; i < (int) mCache.size(); i++) { + printf(" %d: (%d) '%s'\n", i, + mCache.itemAt(i).getFileType(), + (const char*) mCache.itemAt(i).getFileName()); + } +#endif + + mCacheValid = true; +} + +/* + * Scan up to 8 versions of the specified directory. + */ +void AssetManager::fncScanLocked(SortedVector* pMergedInfo, + const char* dirName) +{ + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + const asset_path& ap = mAssetPaths.itemAt(i); + fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName); + if (mLocale != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName); + if (mVendor != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName); + if (mLocale != NULL && mVendor != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName); + } +} + +/* + * Recursively scan this directory and all subdirs. + * + * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE + * files, and we prepend the extended partial path to the filenames. + */ +bool AssetManager::fncScanAndMergeDirLocked( + SortedVector* pMergedInfo, + const asset_path& ap, const char* locale, const char* vendor, + const char* dirName) +{ + SortedVector* pContents; + String8 partialPath; + String8 fullPath; + + // XXX This is broken -- the filename cache needs to hold the base + // asset path separately from its filename. + + partialPath = createPathNameLocked(ap, locale, vendor); + if (dirName[0] != '\0') { + partialPath.appendPath(dirName); + } + + fullPath = partialPath; + pContents = scanDirLocked(fullPath); + if (pContents == NULL) { + return false; // directory did not exist + } + + /* + * Scan all subdirectories of the current dir, merging what we find + * into "pMergedInfo". + */ + for (int i = 0; i < (int) pContents->size(); i++) { + if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) { + String8 subdir(dirName); + subdir.appendPath(pContents->itemAt(i).getFileName()); + + fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string()); + } + } + + /* + * To be consistent, we want entries for the root directory. If + * we're the root, add one now. + */ + if (dirName[0] == '\0') { + AssetDir::FileInfo tmpInfo; + + tmpInfo.set(String8(""), kFileTypeDirectory); + tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor)); + pContents->add(tmpInfo); + } + + /* + * We want to prepend the extended partial path to every entry in + * "pContents". It's the same value for each entry, so this will + * not change the sorting order of the vector contents. + */ + for (int i = 0; i < (int) pContents->size(); i++) { + const AssetDir::FileInfo& info = pContents->itemAt(i); + pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName())); + } + + mergeInfoLocked(pMergedInfo, pContents); + return true; +} + +/* + * Trash the cache. + */ +void AssetManager::purgeFileNameCacheLocked(void) +{ + mCacheValid = false; + mCache.clear(); +} + +/* + * =========================================================================== + * AssetManager::SharedZip + * =========================================================================== + */ + + +Mutex AssetManager::SharedZip::gLock; +DefaultKeyedVector > AssetManager::SharedZip::gOpen; + +AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) + : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL) +{ + //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); + mZipFile = new ZipFileRO; + LOGV("+++ opening zip '%s'\n", mPath.string()); + if (mZipFile->open(mPath.string()) != NO_ERROR) { + LOGD("failed to open Zip archive '%s'\n", mPath.string()); + delete mZipFile; + mZipFile = NULL; + } +} + +sp AssetManager::SharedZip::get(const String8& path) +{ + AutoMutex _l(gLock); + time_t modWhen = getFileModDate(path); + sp zip = gOpen.valueFor(path).promote(); + if (zip != NULL && zip->mModWhen == modWhen) { + return zip; + } + zip = new SharedZip(path, modWhen); + gOpen.add(path, zip); + return zip; + +} + +ZipFileRO* AssetManager::SharedZip::getZip() +{ + return mZipFile; +} + +Asset* AssetManager::SharedZip::getResourceTableAsset() +{ + LOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset); + return mResourceTableAsset; +} + +Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) +{ + { + AutoMutex _l(gLock); + if (mResourceTableAsset == NULL) { + mResourceTableAsset = asset; + // This is not thread safe the first time it is called, so + // do it here with the global lock held. + asset->getBuffer(true); + return asset; + } + } + delete asset; + return mResourceTableAsset; +} + +bool AssetManager::SharedZip::isUpToDate() +{ + time_t modWhen = getFileModDate(mPath.string()); + return mModWhen == modWhen; +} + +AssetManager::SharedZip::~SharedZip() +{ + //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); + if (mResourceTableAsset != NULL) { + delete mResourceTableAsset; + } + if (mZipFile != NULL) { + delete mZipFile; + LOGV("Closed '%s'\n", mPath.string()); + } +} + +/* + * =========================================================================== + * AssetManager::ZipSet + * =========================================================================== + */ + +/* + * Constructor. + */ +AssetManager::ZipSet::ZipSet(void) +{ +} + +/* + * Destructor. Close any open archives. + */ +AssetManager::ZipSet::~ZipSet(void) +{ + size_t N = mZipFile.size(); + for (size_t i = 0; i < N; i++) + closeZip(i); +} + +/* + * Close a Zip file and reset the entry. + */ +void AssetManager::ZipSet::closeZip(int idx) +{ + mZipFile.editItemAt(idx) = NULL; +} + + +/* + * Retrieve the appropriate Zip file from the set. + */ +ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + if (zip == NULL) { + zip = SharedZip::get(path); + mZipFile.editItemAt(idx) = zip; + } + return zip->getZip(); +} + +Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + if (zip == NULL) { + zip = SharedZip::get(path); + mZipFile.editItemAt(idx) = zip; + } + return zip->getResourceTableAsset(); +} + +Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path, + Asset* asset) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + // doesn't make sense to call before previously accessing. + return zip->setResourceTableAsset(asset); +} + +/* + * Generate the partial pathname for the specified archive. The caller + * gets to prepend the asset root directory. + * + * Returns something like "common/en-US-noogle.jar". + */ +/*static*/ String8 AssetManager::ZipSet::getPathName(const char* zipPath) +{ + return String8(zipPath); +} + +bool AssetManager::ZipSet::isUpToDate() +{ + const size_t N = mZipFile.size(); + for (size_t i=0; iisUpToDate()) { + return false; + } + } + return true; +} + +/* + * Compute the zip file's index. + * + * "appName", "locale", and "vendor" should be set to NULL to indicate the + * default directory. + */ +int AssetManager::ZipSet::getIndex(const String8& zip) const +{ + const size_t N = mZipPath.size(); + for (size_t i=0; i + +#include +#include +#include +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +sp IBinder::queryLocalInterface(const String16& descriptor) +{ + return NULL; +} + +BBinder* IBinder::localBinder() +{ + return NULL; +} + +BpBinder* IBinder::remoteBinder() +{ + return NULL; +} + +bool IBinder::checkSubclass(const void* /*subclassID*/) const +{ + return false; +} + +// --------------------------------------------------------------------------- + +class BBinder::Extras +{ +public: + Mutex mLock; + BpBinder::ObjectManager mObjects; +}; + +// --------------------------------------------------------------------------- + +BBinder::BBinder() + : mExtras(NULL) +{ +} + +bool BBinder::isBinderAlive() const +{ + return true; +} + +status_t BBinder::pingBinder() +{ + return NO_ERROR; +} + +String16 BBinder::getInterfaceDescriptor() const +{ + LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); + return String16(); +} + +status_t BBinder::transact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + data.setDataPosition(0); + + status_t err = NO_ERROR; + switch (code) { + case PING_TRANSACTION: + reply->writeInt32(pingBinder()); + break; + default: + err = onTransact(code, data, reply, flags); + break; + } + + if (reply != NULL) { + reply->setDataPosition(0); + } + + return err; +} + +status_t BBinder::linkToDeath( + const sp& recipient, void* cookie, uint32_t flags) +{ + return INVALID_OPERATION; +} + +status_t BBinder::unlinkToDeath( + const wp& recipient, void* cookie, uint32_t flags, + wp* outRecipient) +{ + return INVALID_OPERATION; +} + +status_t BBinder::dump(int fd, const Vector& args) +{ + return NO_ERROR; +} + +void BBinder::attachObject( + const void* objectID, void* object, void* cleanupCookie, + object_cleanup_func func) +{ + Extras* e = mExtras; + + if (!e) { + e = new Extras; + if (android_atomic_cmpxchg(0, reinterpret_cast(e), + reinterpret_cast(&mExtras)) != 0) { + delete e; + e = mExtras; + } + if (e == 0) return; // out of memory + } + + AutoMutex _l(e->mLock); + e->mObjects.attach(objectID, object, cleanupCookie, func); +} + +void* BBinder::findObject(const void* objectID) const +{ + Extras* e = mExtras; + if (!e) return NULL; + + AutoMutex _l(e->mLock); + return e->mObjects.find(objectID); +} + +void BBinder::detachObject(const void* objectID) +{ + Extras* e = mExtras; + if (!e) return; + + AutoMutex _l(e->mLock); + e->mObjects.detach(objectID); +} + +BBinder* BBinder::localBinder() +{ + return this; +} + +BBinder::~BBinder() +{ + if (mExtras) delete mExtras; +} + + +status_t BBinder::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case INTERFACE_TRANSACTION: + reply->writeString16(getInterfaceDescriptor()); + return NO_ERROR; + + case DUMP_TRANSACTION: { + int fd = data.readFileDescriptor(); + int argc = data.readInt32(); + Vector args; + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + args.add(data.readString16()); + } + return dump(fd, args); + } + default: + return UNKNOWN_TRANSACTION; + } +} + +// --------------------------------------------------------------------------- + +enum { + // This is used to transfer ownership of the remote binder from + // the BpRefBase object holding it (when it is constructed), to the + // owner of the BpRefBase object when it first acquires that BpRefBase. + kRemoteAcquired = 0x00000001 +}; + +BpRefBase::BpRefBase(const sp& o) + : mRemote(o.get()), mRefs(NULL), mState(0) +{ + extendObjectLifetime(OBJECT_LIFETIME_WEAK); + + if (mRemote) { + mRemote->incStrong(this); // Removed on first IncStrong(). + mRefs = mRemote->createWeak(this); // Held for our entire lifetime. + } +} + +BpRefBase::~BpRefBase() +{ + if (mRemote) { + if (!(mState&kRemoteAcquired)) { + mRemote->decStrong(this); + } + mRefs->decWeak(this); + } +} + +void BpRefBase::onFirstRef() +{ + android_atomic_or(kRemoteAcquired, &mState); +} + +void BpRefBase::onLastStrongRef(const void* id) +{ + if (mRemote) { + mRemote->decStrong(this); + } +} + +bool BpRefBase::onIncStrongAttempted(uint32_t flags, const void* id) +{ + return mRemote ? mRefs->attemptIncStrong(this) : false; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/BpBinder.cpp b/libs/utils/BpBinder.cpp new file mode 100644 index 000000000..69ab19574 --- /dev/null +++ b/libs/utils/BpBinder.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2005 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. + */ + +#define LOG_TAG "BpBinder" +//#define LOG_NDEBUG 0 + +#include + +#include +#include + +#include + +//#undef LOGV +//#define LOGV(...) fprintf(stderr, __VA_ARGS__) + +namespace android { + +// --------------------------------------------------------------------------- + +BpBinder::ObjectManager::ObjectManager() +{ +} + +BpBinder::ObjectManager::~ObjectManager() +{ + kill(); +} + +void BpBinder::ObjectManager::attach( + const void* objectID, void* object, void* cleanupCookie, + IBinder::object_cleanup_func func) +{ + entry_t e; + e.object = object; + e.cleanupCookie = cleanupCookie; + e.func = func; + + if (mObjects.indexOfKey(objectID) >= 0) { + LOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use", + objectID, this, object); + return; + } + + mObjects.add(objectID, e); +} + +void* BpBinder::ObjectManager::find(const void* objectID) const +{ + const ssize_t i = mObjects.indexOfKey(objectID); + if (i < 0) return NULL; + return mObjects.valueAt(i).object; +} + +void BpBinder::ObjectManager::detach(const void* objectID) +{ + mObjects.removeItem(objectID); +} + +void BpBinder::ObjectManager::kill() +{ + const size_t N = mObjects.size(); + LOGV("Killing %d objects in manager %p", N, this); + for (size_t i=0; iincWeakHandle(handle); +} + +String16 BpBinder::getInterfaceDescriptor() const +{ + String16 res; + Parcel send, reply; + status_t err = const_cast(this)->transact( + INTERFACE_TRANSACTION, send, &reply); + if (err == NO_ERROR) { + res = reply.readString16(); + } + return res; +} + +bool BpBinder::isBinderAlive() const +{ + return mAlive != 0; +} + +status_t BpBinder::pingBinder() +{ + Parcel send; + Parcel reply; + status_t err = transact(PING_TRANSACTION, send, &reply); + if (err != NO_ERROR) return err; + if (reply.dataSize() < sizeof(status_t)) return NOT_ENOUGH_DATA; + return (status_t)reply.readInt32(); +} + +status_t BpBinder::dump(int fd, const Vector& args) +{ + Parcel send; + Parcel reply; + send.writeFileDescriptor(fd); + const size_t numArgs = args.size(); + send.writeInt32(numArgs); + for (size_t i = 0; i < numArgs; i++) { + send.writeString16(args[i]); + } + status_t err = transact(DUMP_TRANSACTION, send, &reply); + return err; +} + +status_t BpBinder::transact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // Once a binder has died, it will never come back to life. + if (mAlive) { + status_t status = IPCThreadState::self()->transact( + mHandle, code, data, reply, flags); + if (status == DEAD_OBJECT) mAlive = 0; + return status; + } + + return DEAD_OBJECT; +} + +status_t BpBinder::linkToDeath( + const sp& recipient, void* cookie, uint32_t flags) +{ + Obituary ob; + ob.recipient = recipient; + ob.cookie = cookie; + ob.flags = flags; + + LOG_ALWAYS_FATAL_IF(recipient == NULL, + "linkToDeath(): recipient must be non-NULL"); + + { + AutoMutex _l(mLock); + + if (!mObitsSent) { + if (!mObituaries) { + mObituaries = new Vector; + if (!mObituaries) { + return NO_MEMORY; + } + LOGV("Requesting death notification: %p handle %d\n", this, mHandle); + getWeakRefs()->incWeak(this); + IPCThreadState* self = IPCThreadState::self(); + self->requestDeathNotification(mHandle, this); + self->flushCommands(); + } + ssize_t res = mObituaries->add(ob); + return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res; + } + } + + return DEAD_OBJECT; +} + +status_t BpBinder::unlinkToDeath( + const wp& recipient, void* cookie, uint32_t flags, + wp* outRecipient) +{ + AutoMutex _l(mLock); + + if (mObitsSent) { + return DEAD_OBJECT; + } + + const size_t N = mObituaries ? mObituaries->size() : 0; + for (size_t i=0; iitemAt(i); + if ((obit.recipient == recipient + || (recipient == NULL && obit.cookie == cookie)) + && obit.flags == flags) { + const uint32_t allFlags = obit.flags|flags; + if (outRecipient != NULL) { + *outRecipient = mObituaries->itemAt(i).recipient; + } + mObituaries->removeAt(i); + if (mObituaries->size() == 0) { + LOGV("Clearing death notification: %p handle %d\n", this, mHandle); + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(mHandle, this); + self->flushCommands(); + delete mObituaries; + mObituaries = NULL; + } + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +void BpBinder::sendObituary() +{ + LOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", + this, mHandle, mObitsSent ? "true" : "false"); + + mAlive = 0; + if (mObitsSent) return; + + mLock.lock(); + Vector* obits = mObituaries; + if(obits != NULL) { + LOGV("Clearing sent death notification: %p handle %d\n", this, mHandle); + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(mHandle, this); + self->flushCommands(); + mObituaries = NULL; + } + mObitsSent = 1; + mLock.unlock(); + + LOGV("Reporting death of proxy %p for %d recipients\n", + this, obits ? obits->size() : 0); + + if (obits != NULL) { + const size_t N = obits->size(); + for (size_t i=0; iitemAt(i)); + } + + delete obits; + } +} + +void BpBinder::reportOneDeath(const Obituary& obit) +{ + sp recipient = obit.recipient.promote(); + LOGV("Reporting death to recipient: %p\n", recipient.get()); + if (recipient == NULL) return; + + recipient->binderDied(this); +} + + +void BpBinder::attachObject( + const void* objectID, void* object, void* cleanupCookie, + object_cleanup_func func) +{ + AutoMutex _l(mLock); + LOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); + mObjects.attach(objectID, object, cleanupCookie, func); +} + +void* BpBinder::findObject(const void* objectID) const +{ + AutoMutex _l(mLock); + return mObjects.find(objectID); +} + +void BpBinder::detachObject(const void* objectID) +{ + AutoMutex _l(mLock); + mObjects.detach(objectID); +} + +BpBinder* BpBinder::remoteBinder() +{ + return this; +} + +BpBinder::~BpBinder() +{ + LOGV("Destroying BpBinder %p handle %d\n", this, mHandle); + + IPCThreadState* ipc = IPCThreadState::self(); + + mLock.lock(); + Vector* obits = mObituaries; + if(obits != NULL) { + if (ipc) ipc->clearDeathNotification(mHandle, this); + mObituaries = NULL; + } + mLock.unlock(); + + if (obits != NULL) { + // XXX Should we tell any remaining DeathRecipient + // objects that the last strong ref has gone away, so they + // are no longer linked? + delete obits; + } + + if (ipc) { + ipc->expungeHandle(mHandle, this); + ipc->decWeakHandle(mHandle); + } +} + +void BpBinder::onFirstRef() +{ + LOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle); + IPCThreadState* ipc = IPCThreadState::self(); + if (ipc) ipc->incStrongHandle(mHandle); +} + +void BpBinder::onLastStrongRef(const void* id) +{ + LOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle); + IF_LOGV() { + printRefs(); + } + IPCThreadState* ipc = IPCThreadState::self(); + if (ipc) ipc->decStrongHandle(mHandle); +} + +bool BpBinder::onIncStrongAttempted(uint32_t flags, const void* id) +{ + LOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle); + IPCThreadState* ipc = IPCThreadState::self(); + return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/BufferedTextOutput.cpp b/libs/utils/BufferedTextOutput.cpp new file mode 100644 index 000000000..989662e84 --- /dev/null +++ b/libs/utils/BufferedTextOutput.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2006 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 +#include +#include + +#include + +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +struct BufferedTextOutput::BufferState : public RefBase +{ + BufferState(int32_t _seq) + : seq(_seq) + , buffer(NULL) + , bufferPos(0) + , bufferSize(0) + , atFront(true) + , indent(0) + , bundle(0) { + } + ~BufferState() { + free(buffer); + } + + status_t append(const char* txt, size_t len) { + if ((len+bufferPos) > bufferSize) { + void* b = realloc(buffer, ((len+bufferPos)*3)/2); + if (!b) return NO_MEMORY; + buffer = (char*)b; + } + memcpy(buffer+bufferPos, txt, len); + bufferPos += len; + return NO_ERROR; + } + + void restart() { + bufferPos = 0; + atFront = true; + if (bufferSize > 256) { + void* b = realloc(buffer, 256); + if (b) { + buffer = (char*)b; + bufferSize = 256; + } + } + } + + const int32_t seq; + char* buffer; + size_t bufferPos; + size_t bufferSize; + bool atFront; + int32_t indent; + int32_t bundle; +}; + +struct BufferedTextOutput::ThreadState +{ + Vector > states; +}; + +static mutex_t gMutex; + +static thread_store_t tls; + +BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState() +{ + ThreadState* ts = (ThreadState*) thread_store_get( &tls ); + if (ts) return ts; + ts = new ThreadState; + thread_store_set( &tls, ts, threadDestructor ); + return ts; +} + +void BufferedTextOutput::threadDestructor(void *st) +{ + delete ((ThreadState*)st); +} + +static volatile int32_t gSequence = 0; + +static volatile int32_t gFreeBufferIndex = -1; + +static int32_t allocBufferIndex() +{ + int32_t res = -1; + + mutex_lock(&gMutex); + + if (gFreeBufferIndex >= 0) { + res = gFreeBufferIndex; + gFreeBufferIndex = gTextBuffers[res]; + gTextBuffers.editItemAt(res) = -1; + + } else { + res = gTextBuffers.size(); + gTextBuffers.add(-1); + } + + mutex_unlock(&gMutex); + + return res; +} + +static void freeBufferIndex(int32_t idx) +{ + mutex_lock(&gMutex); + gTextBuffers.editItemAt(idx) = gFreeBufferIndex; + gFreeBufferIndex = idx; + mutex_unlock(&gMutex); +} + +// --------------------------------------------------------------------------- + +BufferedTextOutput::BufferedTextOutput(uint32_t flags) + : mFlags(flags) + , mSeq(android_atomic_inc(&gSequence)) + , mIndex(allocBufferIndex()) +{ + mGlobalState = new BufferState(mSeq); + if (mGlobalState) mGlobalState->incStrong(this); +} + +BufferedTextOutput::~BufferedTextOutput() +{ + if (mGlobalState) mGlobalState->decStrong(this); + freeBufferIndex(mIndex); +} + +status_t BufferedTextOutput::print(const char* txt, size_t len) +{ + //printf("BufferedTextOutput: printing %d\n", len); + + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + + const char* const end = txt+len; + + status_t err; + + while (txt < end) { + // Find the next line. + const char* first = txt; + while (txt < end && *txt != '\n') txt++; + + // Include this and all following empty lines. + while (txt < end && *txt == '\n') txt++; + + // Special cases for first data on a line. + if (b->atFront) { + if (b->indent > 0) { + // If this is the start of a line, add the indent. + const char* prefix = stringForIndent(b->indent); + err = b->append(prefix, strlen(prefix)); + if (err != NO_ERROR) return err; + + } else if (*(txt-1) == '\n' && !b->bundle) { + // Fast path: if we are not indenting or bundling, and + // have been given one or more complete lines, just write + // them out without going through the buffer. + + // Slurp up all of the lines. + const char* lastLine = txt+1; + while (txt < end) { + if (*txt++ == '\n') lastLine = txt; + } + struct iovec vec; + vec.iov_base = (void*)first; + vec.iov_len = lastLine-first; + //printf("Writing %d bytes of data!\n", vec.iov_len); + writeLines(vec, 1); + txt = lastLine; + continue; + } + } + + // Append the new text to the buffer. + err = b->append(first, txt-first); + if (err != NO_ERROR) return err; + b->atFront = *(txt-1) == '\n'; + + // If we have finished a line and are not bundling, write + // it out. + //printf("Buffer is now %d bytes\n", b->bufferPos); + if (b->atFront && !b->bundle) { + struct iovec vec; + vec.iov_base = b->buffer; + vec.iov_len = b->bufferPos; + //printf("Writing %d bytes of data!\n", vec.iov_len); + writeLines(vec, 1); + b->restart(); + } + } + + return NO_ERROR; +} + +void BufferedTextOutput::moveIndent(int delta) +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->indent += delta; + if (b->indent < 0) b->indent = 0; +} + +void BufferedTextOutput::pushBundle() +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->bundle++; +} + +void BufferedTextOutput::popBundle() +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->bundle--; + LOG_FATAL_IF(b->bundle < 0, + "TextOutput::popBundle() called more times than pushBundle()"); + if (b->bundle < 0) b->bundle = 0; + + if (b->bundle == 0) { + // Last bundle, write out data if it is complete. If it is not + // complete, don't write until the last line is done... this may + // or may not be the write thing to do, but it's the easiest. + if (b->bufferPos > 0 && b->atFront) { + struct iovec vec; + vec.iov_base = b->buffer; + vec.iov_len = b->bufferPos; + writeLines(vec, 1); + b->restart(); + } + } +} + +BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const +{ + if ((mFlags&MULTITHREADED) != 0) { + ThreadState* ts = getThreadState(); + if (ts) { + while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL); + BufferState* bs = ts->states[mIndex].get(); + if (bs != NULL && bs->seq == mSeq) return bs; + + ts->states.editItemAt(mIndex) = new BufferState(mIndex); + bs = ts->states[mIndex].get(); + if (bs != NULL) return bs; + } + } + + return mGlobalState; +} + +}; // namespace android diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp new file mode 100644 index 000000000..26fb22abc --- /dev/null +++ b/libs/utils/CallStack.cpp @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "CallStack" + +#include +#include +#include + +#if HAVE_DLADDR +#include +#endif + +#if HAVE_CXXABI +#include +#endif + +#include + +#include +#include +#include +#include + + +/*****************************************************************************/ +namespace android { + + +typedef struct { + size_t count; + size_t ignore; + const void** addrs; +} stack_crawl_state_t; + +static +_Unwind_Reason_Code trace_function(_Unwind_Context *context, void *arg) +{ + stack_crawl_state_t* state = (stack_crawl_state_t*)arg; + if (state->count) { + void* ip = (void*)_Unwind_GetIP(context); + if (ip) { + if (state->ignore) { + state->ignore--; + } else { + state->addrs[0] = ip; + state->addrs++; + state->count--; + } + } + } + return _URC_NO_REASON; +} + +static +int backtrace(const void** addrs, size_t ignore, size_t size) +{ + stack_crawl_state_t state; + state.count = size; + state.ignore = ignore; + state.addrs = addrs; + _Unwind_Backtrace(trace_function, (void*)&state); + return size - state.count; +} + +/*****************************************************************************/ + +static +const char *lookup_symbol(const void* addr, void **offset, char* name, size_t bufSize) +{ +#if HAVE_DLADDR + Dl_info info; + if (dladdr(addr, &info)) { + *offset = info.dli_saddr; + return info.dli_sname; + } +#endif + return NULL; +} + +static +int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize) +{ + size_t out_len = 0; +#if HAVE_CXXABI + int status = 0; + char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); + if (status == 0) { + // OK + if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); + else out_len = 0; + free(demangled); + } else { + out_len = 0; + } +#endif + return out_len; +} + +/*****************************************************************************/ + +class MapInfo { + struct mapinfo { + struct mapinfo *next; + uint64_t start; + uint64_t end; + char name[]; + }; + + const char *map_to_name(uint64_t pc, const char* def) { + mapinfo* mi = getMapInfoList(); + while(mi) { + if ((pc >= mi->start) && (pc < mi->end)) + return mi->name; + mi = mi->next; + } + return def; + } + + mapinfo *parse_maps_line(char *line) { + mapinfo *mi; + int len = strlen(line); + if (len < 1) return 0; + line[--len] = 0; + if (len < 50) return 0; + if (line[20] != 'x') return 0; + mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47)); + if (mi == 0) return 0; + mi->start = strtoull(line, 0, 16); + mi->end = strtoull(line + 9, 0, 16); + mi->next = 0; + strcpy(mi->name, line + 49); + return mi; + } + + mapinfo* getMapInfoList() { + Mutex::Autolock _l(mLock); + if (milist == 0) { + char data[1024]; + FILE *fp; + sprintf(data, "/proc/%d/maps", getpid()); + fp = fopen(data, "r"); + if (fp) { + while(fgets(data, 1024, fp)) { + mapinfo *mi = parse_maps_line(data); + if(mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + } + return milist; + } + mapinfo* milist; + Mutex mLock; + static MapInfo sMapInfo; + +public: + MapInfo() + : milist(0) { + } + + ~MapInfo() { + while (milist) { + mapinfo *next = milist->next; + free(milist); + milist = next; + } + } + + static const char *mapAddressToName(const void* pc, const char* def) { + return sMapInfo.map_to_name((uint64_t)pc, def); + } + +}; + +/*****************************************************************************/ + +MapInfo MapInfo::sMapInfo; + +/*****************************************************************************/ + +CallStack::CallStack() + : mCount(0) +{ +} + +CallStack::CallStack(const CallStack& rhs) + : mCount(rhs.mCount) +{ + if (mCount) { + memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + } +} + +CallStack::~CallStack() +{ +} + +CallStack& CallStack::operator = (const CallStack& rhs) +{ + mCount = rhs.mCount; + if (mCount) { + memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + } + return *this; +} + +bool CallStack::operator == (const CallStack& rhs) const { + if (mCount != rhs.mCount) + return false; + return !mCount || (memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) == 0); +} + +bool CallStack::operator != (const CallStack& rhs) const { + return !operator == (rhs); +} + +bool CallStack::operator < (const CallStack& rhs) const { + if (mCount != rhs.mCount) + return mCount < rhs.mCount; + return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) < 0; +} + +bool CallStack::operator >= (const CallStack& rhs) const { + return !operator < (rhs); +} + +bool CallStack::operator > (const CallStack& rhs) const { + if (mCount != rhs.mCount) + return mCount > rhs.mCount; + return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) > 0; +} + +bool CallStack::operator <= (const CallStack& rhs) const { + return !operator > (rhs); +} + +const void* CallStack::operator [] (int index) const { + if (index >= int(mCount)) + return 0; + return mStack[index]; +} + + +void CallStack::clear() +{ + mCount = 0; +} + +void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) +{ + if (maxDepth > MAX_DEPTH) + maxDepth = MAX_DEPTH; + mCount = backtrace(mStack, ignoreDepth, maxDepth); +} + +// Return the stack frame name on the designated level +String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const +{ + String8 res; + char namebuf[1024]; + char tmp[256]; + char tmp1[32]; + char tmp2[32]; + void *offs; + + const void* ip = mStack[level]; + if (!ip) return res; + + if (prefix) res.append(prefix); + snprintf(tmp1, 32, "#%02d ", level); + res.append(tmp1); + + const char* name = lookup_symbol(ip, &offs, namebuf, sizeof(namebuf)); + if (name) { + if (linux_gcc_demangler(name, tmp, 256) != 0) + name = tmp; + snprintf(tmp1, 32, "0x%p: <", ip); + snprintf(tmp2, 32, ">+0x%p", offs); + res.append(tmp1); + res.append(name); + res.append(tmp2); + } else { + name = MapInfo::mapAddressToName(ip, ""); + snprintf(tmp, 256, "pc %p %s", ip, name); + res.append(tmp); + } + res.append("\n"); + + return res; +} + +// Dump a stack trace to the log +void CallStack::dump(const char* prefix) const +{ + /* + * Sending a single long log may be truncated since the stack levels can + * get very deep. So we request function names of each frame individually. + */ + for (int i=0; i + +#include + +#include +#include +#include + +namespace android { + +// --------------------------------------------------------------------- + +static const char indentStr[] = +" " +" "; + +const char* stringForIndent(int32_t indentLevel) +{ + ssize_t off = sizeof(indentStr)-1-(indentLevel*2); + return indentStr + (off < 0 ? 0 : off); +} + +// --------------------------------------------------------------------- + +static void defaultPrintFunc(void* cookie, const char* txt) +{ + printf("%s", txt); +} + +// --------------------------------------------------------------------- + +static inline int isident(int c) +{ + return isalnum(c) || c == '_'; +} + +static inline bool isasciitype(char c) +{ + if( c >= ' ' && c < 127 && c != '\'' && c != '\\' ) return true; + return false; +} + +static inline char makehexdigit(uint32_t val) +{ + return "0123456789abcdef"[val&0xF]; +} + +static char* appendhexnum(uint32_t val, char* out) +{ + for( int32_t i=28; i>=0; i-=4 ) { + *out++ = makehexdigit( val>>i ); + } + *out = 0; + return out; +} + +static inline char makeupperhexdigit(uint32_t val) +{ + return "0123456789ABCDEF"[val&0xF]; +} + +static char* appendupperhexnum(uint32_t val, char* out) +{ + for( int32_t i=28; i>=0; i-=4 ) { + *out++ = makeupperhexdigit( val>>i ); + } + *out = 0; + return out; +} + +static char* appendcharornum(char c, char* out, bool skipzero = true) +{ + if (skipzero && c == 0) return out; + + if (isasciitype(c)) { + *out++ = c; + return out; + } + + *out++ = '\\'; + *out++ = 'x'; + *out++ = makehexdigit(c>>4); + *out++ = makehexdigit(c); + return out; +} + +static char* typetostring(uint32_t type, char* out, + bool fullContext = true, + bool strict = false) +{ + char* pos = out; + char c[4]; + c[0] = (char)((type>>24)&0xFF); + c[1] = (char)((type>>16)&0xFF); + c[2] = (char)((type>>8)&0xFF); + c[3] = (char)(type&0xFF); + bool valid; + if( !strict ) { + // now even less strict! + // valid = isasciitype(c[3]); + valid = true; + int32_t i = 0; + bool zero = true; + while (valid && i<3) { + if (c[i] == 0) { + if (!zero) valid = false; + } else { + zero = false; + //if (!isasciitype(c[i])) valid = false; + } + i++; + } + // if all zeros, not a valid type code. + if (zero) valid = false; + } else { + valid = isident(c[3]) ? true : false; + int32_t i = 0; + bool zero = true; + while (valid && i<3) { + if (c[i] == 0) { + if (!zero) valid = false; + } else { + zero = false; + if (!isident(c[i])) valid = false; + } + i++; + } + } + if( valid && (!fullContext || c[0] != '0' || c[1] != 'x') ) { + if( fullContext ) *pos++ = '\''; + pos = appendcharornum(c[0], pos); + pos = appendcharornum(c[1], pos); + pos = appendcharornum(c[2], pos); + pos = appendcharornum(c[3], pos); + if( fullContext ) *pos++ = '\''; + *pos = 0; + return pos; + } + + if( fullContext ) { + *pos++ = '0'; + *pos++ = 'x'; + } + return appendhexnum(type, pos); +} + +void printTypeCode(uint32_t typeCode, debugPrintFunc func, void* cookie) +{ + char buffer[32]; + char* end = typetostring(typeCode, buffer); + *end = 0; + func ? (*func)(cookie, buffer) : defaultPrintFunc(cookie, buffer); +} + +void printHexData(int32_t indent, const void *buf, size_t length, + size_t bytesPerLine, int32_t singleLineBytesCutoff, + size_t alignment, bool cStyle, + debugPrintFunc func, void* cookie) +{ + if (alignment == 0) { + if (bytesPerLine >= 16) alignment = 4; + else if (bytesPerLine >= 8) alignment = 2; + else alignment = 1; + } + if (func == NULL) func = defaultPrintFunc; + + size_t offset; + + unsigned char *pos = (unsigned char *)buf; + + if (pos == NULL) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + func(cookie, "(NULL)"); + return; + } + + if (length == 0) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + func(cookie, "(empty)"); + return; + } + + if ((int32_t)length < 0) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + char buf[64]; + sprintf(buf, "(bad length: %d)", length); + func(cookie, buf); + return; + } + + char buffer[256]; + static const size_t maxBytesPerLine = (sizeof(buffer)-1-11-4)/(3+1); + + if (bytesPerLine > maxBytesPerLine) bytesPerLine = maxBytesPerLine; + + const bool oneLine = (int32_t)length <= singleLineBytesCutoff; + bool newLine = false; + if (cStyle) { + indent++; + func(cookie, "{\n"); + newLine = true; + } else if (!oneLine) { + func(cookie, "\n"); + newLine = true; + } + + for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) { + long remain = length; + + char* c = buffer; + if (!oneLine && !cStyle) { + sprintf(c, "0x%08x: ", (int)offset); + c += 12; + } + + size_t index; + size_t word; + + for (word = 0; word < bytesPerLine; ) { + +#ifdef HAVE_LITTLE_ENDIAN + const size_t startIndex = word+(alignment-(alignment?1:0)); + const ssize_t dir = -1; +#else + const size_t startIndex = word; + const ssize_t dir = 1; +#endif + + for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) { + + if (!cStyle) { + if (index == 0 && word > 0 && alignment > 0) { + *c++ = ' '; + } + + if (remain-- > 0) { + const unsigned char val = *(pos+startIndex+(index*dir)); + *c++ = makehexdigit(val>>4); + *c++ = makehexdigit(val); + } else if (!oneLine) { + *c++ = ' '; + *c++ = ' '; + } + } else { + if (remain > 0) { + if (index == 0 && word > 0) { + *c++ = ','; + *c++ = ' '; + } + if (index == 0) { + *c++ = '0'; + *c++ = 'x'; + } + const unsigned char val = *(pos+startIndex+(index*dir)); + *c++ = makehexdigit(val>>4); + *c++ = makehexdigit(val); + remain--; + } + } + } + + word += index; + } + + if (!cStyle) { + remain = length; + *c++ = ' '; + *c++ = '\''; + for (index = 0; index < bytesPerLine; index++) { + + if (remain-- > 0) { + const unsigned char val = pos[index]; + *c++ = (val >= ' ' && val < 127) ? val : '.'; + } else if (!oneLine) { + *c++ = ' '; + } + } + + *c++ = '\''; + if (length > bytesPerLine) *c++ = '\n'; + } else { + if (remain > 0) *c++ = ','; + *c++ = '\n'; + } + + if (newLine && indent) func(cookie, stringForIndent(indent)); + *c = 0; + func(cookie, buffer); + newLine = true; + + if (length <= bytesPerLine) break; + length -= bytesPerLine; + } + + if (cStyle) { + if (indent > 0) func(cookie, stringForIndent(indent-1)); + func(cookie, "};"); + } +} + +}; // namespace android + diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp new file mode 100644 index 000000000..e1ba9b238 --- /dev/null +++ b/libs/utils/FileMap.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Shared file mapping class. +// + +#define LOG_TAG "filemap" + +#include +#include + +#include +#include + +#ifdef HAVE_POSIX_FILEMAP +#include +#endif + +#include +#include +#include +#include + +using namespace android; + +/*static*/ long FileMap::mPageSize = -1; + + +/* + * Constructor. Create an empty object. + */ +FileMap::FileMap(void) + : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0), + mDataPtr(NULL), mDataLength(0) +{ +} + +/* + * Destructor. + */ +FileMap::~FileMap(void) +{ + assert(mRefCount == 0); + + //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength); + + mRefCount = -100; // help catch double-free + if (mFileName != NULL) { + free(mFileName); + } +#ifdef HAVE_POSIX_FILEMAP + if (munmap(mBasePtr, mBaseLength) != 0) { + LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); + } +#endif +#ifdef HAVE_WIN32_FILEMAP + if ( UnmapViewOfFile(mBasePtr) == 0) { + LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, + GetLastError() ); + } + CloseHandle(mFileMapping); + CloseHandle(mFileHandle); +#endif +} + + +/* + * Create a new mapping on an open file. + * + * Closing the file descriptor does not unmap the pages, so we don't + * claim ownership of the fd. + * + * Returns "false" on failure. + */ +bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t length, bool readOnly) +{ +#ifdef HAVE_WIN32_FILEMAP + int adjust; + off_t adjOffset; + size_t adjLength; + + if (mPageSize == -1) { + SYSTEM_INFO si; + + GetSystemInfo( &si ); + mPageSize = si.dwAllocationGranularity; + } + + DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE; + + mFileHandle = (HANDLE) _get_osfhandle(fd); + mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL); + if (mFileMapping == NULL) { + LOGE("CreateFileMapping(%p, %lx) failed with error %ld\n", + mFileHandle, protect, GetLastError() ); + return false; + } + + adjust = offset % mPageSize; + adjOffset = offset - adjust; + adjLength = length + adjust; + + mBasePtr = MapViewOfFile( mFileMapping, + readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, + 0, + (DWORD)(adjOffset), + adjLength ); + if (mBasePtr == NULL) { + LOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n", + adjOffset, adjLength, GetLastError() ); + CloseHandle(mFileMapping); + mFileMapping = INVALID_HANDLE_VALUE; + return false; + } +#endif +#ifdef HAVE_POSIX_FILEMAP + int prot, flags, adjust; + off_t adjOffset; + size_t adjLength; + + void* ptr; + + assert(mRefCount == 1); + assert(fd >= 0); + assert(offset >= 0); + assert(length > 0); + + /* init on first use */ + if (mPageSize == -1) { +#if NOT_USING_KLIBC + mPageSize = sysconf(_SC_PAGESIZE); + if (mPageSize == -1) { + LOGE("could not get _SC_PAGESIZE\n"); + return false; + } +#else + /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */ + mPageSize = 4096; +#endif + } + + adjust = offset % mPageSize; +try_again: + adjOffset = offset - adjust; + adjLength = length + adjust; + + flags = MAP_SHARED; + prot = PROT_READ; + if (!readOnly) + prot |= PROT_WRITE; + + ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset); + if (ptr == MAP_FAILED) { + // Cygwin does not seem to like file mapping files from an offset. + // So if we fail, try again with offset zero + if (adjOffset > 0) { + adjust = offset; + goto try_again; + } + + LOGE("mmap(%ld,%ld) failed: %s\n", + (long) adjOffset, (long) adjLength, strerror(errno)); + return false; + } + mBasePtr = ptr; +#endif /* HAVE_POSIX_FILEMAP */ + + mFileName = origFileName != NULL ? strdup(origFileName) : NULL; + mBaseLength = adjLength; + mDataOffset = offset; + mDataPtr = (char*) mBasePtr + adjust; + mDataLength = length; + + assert(mBasePtr != NULL); + + LOGV("MAP: base %p/%d data %p/%d\n", + mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength); + + return true; +} + +/* + * Provide guidance to the system. + */ +int FileMap::advise(MapAdvice advice) +{ +#if HAVE_MADVISE + int cc, sysAdvice; + + switch (advice) { + case NORMAL: sysAdvice = MADV_NORMAL; break; + case RANDOM: sysAdvice = MADV_RANDOM; break; + case SEQUENTIAL: sysAdvice = MADV_SEQUENTIAL; break; + case WILLNEED: sysAdvice = MADV_WILLNEED; break; + case DONTNEED: sysAdvice = MADV_DONTNEED; break; + default: + assert(false); + return -1; + } + + cc = madvise(mBasePtr, mBaseLength, sysAdvice); + if (cc != 0) + LOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); + return cc; +#else + return -1; +#endif // HAVE_MADVISE +} diff --git a/libs/utils/IDataConnection.cpp b/libs/utils/IDataConnection.cpp new file mode 100644 index 000000000..c6d49aa48 --- /dev/null +++ b/libs/utils/IDataConnection.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2006 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 + +namespace android { + +// --------------------------------------------------------------------------- + +enum +{ + CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1 +}; + +class BpDataConnection : public BpInterface +{ +public: + BpDataConnection::BpDataConnection(const sp& impl) + : BpInterface(impl) + { + } + + virtual void connect() + { + Parcel data, reply; + data.writeInterfaceToken(IDataConnection::descriptor()); + remote()->transact(CONNECT_TRANSACTION, data, &reply); + } + + virtual void disconnect() + { + Parcel data, reply; + remote()->transact(DISCONNECT_TRANSACTION, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(DataConnection, "android.utils.IDataConnection"); + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnDataConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) + { + case CONNECT_TRANSACTION: + { + CHECK_INTERFACE(IDataConnection, data, reply); + connect(); + return NO_ERROR; + } + + case DISCONNECT_TRANSACTION: + { + CHECK_INTERFACE(IDataConnection, data, reply); + disconnect(); + return NO_ERROR; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/IInterface.cpp b/libs/utils/IInterface.cpp new file mode 100644 index 000000000..6ea817887 --- /dev/null +++ b/libs/utils/IInterface.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005 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 + +namespace android { + +// --------------------------------------------------------------------------- + +sp IInterface::asBinder() +{ + return this ? onAsBinder() : NULL; +} + +sp IInterface::asBinder() const +{ + return this ? const_cast(this)->onAsBinder() : NULL; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/IMemory.cpp b/libs/utils/IMemory.cpp new file mode 100644 index 000000000..429bc2b94 --- /dev/null +++ b/libs/utils/IMemory.cpp @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2008 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. + */ + +#define LOG_TAG "IMemory" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define VERBOSE 0 + +namespace android { +// --------------------------------------------------------------------------- + +class HeapCache : public IBinder::DeathRecipient +{ +public: + HeapCache(); + virtual ~HeapCache(); + + virtual void binderDied(const wp& who); + + sp find_heap(const sp& binder); + void pin_heap(const sp& binder); + void free_heap(const sp& binder); + sp get_heap(const sp& binder); + void dump_heaps(); + +private: + // For IMemory.cpp + struct heap_info_t { + sp heap; + int32_t count; + }; + + void free_heap(const wp& binder); + + Mutex mHeapCacheLock; + KeyedVector< wp, heap_info_t > mHeapCache; +}; + +static sp gHeapCache = new HeapCache(); + +/******************************************************************************/ + +enum { + HEAP_ID = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpMemoryHeap : public BpInterface +{ +public: + BpMemoryHeap(const sp& impl); + virtual ~BpMemoryHeap(); + + virtual int getHeapID() const; + virtual void* getBase() const; + virtual size_t getSize() const; + virtual uint32_t getFlags() const; + +private: + friend class IMemory; + friend class HeapCache; + + // for debugging in this module + static inline sp find_heap(const sp& binder) { + return gHeapCache->find_heap(binder); + } + static inline void free_heap(const sp& binder) { + gHeapCache->free_heap(binder); + } + static inline sp get_heap(const sp& binder) { + return gHeapCache->get_heap(binder); + } + static inline void dump_heaps() { + gHeapCache->dump_heaps(); + } + void inline pin_heap() const { + gHeapCache->pin_heap(const_cast(this)->asBinder()); + } + + void assertMapped() const; + void assertReallyMapped() const; + void pinHeap() const; + + mutable volatile int32_t mHeapId; + mutable void* mBase; + mutable size_t mSize; + mutable uint32_t mFlags; + mutable bool mRealHeap; + mutable Mutex mLock; +}; + +// ---------------------------------------------------------------------------- + +enum { + GET_MEMORY = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpMemory : public BpInterface +{ +public: + BpMemory(const sp& impl); + virtual ~BpMemory(); + virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const; + +private: + mutable sp mHeap; + mutable ssize_t mOffset; + mutable size_t mSize; +}; + +/******************************************************************************/ + +void* IMemory::fastPointer(const sp& binder, ssize_t offset) const +{ + sp realHeap = BpMemoryHeap::get_heap(binder); + void* const base = realHeap->base(); + if (base == MAP_FAILED) + return 0; + return static_cast(base) + offset; +} + +void* IMemory::pointer() const { + ssize_t offset; + sp heap = getMemory(&offset); + void* const base = heap!=0 ? heap->base() : MAP_FAILED; + if (base == MAP_FAILED) + return 0; + return static_cast(base) + offset; +} + +size_t IMemory::size() const { + size_t size; + getMemory(NULL, &size); + return size; +} + +ssize_t IMemory::offset() const { + ssize_t offset; + getMemory(&offset); + return offset; +} + +/******************************************************************************/ + +BpMemory::BpMemory(const sp& impl) + : BpInterface(impl), mOffset(0), mSize(0) +{ +} + +BpMemory::~BpMemory() +{ +} + +sp BpMemory::getMemory(ssize_t* offset, size_t* size) const +{ + if (mHeap == 0) { + Parcel data, reply; + data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); + if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { + sp heap = reply.readStrongBinder(); + ssize_t o = reply.readInt32(); + size_t s = reply.readInt32(); + if (heap != 0) { + mHeap = interface_cast(heap); + if (mHeap != 0) { + mOffset = o; + mSize = s; + } + } + } + } + if (offset) *offset = mOffset; + if (size) *size = mSize; + return mHeap; +} + +// --------------------------------------------------------------------------- + +IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMemory::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_MEMORY: { + CHECK_INTERFACE(IMemory, data, reply); + ssize_t offset; + size_t size; + reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() ); + reply->writeInt32(offset); + reply->writeInt32(size); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + + +/******************************************************************************/ + +BpMemoryHeap::BpMemoryHeap(const sp& impl) + : BpInterface(impl), + mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false) +{ +} + +BpMemoryHeap::~BpMemoryHeap() { + if (mHeapId != -1) { + close(mHeapId); + if (mRealHeap) { + // by construction we're the last one + if (mBase != MAP_FAILED) { + sp binder = const_cast(this)->asBinder(); + + if (VERBOSE) { + LOGD("UNMAPPING binder=%p, heap=%p, size=%d, fd=%d", + binder.get(), this, mSize, mHeapId); + CallStack stack; + stack.update(); + stack.dump("callstack"); + } + + munmap(mBase, mSize); + } + } else { + // remove from list only if it was mapped before + sp binder = const_cast(this)->asBinder(); + free_heap(binder); + } + } +} + +void BpMemoryHeap::assertMapped() const +{ + if (mHeapId == -1) { + sp binder(const_cast(this)->asBinder()); + sp heap(static_cast(find_heap(binder).get())); + heap->assertReallyMapped(); + if (heap->mBase != MAP_FAILED) { + Mutex::Autolock _l(mLock); + if (mHeapId == -1) { + mBase = heap->mBase; + mSize = heap->mSize; + android_atomic_write( dup( heap->mHeapId ), &mHeapId ); + } + } else { + // something went wrong + free_heap(binder); + } + } +} + +void BpMemoryHeap::assertReallyMapped() const +{ + if (mHeapId == -1) { + + // remote call without mLock held, worse case scenario, we end up + // calling transact() from multiple threads, but that's not a problem, + // only mmap below must be in the critical section. + + Parcel data, reply; + data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); + status_t err = remote()->transact(HEAP_ID, data, &reply); + int parcel_fd = reply.readFileDescriptor(); + ssize_t size = reply.readInt32(); + uint32_t flags = reply.readInt32(); + + LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%d, err=%d (%s)", + asBinder().get(), parcel_fd, size, err, strerror(-err)); + + int fd = dup( parcel_fd ); + LOGE_IF(fd==-1, "cannot dup fd=%d, size=%d, err=%d (%s)", + parcel_fd, size, err, strerror(errno)); + + int access = PROT_READ; + if (!(flags & READ_ONLY)) { + access |= PROT_WRITE; + } + + Mutex::Autolock _l(mLock); + if (mHeapId == -1) { + mRealHeap = true; + mBase = mmap(0, size, access, MAP_SHARED, fd, 0); + if (mBase == MAP_FAILED) { + LOGE("cannot map BpMemoryHeap (binder=%p), size=%d, fd=%d (%s)", + asBinder().get(), size, fd, strerror(errno)); + close(fd); + } else { + if (flags & MAP_ONCE) { + //LOGD("pinning heap (binder=%p, size=%d, fd=%d", + // asBinder().get(), size, fd); + pin_heap(); + } + mSize = size; + mFlags = flags; + android_atomic_write(fd, &mHeapId); + } + } + } +} + +int BpMemoryHeap::getHeapID() const { + assertMapped(); + return mHeapId; +} + +void* BpMemoryHeap::getBase() const { + assertMapped(); + return mBase; +} + +size_t BpMemoryHeap::getSize() const { + assertMapped(); + return mSize; +} + +uint32_t BpMemoryHeap::getFlags() const { + assertMapped(); + return mFlags; +} + +// --------------------------------------------------------------------------- + +IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); + +status_t BnMemoryHeap::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case HEAP_ID: { + CHECK_INTERFACE(IMemoryHeap, data, reply); + reply->writeFileDescriptor(getHeapID()); + reply->writeInt32(getSize()); + reply->writeInt32(getFlags()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +/*****************************************************************************/ + +HeapCache::HeapCache() + : DeathRecipient() +{ +} + +HeapCache::~HeapCache() +{ +} + +void HeapCache::binderDied(const wp& binder) +{ + //LOGD("binderDied binder=%p", binder.unsafe_get()); + free_heap(binder); +} + +sp HeapCache::find_heap(const sp& binder) +{ + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info = mHeapCache.editValueAt(i); + LOGD_IF(VERBOSE, + "found binder=%p, heap=%p, size=%d, fd=%d, count=%d", + binder.get(), info.heap.get(), + static_cast(info.heap.get())->mSize, + static_cast(info.heap.get())->mHeapId, + info.count); + android_atomic_inc(&info.count); + return info.heap; + } else { + heap_info_t info; + info.heap = interface_cast(binder); + info.count = 1; + //LOGD("adding binder=%p, heap=%p, count=%d", + // binder.get(), info.heap.get(), info.count); + mHeapCache.add(binder, info); + return info.heap; + } +} + +void HeapCache::pin_heap(const sp& binder) +{ + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info(mHeapCache.editValueAt(i)); + android_atomic_inc(&info.count); + binder->linkToDeath(this); + } else { + LOGE("pin_heap binder=%p not found!!!", binder.get()); + } +} + +void HeapCache::free_heap(const sp& binder) { + free_heap( wp(binder) ); +} + +void HeapCache::free_heap(const wp& binder) +{ + sp rel; + { + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info(mHeapCache.editValueAt(i)); + int32_t c = android_atomic_dec(&info.count); + if (c == 1) { + LOGD_IF(VERBOSE, + "removing binder=%p, heap=%p, size=%d, fd=%d, count=%d", + binder.unsafe_get(), info.heap.get(), + static_cast(info.heap.get())->mSize, + static_cast(info.heap.get())->mHeapId, + info.count); + rel = mHeapCache.valueAt(i).heap; + mHeapCache.removeItemsAt(i); + } + } else { + LOGE("free_heap binder=%p not found!!!", binder.unsafe_get()); + } + } +} + +sp HeapCache::get_heap(const sp& binder) +{ + sp realHeap; + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) realHeap = mHeapCache.valueAt(i).heap; + else realHeap = interface_cast(binder); + return realHeap; +} + +void HeapCache::dump_heaps() +{ + Mutex::Autolock _l(mHeapCacheLock); + int c = mHeapCache.size(); + for (int i=0 ; i(info.heap.get())); + LOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%d)", + mHeapCache.keyAt(i).unsafe_get(), + info.heap.get(), info.count, + h->mHeapId, h->mBase, h->mSize); + } +} + + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/IPCThreadState.cpp b/libs/utils/IPCThreadState.cpp new file mode 100644 index 000000000..04ae1424e --- /dev/null +++ b/libs/utils/IPCThreadState.cpp @@ -0,0 +1,1030 @@ +/* + * Copyright (C) 2005 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 +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_PTHREADS +#include +#include +#include +#endif +#ifdef HAVE_WIN32_THREADS +#include +#endif + + +#if LOG_NDEBUG + +#define IF_LOG_TRANSACTIONS() if (false) +#define IF_LOG_COMMANDS() if (false) +#define LOG_REMOTEREFS(...) +#define IF_LOG_REMOTEREFS() if (false) +#define LOG_THREADPOOL(...) +#define LOG_ONEWAY(...) + +#else + +#define IF_LOG_TRANSACTIONS() IF_LOG(LOG_VERBOSE, "transact") +#define IF_LOG_COMMANDS() IF_LOG(LOG_VERBOSE, "ipc") +#define LOG_REMOTEREFS(...) LOG(LOG_DEBUG, "remoterefs", __VA_ARGS__) +#define IF_LOG_REMOTEREFS() IF_LOG(LOG_DEBUG, "remoterefs") +#define LOG_THREADPOOL(...) LOG(LOG_DEBUG, "threadpool", __VA_ARGS__) +#define LOG_ONEWAY(...) LOG(LOG_DEBUG, "ipc", __VA_ARGS__) + +#endif + +// --------------------------------------------------------------------------- + +namespace android { + +static const char* getReturnString(size_t idx); +static const char* getCommandString(size_t idx); +static const void* printReturnCommand(TextOutput& out, const void* _cmd); +static const void* printCommand(TextOutput& out, const void* _cmd); + +// This will result in a missing symbol failure if the IF_LOG_COMMANDS() +// conditionals don't get stripped... but that is probably what we want. +#if !LOG_NDEBUG +static const char *kReturnStrings[] = { +#if 1 /* TODO: error update strings */ + "unknown", +#else + "BR_OK", + "BR_TIMEOUT", + "BR_WAKEUP", + "BR_TRANSACTION", + "BR_REPLY", + "BR_ACQUIRE_RESULT", + "BR_DEAD_REPLY", + "BR_TRANSACTION_COMPLETE", + "BR_INCREFS", + "BR_ACQUIRE", + "BR_RELEASE", + "BR_DECREFS", + "BR_ATTEMPT_ACQUIRE", + "BR_EVENT_OCCURRED", + "BR_NOOP", + "BR_SPAWN_LOOPER", + "BR_FINISHED", + "BR_DEAD_BINDER", + "BR_CLEAR_DEATH_NOTIFICATION_DONE" +#endif +}; + +static const char *kCommandStrings[] = { +#if 1 /* TODO: error update strings */ + "unknown", +#else + "BC_NOOP", + "BC_TRANSACTION", + "BC_REPLY", + "BC_ACQUIRE_RESULT", + "BC_FREE_BUFFER", + "BC_TRANSACTION_COMPLETE", + "BC_INCREFS", + "BC_ACQUIRE", + "BC_RELEASE", + "BC_DECREFS", + "BC_INCREFS_DONE", + "BC_ACQUIRE_DONE", + "BC_ATTEMPT_ACQUIRE", + "BC_RETRIEVE_ROOT_OBJECT", + "BC_SET_THREAD_ENTRY", + "BC_REGISTER_LOOPER", + "BC_ENTER_LOOPER", + "BC_EXIT_LOOPER", + "BC_SYNC", + "BC_STOP_PROCESS", + "BC_STOP_SELF", + "BC_REQUEST_DEATH_NOTIFICATION", + "BC_CLEAR_DEATH_NOTIFICATION", + "BC_DEAD_BINDER_DONE" +#endif +}; + +static const char* getReturnString(size_t idx) +{ + if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0])) + return kReturnStrings[idx]; + else + return "unknown"; +} + +static const char* getCommandString(size_t idx) +{ + if (idx < sizeof(kCommandStrings) / sizeof(kCommandStrings[0])) + return kCommandStrings[idx]; + else + return "unknown"; +} + +static const void* printBinderTransactionData(TextOutput& out, const void* data) +{ + const binder_transaction_data* btd = + (const binder_transaction_data*)data; + out << "target=" << btd->target.ptr << " (cookie " << btd->cookie << ")" << endl + << "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl + << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size + << " bytes)" << endl + << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size + << " bytes)" << endl; + return btd+1; +} + +static const void* printReturnCommand(TextOutput& out, const void* _cmd) +{ + static const int32_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]); + + const int32_t* cmd = (const int32_t*)_cmd; + int32_t code = *cmd++; + if (code == BR_ERROR) { + out << "BR_ERROR: " << (void*)(*cmd++) << endl; + return cmd; + } else if (code < 0 || code >= N) { + out << "Unknown reply: " << code << endl; + return cmd; + } + + out << kReturnStrings[code]; + switch (code) { + case BR_TRANSACTION: + case BR_REPLY: { + out << ": " << indent; + cmd = (const int32_t *)printBinderTransactionData(out, cmd); + out << dedent; + } break; + + case BR_ACQUIRE_RESULT: { + const int32_t res = *cmd++; + out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); + } break; + + case BR_INCREFS: + case BR_ACQUIRE: + case BR_RELEASE: + case BR_DECREFS: { + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; + } break; + + case BR_ATTEMPT_ACQUIRE: { + const int32_t p = *cmd++; + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c + << "), pri=" << p; + } break; + + case BR_DEAD_BINDER: + case BR_CLEAR_DEATH_NOTIFICATION_DONE: { + const int32_t c = *cmd++; + out << ": death cookie " << (void*)c; + } break; + } + + out << endl; + return cmd; +} + +static const void* printCommand(TextOutput& out, const void* _cmd) +{ + static const int32_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]); + + const int32_t* cmd = (const int32_t*)_cmd; + int32_t code = *cmd++; + if (code < 0 || code >= N) { + out << "Unknown command: " << code << endl; + return cmd; + } + + out << kCommandStrings[code]; + switch (code) { + case BC_TRANSACTION: + case BC_REPLY: { + out << ": " << indent; + cmd = (const int32_t *)printBinderTransactionData(out, cmd); + out << dedent; + } break; + + case BC_ACQUIRE_RESULT: { + const int32_t res = *cmd++; + out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); + } break; + + case BC_FREE_BUFFER: { + const int32_t buf = *cmd++; + out << ": buffer=" << (void*)buf; + } break; + + case BC_INCREFS: + case BC_ACQUIRE: + case BC_RELEASE: + case BC_DECREFS: { + const int32_t d = *cmd++; + out << ": descriptor=" << (void*)d; + } break; + + case BC_INCREFS_DONE: + case BC_ACQUIRE_DONE: { + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; + } break; + + case BC_ATTEMPT_ACQUIRE: { + const int32_t p = *cmd++; + const int32_t d = *cmd++; + out << ": decriptor=" << (void*)d << ", pri=" << p; + } break; + + case BC_REQUEST_DEATH_NOTIFICATION: + case BC_CLEAR_DEATH_NOTIFICATION: { + const int32_t h = *cmd++; + const int32_t c = *cmd++; + out << ": handle=" << h << " (death cookie " << (void*)c << ")"; + } break; + + case BC_DEAD_BINDER_DONE: { + const int32_t c = *cmd++; + out << ": death cookie " << (void*)c; + } break; + } + + out << endl; + return cmd; +} +#endif + +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; +static bool gShutdown = false; + +IPCThreadState* IPCThreadState::self() +{ + if (gHaveTLS) { +restart: + const pthread_key_t k = gTLS; + IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); + if (st) return st; + return new IPCThreadState; + } + + if (gShutdown) return NULL; + + pthread_mutex_lock(&gTLSMutex); + if (!gHaveTLS) { + if (pthread_key_create(&gTLS, threadDestructor) != 0) { + pthread_mutex_unlock(&gTLSMutex); + return NULL; + } + gHaveTLS = true; + } + pthread_mutex_unlock(&gTLSMutex); + goto restart; +} + +void IPCThreadState::shutdown() +{ + gShutdown = true; + + if (gHaveTLS) { + // XXX Need to wait for all thread pool threads to exit! + IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS); + if (st) { + delete st; + pthread_setspecific(gTLS, NULL); + } + gHaveTLS = false; + } +} + +sp IPCThreadState::process() +{ + return mProcess; +} + +status_t IPCThreadState::clearLastError() +{ + const status_t err = mLastError; + mLastError = NO_ERROR; + return err; +} + +int IPCThreadState::getCallingPid() +{ + return mCallingPid; +} + +int IPCThreadState::getCallingUid() +{ + return mCallingUid; +} + +int64_t IPCThreadState::clearCallingIdentity() +{ + int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; + clearCaller(); + return token; +} + +void IPCThreadState::restoreCallingIdentity(int64_t token) +{ + mCallingUid = (int)(token>>32); + mCallingPid = (int)token; +} + +void IPCThreadState::clearCaller() +{ + if (mProcess->supportsProcesses()) { + mCallingPid = getpid(); + mCallingUid = getuid(); + } else { + mCallingPid = -1; + mCallingUid = -1; + } +} + +void IPCThreadState::flushCommands() +{ + if (mProcess->mDriverFD <= 0) + return; + talkWithDriver(false); +} + +void IPCThreadState::joinThreadPool(bool isMain) +{ + LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); + + mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); + + status_t result; + do { + int32_t cmd; + + // When we've cleared the incoming command queue, process any pending derefs + if (mIn.dataPosition() >= mIn.dataSize()) { + size_t numPending = mPendingWeakDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + RefBase::weakref_type* refs = mPendingWeakDerefs[i]; + refs->decWeak(mProcess.get()); + } + mPendingWeakDerefs.clear(); + } + + numPending = mPendingStrongDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + BBinder* obj = mPendingStrongDerefs[i]; + obj->decStrong(mProcess.get()); + } + mPendingStrongDerefs.clear(); + } + } + + // now get the next command to be processed, waiting if necessary + result = talkWithDriver(); + if (result >= NO_ERROR) { + size_t IN = mIn.dataAvail(); + if (IN < sizeof(int32_t)) continue; + cmd = mIn.readInt32(); + IF_LOG_COMMANDS() { + alog << "Processing top-level Command: " + << getReturnString(cmd) << endl; + } + result = executeCommand(cmd); + } + + // Let this thread exit the thread pool if it is no longer + // needed and it is not the main process thread. + if(result == TIMED_OUT && !isMain) { + break; + } + } while (result != -ECONNREFUSED && result != -EBADF); + + LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n", + (void*)pthread_self(), getpid(), (void*)result); + + mOut.writeInt32(BC_EXIT_LOOPER); + talkWithDriver(false); +} + +void IPCThreadState::stopProcess(bool immediate) +{ + //LOGI("**** STOPPING PROCESS"); + flushCommands(); + int fd = mProcess->mDriverFD; + mProcess->mDriverFD = -1; + close(fd); + //kill(getpid(), SIGKILL); +} + +status_t IPCThreadState::transact(int32_t handle, + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) +{ + status_t err = data.errorCheck(); + + flags |= TF_ACCEPT_FDS; + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " + << handle << " / code " << TypeCode(code) << ": " + << indent << data << dedent << endl; + } + + if (err == NO_ERROR) { + LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(), + (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY"); + err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); + } + + if (err != NO_ERROR) { + if (reply) reply->setError(err); + return (mLastError = err); + } + + if ((flags & TF_ONE_WAY) == 0) { + if (reply) { + err = waitForResponse(reply); + } else { + Parcel fakeReply; + err = waitForResponse(&fakeReply); + } + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand " + << handle << ": "; + if (reply) alog << indent << *reply << dedent << endl; + else alog << "(none requested)" << endl; + } + } else { + err = waitForResponse(NULL, NULL); + } + + return err; +} + +void IPCThreadState::incStrongHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle); + mOut.writeInt32(BC_ACQUIRE); + mOut.writeInt32(handle); +} + +void IPCThreadState::decStrongHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle); + mOut.writeInt32(BC_RELEASE); + mOut.writeInt32(handle); +} + +void IPCThreadState::incWeakHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle); + mOut.writeInt32(BC_INCREFS); + mOut.writeInt32(handle); +} + +void IPCThreadState::decWeakHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle); + mOut.writeInt32(BC_DECREFS); + mOut.writeInt32(handle); +} + +status_t IPCThreadState::attemptIncStrongHandle(int32_t handle) +{ + mOut.writeInt32(BC_ATTEMPT_ACQUIRE); + mOut.writeInt32(0); // xxx was thread priority + mOut.writeInt32(handle); + status_t result = UNKNOWN_ERROR; + + waitForResponse(NULL, &result); + +#if LOG_REFCOUNTS + printf("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n", + handle, result == NO_ERROR ? "SUCCESS" : "FAILURE"); +#endif + + return result; +} + +void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder) +{ +#if LOG_REFCOUNTS + printf("IPCThreadState::expungeHandle(%ld)\n", handle); +#endif + self()->mProcess->expungeHandle(handle, binder); +} + +status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy) +{ + mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION); + mOut.writeInt32((int32_t)handle); + mOut.writeInt32((int32_t)proxy); + return NO_ERROR; +} + +status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) +{ + mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION); + mOut.writeInt32((int32_t)handle); + mOut.writeInt32((int32_t)proxy); + return NO_ERROR; +} + +IPCThreadState::IPCThreadState() + : mProcess(ProcessState::self()) +{ + pthread_setspecific(gTLS, this); + clearCaller(); + mIn.setDataCapacity(256); + mOut.setDataCapacity(256); +} + +IPCThreadState::~IPCThreadState() +{ +} + +status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags) +{ + status_t err; + status_t statusBuffer; + err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer); + if (err < NO_ERROR) return err; + + return waitForResponse(NULL, NULL); +} + +status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) +{ + int32_t cmd; + int32_t err; + + while (1) { + if ((err=talkWithDriver()) < NO_ERROR) break; + err = mIn.errorCheck(); + if (err < NO_ERROR) break; + if (mIn.dataAvail() == 0) continue; + + cmd = mIn.readInt32(); + + IF_LOG_COMMANDS() { + alog << "Processing waitForResponse Command: " + << getReturnString(cmd) << endl; + } + + switch (cmd) { + case BR_TRANSACTION_COMPLETE: + if (!reply && !acquireResult) goto finish; + break; + + case BR_DEAD_REPLY: + err = DEAD_OBJECT; + goto finish; + + case BR_FAILED_REPLY: + err = FAILED_TRANSACTION; + goto finish; + + case BR_ACQUIRE_RESULT: + { + LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT"); + const int32_t result = mIn.readInt32(); + if (!acquireResult) continue; + *acquireResult = result ? NO_ERROR : INVALID_OPERATION; + } + goto finish; + + case BR_REPLY: + { + binder_transaction_data tr; + err = mIn.read(&tr, sizeof(tr)); + LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY"); + if (err != NO_ERROR) goto finish; + + if (reply) { + if ((tr.flags & TF_STATUS_CODE) == 0) { + reply->ipcSetDataReference( + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), + freeBuffer, this); + } else { + err = *static_cast(tr.data.ptr.buffer); + freeBuffer(NULL, + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), this); + } + } else { + freeBuffer(NULL, + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), this); + continue; + } + } + goto finish; + + default: + err = executeCommand(cmd); + if (err != NO_ERROR) goto finish; + break; + } + } + +finish: + if (err != NO_ERROR) { + if (acquireResult) *acquireResult = err; + if (reply) reply->setError(err); + mLastError = err; + } + + return err; +} + +status_t IPCThreadState::talkWithDriver(bool doReceive) +{ + LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened"); + + binder_write_read bwr; + + // Is the read buffer empty? + const bool needRead = mIn.dataPosition() >= mIn.dataSize(); + + // We don't want to write anything if we are still reading + // from data left in the input buffer and the caller + // has requested to read the next data. + const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; + + bwr.write_size = outAvail; + bwr.write_buffer = (long unsigned int)mOut.data(); + + // This is what we'll read. + if (doReceive && needRead) { + bwr.read_size = mIn.dataCapacity(); + bwr.read_buffer = (long unsigned int)mIn.data(); + } else { + bwr.read_size = 0; + } + + IF_LOG_COMMANDS() { + TextOutput::Bundle _b(alog); + if (outAvail != 0) { + alog << "Sending commands to driver: " << indent; + const void* cmds = (const void*)bwr.write_buffer; + const void* end = ((const uint8_t*)cmds)+bwr.write_size; + alog << HexDump(cmds, bwr.write_size) << endl; + while (cmds < end) cmds = printCommand(alog, cmds); + alog << dedent; + } + alog << "Size of receive buffer: " << bwr.read_size + << ", needRead: " << needRead << ", doReceive: " << doReceive << endl; + } + + // Return immediately if there is nothing to do. + if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; + + bwr.write_consumed = 0; + bwr.read_consumed = 0; + status_t err; + do { + IF_LOG_COMMANDS() { + alog << "About to read/write, write size = " << mOut.dataSize() << endl; + } +#if defined(HAVE_ANDROID_OS) + if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) + err = NO_ERROR; + else + err = -errno; +#else + err = INVALID_OPERATION; +#endif + IF_LOG_COMMANDS() { + alog << "Finished read/write, write size = " << mOut.dataSize() << endl; + } + } while (err == -EINTR); + + IF_LOG_COMMANDS() { + alog << "Our err: " << (void*)err << ", write consumed: " + << bwr.write_consumed << " (of " << mOut.dataSize() + << "), read consumed: " << bwr.read_consumed << endl; + } + + if (err >= NO_ERROR) { + if (bwr.write_consumed > 0) { + if (bwr.write_consumed < (ssize_t)mOut.dataSize()) + mOut.remove(0, bwr.write_consumed); + else + mOut.setDataSize(0); + } + if (bwr.read_consumed > 0) { + mIn.setDataSize(bwr.read_consumed); + mIn.setDataPosition(0); + } + IF_LOG_COMMANDS() { + TextOutput::Bundle _b(alog); + alog << "Remaining data size: " << mOut.dataSize() << endl; + alog << "Received commands from driver: " << indent; + const void* cmds = mIn.data(); + const void* end = mIn.data() + mIn.dataSize(); + alog << HexDump(cmds, mIn.dataSize()) << endl; + while (cmds < end) cmds = printReturnCommand(alog, cmds); + alog << dedent; + } + return NO_ERROR; + } + + return err; +} + +status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, + int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) +{ + binder_transaction_data tr; + + tr.target.handle = handle; + tr.code = code; + tr.flags = binderFlags; + + const status_t err = data.errorCheck(); + if (err == NO_ERROR) { + tr.data_size = data.ipcDataSize(); + tr.data.ptr.buffer = data.ipcData(); + tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t); + tr.data.ptr.offsets = data.ipcObjects(); + } else if (statusBuffer) { + tr.flags |= TF_STATUS_CODE; + *statusBuffer = err; + tr.data_size = sizeof(status_t); + tr.data.ptr.buffer = statusBuffer; + tr.offsets_size = 0; + tr.data.ptr.offsets = NULL; + } else { + return (mLastError = err); + } + + mOut.writeInt32(cmd); + mOut.write(&tr, sizeof(tr)); + + return NO_ERROR; +} + +sp the_context_object; + +void setTheContextObject(sp obj) +{ + the_context_object = obj; +} + +status_t IPCThreadState::executeCommand(int32_t cmd) +{ + BBinder* obj; + RefBase::weakref_type* refs; + status_t result = NO_ERROR; + + switch (cmd) { + case BR_ERROR: + result = mIn.readInt32(); + break; + + case BR_OK: + break; + + case BR_ACQUIRE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + LOG_ASSERT(refs->refBase() == obj, + "BR_ACQUIRE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + obj->incStrong(mProcess.get()); + IF_LOG_REMOTEREFS() { + LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj); + obj->printRefs(); + } + mOut.writeInt32(BC_ACQUIRE_DONE); + mOut.writeInt32((int32_t)refs); + mOut.writeInt32((int32_t)obj); + break; + + case BR_RELEASE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + LOG_ASSERT(refs->refBase() == obj, + "BR_RELEASE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + IF_LOG_REMOTEREFS() { + LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj); + obj->printRefs(); + } + mPendingStrongDerefs.push(obj); + break; + + case BR_INCREFS: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + refs->incWeak(mProcess.get()); + mOut.writeInt32(BC_INCREFS_DONE); + mOut.writeInt32((int32_t)refs); + mOut.writeInt32((int32_t)obj); + break; + + case BR_DECREFS: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + // NOTE: This assertion is not valid, because the object may no + // longer exist (thus the (BBinder*)cast above resulting in a different + // memory address). + //LOG_ASSERT(refs->refBase() == obj, + // "BR_DECREFS: object %p does not match cookie %p (expected %p)", + // refs, obj, refs->refBase()); + mPendingWeakDerefs.push(refs); + break; + + case BR_ATTEMPT_ACQUIRE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + + { + const bool success = refs->attemptIncStrong(mProcess.get()); + LOG_ASSERT(success && refs->refBase() == obj, + "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + + mOut.writeInt32(BC_ACQUIRE_RESULT); + mOut.writeInt32((int32_t)success); + } + break; + + case BR_TRANSACTION: + { + binder_transaction_data tr; + result = mIn.read(&tr, sizeof(tr)); + LOG_ASSERT(result == NO_ERROR, + "Not enough command data for brTRANSACTION"); + if (result != NO_ERROR) break; + + Parcel buffer; + buffer.ipcSetDataReference( + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), freeBuffer, this); + + const pid_t origPid = mCallingPid; + const uid_t origUid = mCallingUid; + + mCallingPid = tr.sender_pid; + mCallingUid = tr.sender_euid; + + //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid); + + Parcel reply; + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BR_TRANSACTION thr " << (void*)pthread_self() + << " / obj " << tr.target.ptr << " / code " + << TypeCode(tr.code) << ": " << indent << buffer + << dedent << endl + << "Data addr = " + << reinterpret_cast(tr.data.ptr.buffer) + << ", offsets addr=" + << reinterpret_cast(tr.data.ptr.offsets) << endl; + } + if (tr.target.ptr) { + sp b((BBinder*)tr.cookie); + const status_t error = b->transact(tr.code, buffer, &reply, 0); + if (error < NO_ERROR) reply.setError(error); + + } else { + const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0); + if (error < NO_ERROR) reply.setError(error); + } + + //LOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n", + // mCallingPid, origPid, origUid); + + if ((tr.flags & TF_ONE_WAY) == 0) { + LOG_ONEWAY("Sending reply to %d!", mCallingPid); + sendReply(reply, 0); + } else { + LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); + } + + mCallingPid = origPid; + mCallingUid = origUid; + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj " + << tr.target.ptr << ": " << indent << reply << dedent << endl; + } + + } + break; + + case BR_DEAD_BINDER: + { + BpBinder *proxy = (BpBinder*)mIn.readInt32(); + proxy->sendObituary(); + mOut.writeInt32(BC_DEAD_BINDER_DONE); + mOut.writeInt32((int32_t)proxy); + } break; + + case BR_CLEAR_DEATH_NOTIFICATION_DONE: + { + BpBinder *proxy = (BpBinder*)mIn.readInt32(); + proxy->getWeakRefs()->decWeak(proxy); + } break; + + case BR_FINISHED: + result = TIMED_OUT; + break; + + case BR_NOOP: + break; + + case BR_SPAWN_LOOPER: + mProcess->spawnPooledThread(false); + break; + + default: + printf("*** BAD COMMAND %d received from Binder driver\n", cmd); + result = UNKNOWN_ERROR; + break; + } + + if (result != NO_ERROR) { + mLastError = result; + } + + return result; +} + +void IPCThreadState::threadDestructor(void *st) +{ + IPCThreadState* const self = static_cast(st); + if (self) { + self->flushCommands(); +#if defined(HAVE_ANDROID_OS) + ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); +#endif + delete self; + } +} + + +void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie) +{ + //LOGI("Freeing parcel %p", &parcel); + IF_LOG_COMMANDS() { + alog << "Writing BC_FREE_BUFFER for " << data << endl; + } + LOG_ASSERT(data != NULL, "Called with NULL data"); + if (parcel != NULL) parcel->closeFileDescriptors(); + IPCThreadState* state = self(); + state->mOut.writeInt32(BC_FREE_BUFFER); + state->mOut.writeInt32((int32_t)data); +} + +}; // namespace android diff --git a/libs/utils/IPermissionController.cpp b/libs/utils/IPermissionController.cpp new file mode 100644 index 000000000..f01d38fd1 --- /dev/null +++ b/libs/utils/IPermissionController.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005 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. + */ + +#define LOG_TAG "PermissionController" + +#include + +#include +#include +#include +#include + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class BpPermissionController : public BpInterface +{ +public: + BpPermissionController(const sp& impl) + : BpInterface(impl) + { + } + + virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) + { + Parcel data, reply; + data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); + data.writeString16(permission); + data.writeInt32(pid); + data.writeInt32(uid); + remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply); + // fail on exception + if (reply.readInt32() != 0) return 0; + return reply.readInt32() != 0; + } +}; + +IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnPermissionController::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + //printf("PermissionController received: "); data.print(); + switch(code) { + case CHECK_PERMISSION_TRANSACTION: { + CHECK_INTERFACE(IPermissionController, data, reply); + String16 permission = data.readString16(); + int32_t pid = data.readInt32(); + int32_t uid = data.readInt32(); + bool res = checkPermission(permission, pid, uid); + // write exception + reply->writeInt32(0); + reply->writeInt32(res ? 1 : 0); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/libs/utils/IServiceManager.cpp b/libs/utils/IServiceManager.cpp new file mode 100644 index 000000000..9beeaddde --- /dev/null +++ b/libs/utils/IServiceManager.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2005 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. + */ + +#define LOG_TAG "ServiceManager" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace android { + +sp defaultServiceManager() +{ + if (gDefaultServiceManager != NULL) return gDefaultServiceManager; + + { + AutoMutex _l(gDefaultServiceManagerLock); + if (gDefaultServiceManager == NULL) { + gDefaultServiceManager = interface_cast( + ProcessState::self()->getContextObject(NULL)); + } + } + + return gDefaultServiceManager; +} + +bool checkCallingPermission(const String16& permission) +{ + return checkCallingPermission(permission, NULL, NULL); +} + +static String16 _permission("permission"); + +bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid) +{ + IPCThreadState* ipcState = IPCThreadState::self(); + int32_t pid = ipcState->getCallingPid(); + int32_t uid = ipcState->getCallingUid(); + if (outPid) *outPid = pid; + if (outUid) *outUid= uid; + + sp pc; + gDefaultServiceManagerLock.lock(); + pc = gPermissionController; + gDefaultServiceManagerLock.unlock(); + + int64_t startTime = 0; + + while (true) { + if (pc != NULL) { + bool res = pc->checkPermission(permission, pid, uid); + if (res) { + if (startTime != 0) { + LOGI("Check passed after %d seconds for %s from uid=%d pid=%d", + (int)((uptimeMillis()-startTime)/1000), + String8(permission).string(), uid, pid); + } + return res; + } + + // Is this a permission failure, or did the controller go away? + if (pc->asBinder()->isBinderAlive()) { + LOGW("Permission failure: %s from uid=%d pid=%d", + String8(permission).string(), uid, pid); + return false; + } + + // Object is dead! + gDefaultServiceManagerLock.lock(); + if (gPermissionController == pc) { + gPermissionController = NULL; + } + gDefaultServiceManagerLock.unlock(); + } + + // Need to retrieve the permission controller. + sp binder = defaultServiceManager()->checkService(_permission); + if (binder == NULL) { + // Wait for the permission controller to come back... + if (startTime == 0) { + startTime = uptimeMillis(); + LOGI("Waiting to check permission %s from uid=%d pid=%d", + String8(permission).string(), uid, pid); + } + sleep(1); + } else { + pc = interface_cast(binder); + // Install the new permission controller, and try again. + gDefaultServiceManagerLock.lock(); + gPermissionController = pc; + gDefaultServiceManagerLock.unlock(); + } + } +} + +// ---------------------------------------------------------------------- + +class BpServiceManager : public BpInterface +{ +public: + BpServiceManager(const sp& impl) + : BpInterface(impl) + { + } + + virtual sp getService(const String16& name) const + { + unsigned n; + for (n = 0; n < 5; n++){ + sp svc = checkService(name); + if (svc != NULL) return svc; + LOGI("Waiting for sevice %s...\n", String8(name).string()); + sleep(1); + } + return NULL; + } + + virtual sp checkService( const String16& name) const + { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeString16(name); + remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); + return reply.readStrongBinder(); + } + + virtual status_t addService(const String16& name, const sp& service) + { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeString16(name); + data.writeStrongBinder(service); + status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); + return err == NO_ERROR ? reply.readInt32() : err; + } + + virtual Vector listServices() + { + Vector res; + int n = 0; + + for (;;) { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeInt32(n++); + status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply); + if (err != NO_ERROR) + break; + res.add(reply.readString16()); + } + return res; + } +}; + +IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnServiceManager::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + //printf("ServiceManager received: "); data.print(); + switch(code) { + case GET_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = const_cast(this)->getService(which); + reply->writeStrongBinder(b); + return NO_ERROR; + } break; + case CHECK_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = const_cast(this)->checkService(which); + reply->writeStrongBinder(b); + return NO_ERROR; + } break; + case ADD_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = data.readStrongBinder(); + status_t err = addService(which, b); + reply->writeInt32(err); + return NO_ERROR; + } break; + case LIST_SERVICES_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + Vector list = listServices(); + const size_t N = list.size(); + reply->writeInt32(N); + for (size_t i=0; iwriteString16(list[i]); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp new file mode 100644 index 000000000..39a0a6839 --- /dev/null +++ b/libs/utils/InetAddress.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Internet address class. +// +#ifdef HAVE_WINSOCK +# include +#else +# include +# include +# include +//# include +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace android; + + +/* + * =========================================================================== + * InetAddress + * =========================================================================== + */ + +// lock for the next couple of functions; could tuck into InetAddress +static Mutex* gGHBNLock; + +/* + * Lock/unlock access to the hostent struct returned by gethostbyname(). + */ +static inline void lock_gethostbyname(void) +{ + if (gGHBNLock == NULL) + gGHBNLock = new Mutex; + gGHBNLock->lock(); +} +static inline void unlock_gethostbyname(void) +{ + assert(gGHBNLock != NULL); + gGHBNLock->unlock(); +} + + +/* + * Constructor -- just init members. This is private so that callers + * are required to use getByName(). + */ +InetAddress::InetAddress(void) + : mAddress(NULL), mLength(-1), mName(NULL) +{ +} + +/* + * Destructor -- free address storage. + */ +InetAddress::~InetAddress(void) +{ + delete[] (char*) mAddress; + delete[] mName; +} + +/* + * Copy constructor. + */ +InetAddress::InetAddress(const InetAddress& orig) +{ + *this = orig; // use assignment code +} + +/* + * Assignment operator. + */ +InetAddress& InetAddress::operator=(const InetAddress& addr) +{ + // handle self-assignment + if (this == &addr) + return *this; + // copy mLength and mAddress + mLength = addr.mLength; + if (mLength > 0) { + mAddress = new char[mLength]; + memcpy(mAddress, addr.mAddress, mLength); + LOG(LOG_DEBUG, "socket", + "HEY: copied %d bytes in assignment operator\n", mLength); + } else { + mAddress = NULL; + } + // copy mName + mName = new char[strlen(addr.mName)+1]; + strcpy(mName, addr.mName); + + return *this; +} + +/* + * Create a new object from a name or a dotted-number IP notation. + * + * Returns NULL on failure. + */ +InetAddress* +InetAddress::getByName(const char* host) +{ + InetAddress* newAddr = NULL; + struct sockaddr_in addr; + struct hostent* he; + DurationTimer hostTimer, lockTimer; + + // gethostbyname() isn't reentrant, so we need to lock things until + // we can copy the data out. + lockTimer.start(); + lock_gethostbyname(); + hostTimer.start(); + + he = gethostbyname(host); + if (he == NULL) { + LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host); + unlock_gethostbyname(); + return NULL; + } + + memcpy(&addr.sin_addr, he->h_addr, he->h_length); + addr.sin_family = he->h_addrtype; + addr.sin_port = 0; + + // got it, unlock us + hostTimer.stop(); + he = NULL; + unlock_gethostbyname(); + + lockTimer.stop(); + if ((long) lockTimer.durationUsecs() > 100000) { + long lockTime = (long) lockTimer.durationUsecs(); + long hostTime = (long) hostTimer.durationUsecs(); + LOG(LOG_DEBUG, "socket", + "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n", + host, lockTime / 1000000.0, hostTime / 1000000.0, + (lockTime - hostTime) / 1000000.0); + } + + // Alloc storage and copy it over. + newAddr = new InetAddress(); + if (newAddr == NULL) + return NULL; + + newAddr->mLength = sizeof(struct sockaddr_in); + newAddr->mAddress = new char[sizeof(struct sockaddr_in)]; + if (newAddr->mAddress == NULL) { + delete newAddr; + return NULL; + } + memcpy(newAddr->mAddress, &addr, newAddr->mLength); + + // Keep this for debug messages. + newAddr->mName = new char[strlen(host)+1]; + if (newAddr->mName == NULL) { + delete newAddr; + return NULL; + } + strcpy(newAddr->mName, host); + + return newAddr; +} + + +/* + * =========================================================================== + * InetSocketAddress + * =========================================================================== + */ + +/* + * Create an address with the host wildcard (INADDR_ANY). + */ +bool InetSocketAddress::create(int port) +{ + assert(mAddress == NULL); + + mAddress = InetAddress::getByName("0.0.0.0"); + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + +/* + * Create address with host and port specified. + */ +bool InetSocketAddress::create(const InetAddress* addr, int port) +{ + assert(mAddress == NULL); + + mAddress = new InetAddress(*addr); // make a copy + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + +/* + * Create address with host and port specified. + */ +bool InetSocketAddress::create(const char* host, int port) +{ + assert(mAddress == NULL); + + mAddress = InetAddress::getByName(host); + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp new file mode 100644 index 000000000..55c1b99af --- /dev/null +++ b/libs/utils/LogSocket.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2008 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. + */ + + +#ifndef HAVE_WINSOCK +//#define SOCKETLOG +#endif + +#ifdef SOCKETLOG + +#define LOG_TAG "SOCKETLOG" + +#include +#include +#include "utils/LogSocket.h" +#include "utils/logger.h" +#include "cutils/hashmap.h" + +// defined in //device/data/etc/event-log-tags +#define SOCKET_CLOSE_LOG 51000 + +static Hashmap* statsMap = NULL; + +#define LOG_LIST_NUMBER 5 + +typedef struct SocketStats { + int fd; + unsigned int send; + unsigned int recv; + unsigned int ip; + unsigned short port; + short reason; +}SocketStats; + +SocketStats *get_socket_stats(int fd) { + if (statsMap == NULL) { + statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals); + } + + SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); + if (s == NULL) { + // LOGD("create SocketStats for fd %d", fd); + s = (SocketStats*) malloc(sizeof(SocketStats)); + memset(s, 0, sizeof(SocketStats)); + s->fd = fd; + hashmapPut(statsMap, &s->fd, s); + } + return s; +} + +void log_socket_connect(int fd, unsigned int ip, unsigned short port) { + // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port); + SocketStats *s = get_socket_stats(fd); + s->ip = ip; + s->port = port; +} + +void add_send_stats(int fd, int send) { + if (send <=0) { + LOGE("add_send_stats send %d", send); + return; + } + SocketStats *s = get_socket_stats(fd); + s->send += send; + // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port); +} + +void add_recv_stats(int fd, int recv) { + if (recv <=0) { + LOGE("add_recv_stats recv %d", recv); + return; + } + SocketStats *s = get_socket_stats(fd); + s->recv += recv; + // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port); +} + +char* put_int(char* buf, int value) { + *buf = EVENT_TYPE_INT; + buf++; + memcpy(buf, &value, sizeof(int)); + return buf + sizeof(int); +} + +void log_socket_close(int fd, short reason) { + if (statsMap) { + SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); + if (s != NULL) { + if (s->send != 0 || s->recv != 0) { + s->reason = reason; + // 5 int + list type need 2 bytes + char buf[LOG_LIST_NUMBER * 5 + 2]; + buf[0] = EVENT_TYPE_LIST; + buf[1] = LOG_LIST_NUMBER; + char* writePos = buf + 2; + writePos = put_int(writePos, s->send); + writePos = put_int(writePos, s->recv); + writePos = put_int(writePos, s->ip); + writePos = put_int(writePos, s->port); + writePos = put_int(writePos, s->reason); + + android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf)); + // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason); + } + hashmapRemove(statsMap, &s->fd); + free(s); + } + } +} + +#else +void add_send_stats(int fd, int send) {} +void add_recv_stats(int fd, int recv) {} +void log_socket_close(int fd, short reason) {} +void log_socket_connect(int fd, unsigned int ip, unsigned short port) {} +#endif diff --git a/libs/utils/MODULE_LICENSE_APACHE2 b/libs/utils/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/libs/utils/MemoryBase.cpp b/libs/utils/MemoryBase.cpp new file mode 100644 index 000000000..f25e11c6b --- /dev/null +++ b/libs/utils/MemoryBase.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 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 + + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryBase::MemoryBase(const sp& heap, + ssize_t offset, size_t size) + : mSize(size), mOffset(offset), mHeap(heap) +{ +} + +sp MemoryBase::getMemory(ssize_t* offset, size_t* size) const +{ + if (offset) *offset = mOffset; + if (size) *size = mSize; + return mHeap; +} + +MemoryBase::~MemoryBase() +{ +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/MemoryDealer.cpp b/libs/utils/MemoryDealer.cpp new file mode 100644 index 000000000..cf8201b85 --- /dev/null +++ b/libs/utils/MemoryDealer.cpp @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "MemoryDealer" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { + + +// ---------------------------------------------------------------------------- + +class SimpleMemory : public MemoryBase { +public: + SimpleMemory(const sp& heap, ssize_t offset, size_t size); + virtual ~SimpleMemory(); +}; + + +// ---------------------------------------------------------------------------- + +MemoryDealer::Allocation::Allocation( + const sp& dealer, ssize_t offset, size_t size, + const sp& memory) + : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory) +{ +} + +MemoryDealer::Allocation::~Allocation() +{ + if (mSize) { + /* NOTE: it's VERY important to not free allocations of size 0 because + * they're special as they don't have any record in the allocator + * and could alias some real allocation (their offset is zero). */ + mDealer->deallocate(mOffset); + } +} + +sp MemoryDealer::Allocation::getMemory( + ssize_t* offset, size_t* size) const +{ + return mMemory->getMemory(offset, size); +} + +// ---------------------------------------------------------------------------- + +MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name) + : mHeap(new SharedHeap(size, flags, name)), + mAllocator(new SimpleBestFitAllocator(size)) +{ +} + +MemoryDealer::MemoryDealer(const sp& heap) + : mHeap(heap), + mAllocator(new SimpleBestFitAllocator(heap->virtualSize())) +{ +} + +MemoryDealer::MemoryDealer( const sp& heap, + const sp& allocator) + : mHeap(heap), mAllocator(allocator) +{ +} + +MemoryDealer::~MemoryDealer() +{ +} + +sp MemoryDealer::allocate(size_t size, uint32_t flags) +{ + sp memory; + const ssize_t offset = allocator()->allocate(size, flags); + if (offset >= 0) { + sp new_memory = heap()->mapMemory(offset, size); + if (new_memory != 0) { + memory = new Allocation(this, offset, size, new_memory); + } else { + LOGE("couldn't map [%8x, %d]", offset, size); + if (size) { + /* NOTE: it's VERY important to not free allocations of size 0 + * because they're special as they don't have any record in the + * allocator and could alias some real allocation + * (their offset is zero). */ + allocator()->deallocate(offset); + } + } + } + return memory; +} + +void MemoryDealer::deallocate(size_t offset) +{ + allocator()->deallocate(offset); +} + +void MemoryDealer::dump(const char* what, uint32_t flags) const +{ + allocator()->dump(what, flags); +} + +const sp& MemoryDealer::heap() const { + return mHeap; +} + +const sp& MemoryDealer::allocator() const { + return mAllocator; +} + +// ---------------------------------------------------------------------------- + +// align all the memory blocks on a cache-line boundary +const int SimpleBestFitAllocator::kMemoryAlign = 32; + +SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) +{ + size_t pagesize = getpagesize(); + mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); + + chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); + mList.insertHead(node); +} + +SimpleBestFitAllocator::~SimpleBestFitAllocator() +{ + while(!mList.isEmpty()) { + delete mList.remove(mList.head()); + } +} + +size_t SimpleBestFitAllocator::size() const +{ + return mHeapSize; +} + +size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) +{ + Mutex::Autolock _l(mLock); + ssize_t offset = alloc(size, flags); + return offset; +} + +status_t SimpleBestFitAllocator::deallocate(size_t offset) +{ + Mutex::Autolock _l(mLock); + chunk_t const * const freed = dealloc(offset); + if (freed) { + return NO_ERROR; + } + return NAME_NOT_FOUND; +} + +ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) +{ + if (size == 0) { + return 0; + } + size = (size + kMemoryAlign-1) / kMemoryAlign; + chunk_t* free_chunk = 0; + chunk_t* cur = mList.head(); + + size_t pagesize = getpagesize(); + while (cur) { + int extra = 0; + if (flags & PAGE_ALIGNED) + extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; + + // best fit + if (cur->free && (cur->size >= (size+extra))) { + if ((!free_chunk) || (cur->size < free_chunk->size)) { + free_chunk = cur; + } + if (cur->size == size) { + break; + } + } + cur = cur->next; + } + + if (free_chunk) { + const size_t free_size = free_chunk->size; + free_chunk->free = 0; + free_chunk->size = size; + if (free_size > size) { + int extra = 0; + if (flags & PAGE_ALIGNED) + extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; + if (extra) { + chunk_t* split = new chunk_t(free_chunk->start, extra); + free_chunk->start += extra; + mList.insertBefore(free_chunk, split); + } + + LOGE_IF((flags&PAGE_ALIGNED) && + ((free_chunk->start*kMemoryAlign)&(pagesize-1)), + "PAGE_ALIGNED requested, but page is not aligned!!!"); + + const ssize_t tail_free = free_size - (size+extra); + if (tail_free > 0) { + chunk_t* split = new chunk_t( + free_chunk->start + free_chunk->size, tail_free); + mList.insertAfter(free_chunk, split); + } + } + return (free_chunk->start)*kMemoryAlign; + } + return NO_MEMORY; +} + +SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) +{ + start = start / kMemoryAlign; + chunk_t* cur = mList.head(); + while (cur) { + if (cur->start == start) { + LOG_FATAL_IF(cur->free, + "block at offset 0x%08lX of size 0x%08lX already freed", + cur->start*kMemoryAlign, cur->size*kMemoryAlign); + + // merge freed blocks together + chunk_t* freed = cur; + cur->free = 1; + do { + chunk_t* const p = cur->prev; + chunk_t* const n = cur->next; + if (p && (p->free || !cur->size)) { + freed = p; + p->size += cur->size; + mList.remove(cur); + delete cur; + } + cur = n; + } while (cur && cur->free); + + #ifndef NDEBUG + if (!freed->free) { + dump_l("dealloc (!freed->free)"); + } + #endif + LOG_FATAL_IF(!freed->free, + "freed block at offset 0x%08lX of size 0x%08lX is not free!", + freed->start * kMemoryAlign, freed->size * kMemoryAlign); + + return freed; + } + cur = cur->next; + } + return 0; +} + +void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const +{ + Mutex::Autolock _l(mLock); + dump_l(what, flags); +} + +void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const +{ + String8 result; + dump_l(result, what, flags); + LOGD("%s", result.string()); +} + +void SimpleBestFitAllocator::dump(String8& result, + const char* what, uint32_t flags) const +{ + Mutex::Autolock _l(mLock); + dump_l(result, what, flags); +} + +void SimpleBestFitAllocator::dump_l(String8& result, + const char* what, uint32_t flags) const +{ + size_t size = 0; + int32_t i = 0; + chunk_t const* cur = mList.head(); + + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, " %s (%p, size=%u)\n", + what, this, (unsigned int)mHeapSize); + + result.append(buffer); + + while (cur) { + const char* errs[] = {"", "| link bogus NP", + "| link bogus PN", "| link bogus NP+PN" }; + int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0; + int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0; + + snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n", + i, int(cur), int(cur->start*kMemoryAlign), + int(cur->size*kMemoryAlign), + int(cur->free) ? "F" : "A", + errs[np|pn]); + + result.append(buffer); + + if (!cur->free) + size += cur->size*kMemoryAlign; + + i++; + cur = cur->next; + } + snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024)); + result.append(buffer); +} + +// ---------------------------------------------------------------------------- + + +SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name) + : MemoryHeapBase(size, flags, name) +{ +} + +SharedHeap::~SharedHeap() +{ +} + +sp SharedHeap::mapMemory(size_t offset, size_t size) +{ + return new SimpleMemory(this, offset, size); +} + + +SimpleMemory::SimpleMemory(const sp& heap, + ssize_t offset, size_t size) + : MemoryBase(heap, offset, size) +{ +#ifndef NDEBUG + void* const start_ptr = (void*)(intptr_t(heap->base()) + offset); + memset(start_ptr, 0xda, size); +#endif +} + +SimpleMemory::~SimpleMemory() +{ + size_t freedOffset = getOffset(); + size_t freedSize = getSize(); + + // keep the size to unmap in excess + size_t pagesize = getpagesize(); + size_t start = freedOffset; + size_t end = start + freedSize; + start &= ~(pagesize-1); + end = (end + pagesize-1) & ~(pagesize-1); + + // give back to the kernel the pages we don't need + size_t free_start = freedOffset; + size_t free_end = free_start + freedSize; + if (start < free_start) + start = free_start; + if (end > free_end) + end = free_end; + start = (start + pagesize-1) & ~(pagesize-1); + end &= ~(pagesize-1); + + if (start < end) { + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); + size_t size = end-start; + +#ifndef NDEBUG + memset(start_ptr, 0xdf, size); +#endif + + // MADV_REMOVE is not defined on Dapper based Goobuntu +#ifdef MADV_REMOVE + if (size) { + int err = madvise(start_ptr, size, MADV_REMOVE); + LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", + start_ptr, size, err<0 ? strerror(errno) : "Ok"); + } +#endif + } +} + +}; // namespace android diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/utils/MemoryHeapBase.cpp new file mode 100644 index 000000000..825172819 --- /dev/null +++ b/libs/utils/MemoryHeapBase.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2008 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. + */ + +#define LOG_TAG "MemoryHeapBase" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#if HAVE_ANDROID_OS +#include +#endif + + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryHeapBase::MemoryHeapBase() + : mFD(-1), mSize(0), mBase(MAP_FAILED), + mDevice(NULL), mNeedUnmap(false) +{ +} + +MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size); + LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno)); + if (fd >= 0) { + if (mapfd(fd, size) == NO_ERROR) { + if (flags & READ_ONLY) { + ashmem_set_prot_region(fd, PROT_READ); + } + } + } +} + +MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + int fd = open(device, O_RDWR); + LOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno)); + if (fd >= 0) { + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + if (mapfd(fd, size) == NO_ERROR) { + mDevice = device; + } + } +} + +MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + mapfd(dup(fd), size); +} + +status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) +{ + if (mFD != -1) { + return INVALID_OPERATION; + } + mFD = fd; + mBase = base; + mSize = size; + mFlags = flags; + mDevice = device; + return NO_ERROR; +} + +status_t MemoryHeapBase::mapfd(int fd, size_t size) +{ + if (size == 0) { + // try to figure out the size automatically +#if HAVE_ANDROID_OS + // first try the PMEM ioctl + pmem_region reg; + int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®); + if (err == 0) + size = reg.len; +#endif + if (size == 0) { // try fstat + struct stat sb; + if (fstat(fd, &sb) == 0) + size = sb.st_size; + } + // if it didn't work, let mmap() fail. + } + + if ((mFlags & DONT_MAP_LOCALLY) == 0) { + void* base = (uint8_t*)mmap(0, size, + PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (base == MAP_FAILED) { + LOGE("mmap(fd=%d, size=%u) failed (%s)", + fd, uint32_t(size), strerror(errno)); + close(fd); + return -errno; + } + //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); + mBase = base; + mNeedUnmap = true; + } else { + mBase = 0; // not MAP_FAILED + mNeedUnmap = false; + } + mFD = fd; + mSize = size; + return NO_ERROR; +} + +MemoryHeapBase::~MemoryHeapBase() +{ + dispose(); +} + +void MemoryHeapBase::dispose() +{ + int fd = android_atomic_or(-1, &mFD); + if (fd >= 0) { + if (mNeedUnmap) { + //LOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize); + munmap(mBase, mSize); + } + mBase = 0; + mSize = 0; + close(fd); + } +} + +int MemoryHeapBase::getHeapID() const { + return mFD; +} + +void* MemoryHeapBase::getBase() const { + return mBase; +} + +size_t MemoryHeapBase::getSize() const { + return mSize; +} + +uint32_t MemoryHeapBase::getFlags() const { + return mFlags; +} + +const char* MemoryHeapBase::getDevice() const { + return mDevice; +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/utils/MemoryHeapPmem.cpp new file mode 100644 index 000000000..eba2b3055 --- /dev/null +++ b/libs/utils/MemoryHeapPmem.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2008 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. + */ + +#define LOG_TAG "MemoryHeapPmem" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#if HAVE_ANDROID_OS +#include +#endif + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp& heap) + : BnMemory(), mClientHeap(heap) +{ +} + +MemoryHeapPmem::MemoryPmem::~MemoryPmem() { + if (mClientHeap != NULL) { + mClientHeap->remove(this); + } +} + +// --------------------------------------------------------------------------- + +class SubRegionMemory : public MemoryHeapPmem::MemoryPmem { +public: + SubRegionMemory(const sp& heap, ssize_t offset, size_t size); + virtual ~SubRegionMemory(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; +private: + friend class MemoryHeapPmem; + void revoke(); + size_t mSize; + ssize_t mOffset; +}; + +SubRegionMemory::SubRegionMemory(const sp& heap, + ssize_t offset, size_t size) + : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset) +{ +#ifndef NDEBUG + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset); + memset(start_ptr, 0xda, size); +#endif + +#if HAVE_ANDROID_OS + if (size > 0) { + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = heap->heapID(); + struct pmem_region sub = { offset, size }; + int err = ioctl(our_fd, PMEM_MAP, &sub); + LOGE_IF(err<0, "PMEM_MAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); +} +#endif +} + +sp SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const +{ + if (offset) *offset = mOffset; + if (size) *size = mSize; + return getHeap(); +} + +SubRegionMemory::~SubRegionMemory() +{ + revoke(); +} + + +void SubRegionMemory::revoke() +{ + // NOTE: revoke() doesn't need to be protected by a lock because it + // can only be called from MemoryHeapPmem::revoke(), which means + // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(), + // which means MemoryHeapPmem::revoke() wouldn't have been able to + // promote() it. + +#if HAVE_ANDROID_OS + if (mSize != NULL) { + const sp& heap(getHeap()); + int our_fd = heap->heapID(); + struct pmem_region sub; + sub.offset = mOffset; + sub.len = mSize; + int err = ioctl(our_fd, PMEM_UNMAP, &sub); + LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + mSize = 0; + } +#endif +} + +// --------------------------------------------------------------------------- + +MemoryHeapPmem::MemoryHeapPmem(const sp& pmemHeap, + uint32_t flags) + : HeapInterface(), MemoryHeapBase() +{ + char const * const device = pmemHeap->getDevice(); +#if HAVE_ANDROID_OS + if (device) { + int fd = open(device, O_RDWR); + LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno)); + if (fd >= 0) { + int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID()); + if (err < 0) { + LOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d", + strerror(errno), fd, pmemHeap->heapID()); + close(fd); + } else { + // everything went well... + mParentHeap = pmemHeap; + MemoryHeapBase::init(fd, + pmemHeap->getBase(), + pmemHeap->getSize(), + pmemHeap->getFlags() | flags, + device); + } + } + } +#else + mParentHeap = pmemHeap; + MemoryHeapBase::init( + dup(pmemHeap->heapID()), + pmemHeap->getBase(), + pmemHeap->getSize(), + pmemHeap->getFlags() | flags, + device); +#endif +} + +MemoryHeapPmem::~MemoryHeapPmem() +{ +} + +sp MemoryHeapPmem::mapMemory(size_t offset, size_t size) +{ + sp memory = createMemory(offset, size); + if (memory != 0) { + Mutex::Autolock _l(mLock); + mAllocations.add(memory); + } + return memory; +} + +sp MemoryHeapPmem::createMemory( + size_t offset, size_t size) +{ + sp memory; + if (heapID() > 0) + memory = new SubRegionMemory(this, offset, size); + return memory; +} + +status_t MemoryHeapPmem::slap() +{ +#if HAVE_ANDROID_OS + size_t size = getSize(); + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = getHeapID(); + struct pmem_region sub = { 0, size }; + int err = ioctl(our_fd, PMEM_MAP, &sub); + LOGE_IF(err<0, "PMEM_MAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + return -errno; +#else + return NO_ERROR; +#endif +} + +status_t MemoryHeapPmem::unslap() +{ +#if HAVE_ANDROID_OS + size_t size = getSize(); + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = getHeapID(); + struct pmem_region sub = { 0, size }; + int err = ioctl(our_fd, PMEM_UNMAP, &sub); + LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + return -errno; +#else + return NO_ERROR; +#endif +} + +void MemoryHeapPmem::revoke() +{ + SortedVector< wp > allocations; + + { // scope for lock + Mutex::Autolock _l(mLock); + allocations = mAllocations; + } + + ssize_t count = allocations.size(); + for (ssize_t i=0 ; i memory(allocations[i].promote()); + if (memory != 0) + memory->revoke(); + } +} + +void MemoryHeapPmem::remove(const wp& memory) +{ + Mutex::Autolock _l(mLock); + mAllocations.remove(memory); +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/NOTICE b/libs/utils/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/libs/utils/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp new file mode 100644 index 000000000..0f4b64730 --- /dev/null +++ b/libs/utils/Parcel.cpp @@ -0,0 +1,1377 @@ +/* + * Copyright (C) 2005 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. + */ + +#define LOG_TAG "Parcel" +//#define LOG_NDEBUG 0 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifndef INT32_MAX +#define INT32_MAX ((int32_t)(2147483647)) +#endif + +#define LOG_REFS(...) +//#define LOG_REFS(...) LOG(LOG_DEBUG, "Parcel", __VA_ARGS__) + +// --------------------------------------------------------------------------- + +#define PAD_SIZE(s) (((s)+3)&~3) + +// XXX This can be made public if we want to provide +// support for typed data. +struct small_flat_data +{ + uint32_t type; + uint32_t data; +}; + +namespace android { + +void acquire_object(const sp& proc, + const flat_binder_object& obj, const void* who) +{ + switch (obj.type) { + case BINDER_TYPE_BINDER: + if (obj.binder) { + LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie); + static_cast(obj.cookie)->incStrong(who); + } + return; + case BINDER_TYPE_WEAK_BINDER: + if (obj.binder) + static_cast(obj.binder)->incWeak(who); + return; + case BINDER_TYPE_HANDLE: { + const sp b = proc->getStrongProxyForHandle(obj.handle); + if (b != NULL) { + LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get()); + b->incStrong(who); + } + return; + } + case BINDER_TYPE_WEAK_HANDLE: { + const wp b = proc->getWeakProxyForHandle(obj.handle); + if (b != NULL) b.get_refs()->incWeak(who); + return; + } + case BINDER_TYPE_FD: { + // intentionally blank -- nothing to do to acquire this, but we do + // recognize it as a legitimate object type. + return; + } + } + + LOGD("Invalid object type 0x%08lx", obj.type); +} + +void release_object(const sp& proc, + const flat_binder_object& obj, const void* who) +{ + switch (obj.type) { + case BINDER_TYPE_BINDER: + if (obj.binder) { + LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie); + static_cast(obj.cookie)->decStrong(who); + } + return; + case BINDER_TYPE_WEAK_BINDER: + if (obj.binder) + static_cast(obj.binder)->decWeak(who); + return; + case BINDER_TYPE_HANDLE: { + const sp b = proc->getStrongProxyForHandle(obj.handle); + if (b != NULL) { + LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get()); + b->decStrong(who); + } + return; + } + case BINDER_TYPE_WEAK_HANDLE: { + const wp b = proc->getWeakProxyForHandle(obj.handle); + if (b != NULL) b.get_refs()->decWeak(who); + return; + } + case BINDER_TYPE_FD: { + if (obj.cookie != (void*)0) close(obj.handle); + return; + } + } + + LOGE("Invalid object type 0x%08lx", obj.type); +} + +inline static status_t finish_flatten_binder( + const sp& binder, const flat_binder_object& flat, Parcel* out) +{ + return out->writeObject(flat, false); +} + +status_t flatten_binder(const sp& proc, + const sp& binder, Parcel* out) +{ + flat_binder_object obj; + + obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + if (binder != NULL) { + IBinder *local = binder->localBinder(); + if (!local) { + BpBinder *proxy = binder->remoteBinder(); + if (proxy == NULL) { + LOGE("null proxy"); + } + const int32_t handle = proxy ? proxy->handle() : 0; + obj.type = BINDER_TYPE_HANDLE; + obj.handle = handle; + obj.cookie = NULL; + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = local->getWeakRefs(); + obj.cookie = local; + } + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + } + + return finish_flatten_binder(binder, obj, out); +} + +status_t flatten_binder(const sp& proc, + const wp& binder, Parcel* out) +{ + flat_binder_object obj; + + obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + if (binder != NULL) { + sp real = binder.promote(); + if (real != NULL) { + IBinder *local = real->localBinder(); + if (!local) { + BpBinder *proxy = real->remoteBinder(); + if (proxy == NULL) { + LOGE("null proxy"); + } + const int32_t handle = proxy ? proxy->handle() : 0; + obj.type = BINDER_TYPE_WEAK_HANDLE; + obj.handle = handle; + obj.cookie = NULL; + } else { + obj.type = BINDER_TYPE_WEAK_BINDER; + obj.binder = binder.get_refs(); + obj.cookie = binder.unsafe_get(); + } + return finish_flatten_binder(real, obj, out); + } + + // XXX How to deal? In order to flatten the given binder, + // we need to probe it for information, which requires a primary + // reference... but we don't have one. + // + // The OpenBinder implementation uses a dynamic_cast<> here, + // but we can't do that with the different reference counting + // implementation we are using. + LOGE("Unable to unflatten Binder weak reference!"); + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + return finish_flatten_binder(NULL, obj, out); + + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + return finish_flatten_binder(NULL, obj, out); + } +} + +inline static status_t finish_unflatten_binder( + BpBinder* proxy, const flat_binder_object& flat, const Parcel& in) +{ + return NO_ERROR; +} + +status_t unflatten_binder(const sp& proc, + const Parcel& in, sp* out) +{ + const flat_binder_object* flat = in.readObject(false); + + if (flat) { + switch (flat->type) { + case BINDER_TYPE_BINDER: + *out = static_cast(flat->cookie); + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_HANDLE: + *out = proc->getStrongProxyForHandle(flat->handle); + return finish_unflatten_binder( + static_cast(out->get()), *flat, in); + } + } + return BAD_TYPE; +} + +status_t unflatten_binder(const sp& proc, + const Parcel& in, wp* out) +{ + const flat_binder_object* flat = in.readObject(false); + + if (flat) { + switch (flat->type) { + case BINDER_TYPE_BINDER: + *out = static_cast(flat->cookie); + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_WEAK_BINDER: + if (flat->binder != NULL) { + out->set_object_and_refs( + static_cast(flat->cookie), + static_cast(flat->binder)); + } else { + *out = NULL; + } + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_HANDLE: + case BINDER_TYPE_WEAK_HANDLE: + *out = proc->getWeakProxyForHandle(flat->handle); + return finish_unflatten_binder( + static_cast(out->unsafe_get()), *flat, in); + } + } + return BAD_TYPE; +} + +// --------------------------------------------------------------------------- + +Parcel::Parcel() +{ + initState(); +} + +Parcel::~Parcel() +{ + freeDataNoInit(); +} + +const uint8_t* Parcel::data() const +{ + return mData; +} + +size_t Parcel::dataSize() const +{ + return (mDataSize > mDataPos ? mDataSize : mDataPos); +} + +size_t Parcel::dataAvail() const +{ + // TODO: decide what to do about the possibility that this can + // report an available-data size that exceeds a Java int's max + // positive value, causing havoc. Fortunately this will only + // happen if someone constructs a Parcel containing more than two + // gigabytes of data, which on typical phone hardware is simply + // not possible. + return dataSize() - dataPosition(); +} + +size_t Parcel::dataPosition() const +{ + return mDataPos; +} + +size_t Parcel::dataCapacity() const +{ + return mDataCapacity; +} + +status_t Parcel::setDataSize(size_t size) +{ + status_t err; + err = continueWrite(size); + if (err == NO_ERROR) { + mDataSize = size; + LOGV("setDataSize Setting data size of %p to %d\n", this, mDataSize); + } + return err; +} + +void Parcel::setDataPosition(size_t pos) const +{ + mDataPos = pos; + mNextObjectHint = 0; +} + +status_t Parcel::setDataCapacity(size_t size) +{ + if (size > mDataSize) return continueWrite(size); + return NO_ERROR; +} + +status_t Parcel::setData(const uint8_t* buffer, size_t len) +{ + status_t err = restartWrite(len); + if (err == NO_ERROR) { + memcpy(const_cast(data()), buffer, len); + mDataSize = len; + mFdsKnown = false; + } + return err; +} + +status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len) +{ + const sp proc(ProcessState::self()); + status_t err; + uint8_t *data = parcel->mData; + size_t *objects = parcel->mObjects; + size_t size = parcel->mObjectsSize; + int startPos = mDataPos; + int firstIndex = -1, lastIndex = -2; + + if (len == 0) { + return NO_ERROR; + } + + // range checks against the source parcel size + if ((offset > parcel->mDataSize) + || (len > parcel->mDataSize) + || (offset + len > parcel->mDataSize)) { + return BAD_VALUE; + } + + // Count objects in range + for (int i = 0; i < (int) size; i++) { + size_t off = objects[i]; + if ((off >= offset) && (off < offset + len)) { + if (firstIndex == -1) { + firstIndex = i; + } + lastIndex = i; + } + } + int numObjects = lastIndex - firstIndex + 1; + + // grow data + err = growData(len); + if (err != NO_ERROR) { + return err; + } + + // append data + memcpy(mData + mDataPos, data + offset, len); + mDataPos += len; + mDataSize += len; + + if (numObjects > 0) { + // grow objects + if (mObjectsCapacity < mObjectsSize + numObjects) { + int newSize = ((mObjectsSize + numObjects)*3)/2; + size_t *objects = + (size_t*)realloc(mObjects, newSize*sizeof(size_t)); + if (objects == (size_t*)0) { + return NO_MEMORY; + } + mObjects = objects; + mObjectsCapacity = newSize; + } + + // append and acquire objects + int idx = mObjectsSize; + for (int i = firstIndex; i <= lastIndex; i++) { + size_t off = objects[i] - offset + startPos; + mObjects[idx++] = off; + mObjectsSize++; + + const flat_binder_object* flat + = reinterpret_cast(mData + off); + acquire_object(proc, *flat, this); + + // take note if the object is a file descriptor + if (flat->type == BINDER_TYPE_FD) { + mHasFds = mFdsKnown = true; + } + } + } + + return NO_ERROR; +} + +bool Parcel::hasFileDescriptors() const +{ + if (!mFdsKnown) { + scanForFds(); + } + return mHasFds; +} + +status_t Parcel::writeInterfaceToken(const String16& interface) +{ + // currently the interface identification token is just its name as a string + return writeString16(interface); +} + +bool Parcel::enforceInterface(const String16& interface) const +{ + String16 str = readString16(); + if (str == interface) { + return true; + } else { + LOGW("**** enforceInterface() expected '%s' but read '%s'\n", + String8(interface).string(), String8(str).string()); + return false; + } +} + +const size_t* Parcel::objects() const +{ + return mObjects; +} + +size_t Parcel::objectsCount() const +{ + return mObjectsSize; +} + +status_t Parcel::errorCheck() const +{ + return mError; +} + +void Parcel::setError(status_t err) +{ + mError = err; +} + +status_t Parcel::finishWrite(size_t len) +{ + //printf("Finish write of %d\n", len); + mDataPos += len; + LOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos); + if (mDataPos > mDataSize) { + mDataSize = mDataPos; + LOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize); + } + //printf("New pos=%d, size=%d\n", mDataPos, mDataSize); + return NO_ERROR; +} + +status_t Parcel::writeUnpadded(const void* data, size_t len) +{ + size_t end = mDataPos + len; + if (end < mDataPos) { + // integer overflow + return BAD_VALUE; + } + + if (end <= mDataCapacity) { +restart_write: + memcpy(mData+mDataPos, data, len); + return finishWrite(len); + } + + status_t err = growData(len); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::write(const void* data, size_t len) +{ + void* const d = writeInplace(len); + if (d) { + memcpy(d, data, len); + return NO_ERROR; + } + return mError; +} + +void* Parcel::writeInplace(size_t len) +{ + const size_t padded = PAD_SIZE(len); + + // sanity check for integer overflow + if (mDataPos+padded < mDataPos) { + return NULL; + } + + if ((mDataPos+padded) <= mDataCapacity) { +restart_write: + //printf("Writing %ld bytes, padded to %ld\n", len, padded); + uint8_t* const data = mData+mDataPos; + + // Need to pad at end? + if (padded != len) { +#if BYTE_ORDER == BIG_ENDIAN + static const uint32_t mask[4] = { + 0x00000000, 0xffffff00, 0xffff0000, 0xff000000 + }; +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + static const uint32_t mask[4] = { + 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff + }; +#endif + //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len], + // *reinterpret_cast(data+padded-4)); + *reinterpret_cast(data+padded-4) &= mask[padded-len]; + } + + finishWrite(padded); + return data; + } + + status_t err = growData(padded); + if (err == NO_ERROR) goto restart_write; + return NULL; +} + +status_t Parcel::writeInt32(int32_t val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeInt64(int64_t val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeFloat(float val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeDouble(double val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeCString(const char* str) +{ + return write(str, strlen(str)+1); +} + +status_t Parcel::writeString8(const String8& str) +{ + status_t err = writeInt32(str.bytes()); + if (err == NO_ERROR) { + err = write(str.string(), str.bytes()+1); + } + return err; +} + +status_t Parcel::writeString16(const String16& str) +{ + return writeString16(str.string(), str.size()); +} + +status_t Parcel::writeString16(const char16_t* str, size_t len) +{ + if (str == NULL) return writeInt32(-1); + + status_t err = writeInt32(len); + if (err == NO_ERROR) { + len *= sizeof(char16_t); + uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); + if (data) { + memcpy(data, str, len); + *reinterpret_cast(data+len) = 0; + return NO_ERROR; + } + err = mError; + } + return err; +} + +status_t Parcel::writeStrongBinder(const sp& val) +{ + return flatten_binder(ProcessState::self(), val, this); +} + +status_t Parcel::writeWeakBinder(const wp& val) +{ + return flatten_binder(ProcessState::self(), val, this); +} + +status_t Parcel::writeNativeHandle(const native_handle& handle) +{ + if (handle.version != sizeof(native_handle)) + return BAD_TYPE; + + status_t err; + err = writeInt32(handle.numFds); + if (err != NO_ERROR) return err; + + err = writeInt32(handle.numInts); + if (err != NO_ERROR) return err; + + for (int i=0 ; err==NO_ERROR && i(mData+mDataPos) = val; + + // Need to write meta-data? + if (nullMetaData || val.binder != NULL) { + mObjects[mObjectsSize] = mDataPos; + acquire_object(ProcessState::self(), val, this); + mObjectsSize++; + } + + // remember if it's a file descriptor + if (val.type == BINDER_TYPE_FD) { + mHasFds = mFdsKnown = true; + } + + return finishWrite(sizeof(flat_binder_object)); + } + + if (!enoughData) { + const status_t err = growData(sizeof(val)); + if (err != NO_ERROR) return err; + } + if (!enoughObjects) { + size_t newSize = ((mObjectsSize+2)*3)/2; + size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t)); + if (objects == NULL) return NO_MEMORY; + mObjects = objects; + mObjectsCapacity = newSize; + } + + goto restart_write; +} + + +void Parcel::remove(size_t start, size_t amt) +{ + LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); +} + +status_t Parcel::read(void* outData, size_t len) const +{ + if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { + memcpy(outData, mData+mDataPos, len); + mDataPos += PAD_SIZE(len); + LOGV("read Setting data pos of %p to %d\n", this, mDataPos); + return NO_ERROR; + } + return NOT_ENOUGH_DATA; +} + +const void* Parcel::readInplace(size_t len) const +{ + if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += PAD_SIZE(len); + LOGV("readInplace Setting data pos of %p to %d\n", this, mDataPos); + return data; + } + return NULL; +} + +status_t Parcel::readInt32(int32_t *pArg) const +{ + if ((mDataPos+sizeof(int32_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int32_t); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + +int32_t Parcel::readInt32() const +{ + if ((mDataPos+sizeof(int32_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int32_t); + LOGV("readInt32 Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + + +status_t Parcel::readInt64(int64_t *pArg) const +{ + if ((mDataPos+sizeof(int64_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int64_t); + *pArg = *reinterpret_cast(data); + LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +int64_t Parcel::readInt64() const +{ + if ((mDataPos+sizeof(int64_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int64_t); + LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + +status_t Parcel::readFloat(float *pArg) const +{ + if ((mDataPos+sizeof(float)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(float); + LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +float Parcel::readFloat() const +{ + if ((mDataPos+sizeof(float)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(float); + LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + +status_t Parcel::readDouble(double *pArg) const +{ + if ((mDataPos+sizeof(double)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(double); + LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +double Parcel::readDouble() const +{ + if ((mDataPos+sizeof(double)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(double); + LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + + +const char* Parcel::readCString() const +{ + const size_t avail = mDataSize-mDataPos; + if (avail > 0) { + const char* str = reinterpret_cast(mData+mDataPos); + // is the string's trailing NUL within the parcel's valid bounds? + const char* eos = reinterpret_cast(memchr(str, 0, avail)); + if (eos) { + const size_t len = eos - str; + mDataPos += PAD_SIZE(len+1); + LOGV("readCString Setting data pos of %p to %d\n", this, mDataPos); + return str; + } + } + return NULL; +} + +String8 Parcel::readString8() const +{ + int32_t size = readInt32(); + // watch for potential int overflow adding 1 for trailing NUL + if (size > 0 && size < INT32_MAX) { + const char* str = (const char*)readInplace(size+1); + if (str) return String8(str, size); + } + return String8(); +} + +String16 Parcel::readString16() const +{ + size_t len; + const char16_t* str = readString16Inplace(&len); + if (str) return String16(str, len); + LOGE("Reading a NULL string not supported here."); + return String16(); +} + +const char16_t* Parcel::readString16Inplace(size_t* outLen) const +{ + int32_t size = readInt32(); + // watch for potential int overflow from size+1 + if (size >= 0 && size < INT32_MAX) { + *outLen = size; + const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t)); + if (str != NULL) { + return str; + } + } + *outLen = 0; + return NULL; +} + +sp Parcel::readStrongBinder() const +{ + sp val; + unflatten_binder(ProcessState::self(), *this, &val); + return val; +} + +wp Parcel::readWeakBinder() const +{ + wp val; + unflatten_binder(ProcessState::self(), *this, &val); + return val; +} + + +native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int), void* cookie) const +{ + int numFds, numInts; + status_t err; + err = readInt32(&numFds); + if (err != NO_ERROR) return 0; + err = readInt32(&numInts); + if (err != NO_ERROR) return 0; + + native_handle* h; + if (alloc == 0) { + size_t size = sizeof(native_handle) + sizeof(int)*(numFds + numInts); + h = (native_handle*)malloc(size); + h->version = sizeof(native_handle); + h->numFds = numFds; + h->numInts = numInts; + } else { + h = alloc(cookie, numFds, numInts); + if (h->version != sizeof(native_handle)) { + return 0; + } + } + + for (int i=0 ; err==NO_ERROR && idata[i] = dup(readFileDescriptor()); + if (h->data[i] < 0) err = BAD_VALUE; + } + + err = read(h->data + numFds, sizeof(int)*numInts); + + if (err != NO_ERROR) { + if (alloc == 0) { + free(h); + } + h = 0; + } + return h; +} + + +int Parcel::readFileDescriptor() const +{ + const flat_binder_object* flat = readObject(true); + if (flat) { + switch (flat->type) { + case BINDER_TYPE_FD: + //LOGI("Returning file descriptor %ld from parcel %p\n", flat->handle, this); + return flat->handle; + } + } + return BAD_TYPE; +} + +const flat_binder_object* Parcel::readObject(bool nullMetaData) const +{ + const size_t DPOS = mDataPos; + if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) { + const flat_binder_object* obj + = reinterpret_cast(mData+DPOS); + mDataPos = DPOS + sizeof(flat_binder_object); + if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) { + // When transferring a NULL object, we don't write it into + // the object list, so we don't want to check for it when + // reading. + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + + // Ensure that this object is valid... + size_t* const OBJS = mObjects; + const size_t N = mObjectsSize; + size_t opos = mNextObjectHint; + + if (N > 0) { + LOGV("Parcel %p looking for obj at %d, hint=%d\n", + this, DPOS, opos); + + // Start at the current hint position, looking for an object at + // the current data position. + if (opos < N) { + while (opos < (N-1) && OBJS[opos] < DPOS) { + opos++; + } + } else { + opos = N-1; + } + if (OBJS[opos] == DPOS) { + // Found it! + LOGV("Parcel found obj %d at index %d with forward search", + this, DPOS, opos); + mNextObjectHint = opos+1; + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + + // Look backwards for it... + while (opos > 0 && OBJS[opos] > DPOS) { + opos--; + } + if (OBJS[opos] == DPOS) { + // Found it! + LOGV("Parcel found obj %d at index %d with backward search", + this, DPOS, opos); + mNextObjectHint = opos+1; + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + } + LOGW("Attempt to read object from Parcel %p at offset %d that is not in the object list", + this, DPOS); + } + return NULL; +} + +void Parcel::closeFileDescriptors() +{ + size_t i = mObjectsSize; + if (i > 0) { + //LOGI("Closing file descriptors for %d objects...", mObjectsSize); + } + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(mData+mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + //LOGI("Closing fd: %ld\n", flat->handle); + close(flat->handle); + } + } +} + +const uint8_t* Parcel::ipcData() const +{ + return mData; +} + +size_t Parcel::ipcDataSize() const +{ + return (mDataSize > mDataPos ? mDataSize : mDataPos); +} + +const size_t* Parcel::ipcObjects() const +{ + return mObjects; +} + +size_t Parcel::ipcObjectsCount() const +{ + return mObjectsSize; +} + +void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie) +{ + freeDataNoInit(); + mError = NO_ERROR; + mData = const_cast(data); + mDataSize = mDataCapacity = dataSize; + //LOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid()); + mDataPos = 0; + LOGV("setDataReference Setting data pos of %p to %d\n", this, mDataPos); + mObjects = const_cast(objects); + mObjectsSize = mObjectsCapacity = objectsCount; + mNextObjectHint = 0; + mOwner = relFunc; + mOwnerCookie = relCookie; + scanForFds(); +} + +void Parcel::print(TextOutput& to, uint32_t flags) const +{ + to << "Parcel("; + + if (errorCheck() != NO_ERROR) { + const status_t err = errorCheck(); + to << "Error: " << (void*)err << " \"" << strerror(-err) << "\""; + } else if (dataSize() > 0) { + const uint8_t* DATA = data(); + to << indent << HexDump(DATA, dataSize()) << dedent; + const size_t* OBJS = objects(); + const size_t N = objectsCount(); + for (size_t i=0; i(DATA+OBJS[i]); + to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": " + << TypeCode(flat->type & 0x7f7f7f00) + << " = " << flat->binder; + } + } else { + to << "NULL"; + } + + to << ")"; +} + +void Parcel::releaseObjects() +{ + const sp proc(ProcessState::self()); + size_t i = mObjectsSize; + uint8_t* const data = mData; + size_t* const objects = mObjects; + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(data+objects[i]); + release_object(proc, *flat, this); + } +} + +void Parcel::acquireObjects() +{ + const sp proc(ProcessState::self()); + size_t i = mObjectsSize; + uint8_t* const data = mData; + size_t* const objects = mObjects; + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(data+objects[i]); + acquire_object(proc, *flat, this); + } +} + +void Parcel::freeData() +{ + freeDataNoInit(); + initState(); +} + +void Parcel::freeDataNoInit() +{ + if (mOwner) { + //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); + mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); + } else { + releaseObjects(); + if (mData) free(mData); + if (mObjects) free(mObjects); + } +} + +status_t Parcel::growData(size_t len) +{ + size_t newSize = ((mDataSize+len)*3)/2; + return (newSize <= mDataSize) + ? (status_t) NO_MEMORY + : continueWrite(newSize); +} + +status_t Parcel::restartWrite(size_t desired) +{ + if (mOwner) { + freeData(); + return continueWrite(desired); + } + + uint8_t* data = (uint8_t*)realloc(mData, desired); + if (!data && desired > mDataCapacity) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + releaseObjects(); + + if (data) { + mData = data; + mDataCapacity = desired; + } + + mDataSize = mDataPos = 0; + LOGV("restartWrite Setting data size of %p to %d\n", this, mDataSize); + LOGV("restartWrite Setting data pos of %p to %d\n", this, mDataPos); + + free(mObjects); + mObjects = NULL; + mObjectsSize = mObjectsCapacity = 0; + mNextObjectHint = 0; + mHasFds = false; + mFdsKnown = true; + + return NO_ERROR; +} + +status_t Parcel::continueWrite(size_t desired) +{ + // If shrinking, first adjust for any objects that appear + // after the new data size. + size_t objectsSize = mObjectsSize; + if (desired < mDataSize) { + if (desired == 0) { + objectsSize = 0; + } else { + while (objectsSize > 0) { + if (mObjects[objectsSize-1] < desired) + break; + objectsSize--; + } + } + } + + if (mOwner) { + // If the size is going to zero, just release the owner's data. + if (desired == 0) { + freeData(); + return NO_ERROR; + } + + // If there is a different owner, we need to take + // posession. + uint8_t* data = (uint8_t*)malloc(desired); + if (!data) { + mError = NO_MEMORY; + return NO_MEMORY; + } + size_t* objects = NULL; + + if (objectsSize) { + objects = (size_t*)malloc(objectsSize*sizeof(size_t)); + if (!objects) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + // Little hack to only acquire references on objects + // we will be keeping. + size_t oldObjectsSize = mObjectsSize; + mObjectsSize = objectsSize; + acquireObjects(); + mObjectsSize = oldObjectsSize; + } + + if (mData) { + memcpy(data, mData, mDataSize < desired ? mDataSize : desired); + } + if (objects && mObjects) { + memcpy(objects, mObjects, objectsSize*sizeof(size_t)); + } + //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); + mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); + mOwner = NULL; + + mData = data; + mObjects = objects; + mDataSize = (mDataSize < desired) ? mDataSize : desired; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + mDataCapacity = desired; + mObjectsSize = mObjectsCapacity = objectsSize; + mNextObjectHint = 0; + + } else if (mData) { + if (objectsSize < mObjectsSize) { + // Need to release refs on any objects we are dropping. + const sp proc(ProcessState::self()); + for (size_t i=objectsSize; i(mData+mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + // will need to rescan because we may have lopped off the only FDs + mFdsKnown = false; + } + release_object(proc, *flat, this); + } + size_t* objects = + (size_t*)realloc(mObjects, objectsSize*sizeof(size_t)); + if (objects) { + mObjects = objects; + } + mObjectsSize = objectsSize; + mNextObjectHint = 0; + } + + // We own the data, so we can just do a realloc(). + if (desired > mDataCapacity) { + uint8_t* data = (uint8_t*)realloc(mData, desired); + if (data) { + mData = data; + mDataCapacity = desired; + } else if (desired > mDataCapacity) { + mError = NO_MEMORY; + return NO_MEMORY; + } + } else { + mDataSize = desired; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + if (mDataPos > desired) { + mDataPos = desired; + LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); + } + } + + } else { + // This is the first data. Easy! + uint8_t* data = (uint8_t*)malloc(desired); + if (!data) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + if(!(mDataCapacity == 0 && mObjects == NULL + && mObjectsCapacity == 0)) { + LOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired); + } + + mData = data; + mDataSize = mDataPos = 0; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); + mDataCapacity = desired; + } + + return NO_ERROR; +} + +void Parcel::initState() +{ + mError = NO_ERROR; + mData = 0; + mDataSize = 0; + mDataCapacity = 0; + mDataPos = 0; + LOGV("initState Setting data size of %p to %d\n", this, mDataSize); + LOGV("initState Setting data pos of %p to %d\n", this, mDataPos); + mObjects = NULL; + mObjectsSize = 0; + mObjectsCapacity = 0; + mNextObjectHint = 0; + mHasFds = false; + mFdsKnown = true; + mOwner = NULL; +} + +void Parcel::scanForFds() const +{ + bool hasFds = false; + for (size_t i=0; i(mData + mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + hasFds = true; + break; + } + } + mHasFds = hasFds; + mFdsKnown = true; +} + +}; // namespace android diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp new file mode 100644 index 000000000..613906bed --- /dev/null +++ b/libs/utils/Pipe.cpp @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Unidirectional pipe. +// + +#include +#include + +#if defined(HAVE_WIN32_IPC) +# include +#else +# include +# include +# include +#endif + +#include +#include +#include +#include + +using namespace android; + +const unsigned long kInvalidHandle = (unsigned long) -1; + + +/* + * Constructor. Do little. + */ +Pipe::Pipe(void) + : mReadNonBlocking(false), mReadHandle(kInvalidHandle), + mWriteHandle(kInvalidHandle) +{ +} + +/* + * Destructor. Use the system-appropriate close call. + */ +Pipe::~Pipe(void) +{ +#if defined(HAVE_WIN32_IPC) + if (mReadHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mReadHandle)) + LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n", + mReadHandle); + } + if (mWriteHandle != kInvalidHandle) { + FlushFileBuffers((HANDLE)mWriteHandle); + if (!CloseHandle((HANDLE)mWriteHandle)) + LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n", + mWriteHandle); + } +#else + if (mReadHandle != kInvalidHandle) { + if (close((int) mReadHandle) != 0) + LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n", + (int) mReadHandle); + } + if (mWriteHandle != kInvalidHandle) { + if (close((int) mWriteHandle) != 0) + LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n", + (int) mWriteHandle); + } +#endif +} + +/* + * Create the pipe. + * + * Use the POSIX stuff for everything but Windows. + */ +bool Pipe::create(void) +{ + assert(mReadHandle == kInvalidHandle); + assert(mWriteHandle == kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + /* we use this across processes, so they need to be inheritable */ + HANDLE handles[2]; + SECURITY_ATTRIBUTES saAttr; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) { + LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); + return false; + } + mReadHandle = (unsigned long) handles[0]; + mWriteHandle = (unsigned long) handles[1]; + return true; +#else + int fds[2]; + + if (pipe(fds) != 0) { + LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); + return false; + } + mReadHandle = fds[0]; + mWriteHandle = fds[1]; + return true; +#endif +} + +/* + * Create a "half pipe". Please, no Segway riding. + */ +bool Pipe::createReader(unsigned long handle) +{ + mReadHandle = handle; + assert(mWriteHandle == kInvalidHandle); + return true; +} + +/* + * Create a "half pipe" for writing. + */ +bool Pipe::createWriter(unsigned long handle) +{ + mWriteHandle = handle; + assert(mReadHandle == kInvalidHandle); + return true; +} + +/* + * Return "true" if create() has been called successfully. + */ +bool Pipe::isCreated(void) +{ + // one or the other should be open + return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle); +} + + +/* + * Read data from the pipe. + * + * For Linux and Darwin, just call read(). For Windows, implement + * non-blocking reads by calling PeekNamedPipe first. + */ +int Pipe::read(void* buf, int count) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD totalBytesAvail = count; + DWORD bytesRead; + + if (mReadNonBlocking) { + // use PeekNamedPipe to adjust read count expectations + if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, + &totalBytesAvail, NULL)) + { + LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); + return -1; + } + + if (totalBytesAvail == 0) + return 0; + } + + if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead, + NULL)) + { + DWORD err = GetLastError(); + if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) + return 0; + LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err); + return -1; + } + + return (int) bytesRead; +#else + int cc; + cc = ::read(mReadHandle, buf, count); + if (cc < 0 && errno == EAGAIN) + return 0; + return cc; +#endif +} + +/* + * Write data to the pipe. + * + * POSIX systems are trivial, Windows uses a different call and doesn't + * handle non-blocking writes. + * + * If we add non-blocking support here, we probably want to make it an + * all-or-nothing write. + * + * DO NOT use LOG() here, we could be writing a log message. + */ +int Pipe::write(const void* buf, int count) +{ + assert(mWriteHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD bytesWritten; + + if (mWriteNonBlocking) { + // BUG: can't use PeekNamedPipe() to get the amount of space + // left. Looks like we need to use "overlapped I/O" functions. + // I just don't care that much. + } + + if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) { + // can't LOG, use stderr + fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError()); + return -1; + } + + return (int) bytesWritten; +#else + int cc; + cc = ::write(mWriteHandle, buf, count); + if (cc < 0 && errno == EAGAIN) + return 0; + return cc; +#endif +} + +/* + * Figure out if there is data available on the read fd. + * + * We return "true" on error because we want the caller to try to read + * from the pipe. They'll notice the read failure and do something + * appropriate. + */ +bool Pipe::readReady(void) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD totalBytesAvail; + + if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, + &totalBytesAvail, NULL)) + { + LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); + return true; + } + + return (totalBytesAvail != 0); +#else + errno = 0; + fd_set readfds; + struct timeval tv = { 0, 0 }; + int cc; + + FD_ZERO(&readfds); + FD_SET(mReadHandle, &readfds); + + cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv); + if (cc < 0) { + LOG(LOG_ERROR, "pipe", "select() failed\n"); + return true; + } else if (cc == 0) { + /* timed out, nothing available */ + return false; + } else if (cc == 1) { + /* our fd is ready */ + return true; + } else { + LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n"); + return true; + } +#endif +} + +/* + * Enable or disable non-blocking mode for the read descriptor. + * + * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to + * actually be in non-blocking mode. If this matters -- i.e. you're not + * using a select() call -- put a call to readReady() in front of the + * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for + * Darwin. + */ +bool Pipe::setReadNonBlocking(bool val) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + // nothing to do +#else + int flags; + + if (fcntl(mReadHandle, F_GETFL, &flags) == -1) { + LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n"); + return false; + } + if (val) + flags |= O_NONBLOCK; + else + flags &= ~(O_NONBLOCK); + if (fcntl(mReadHandle, F_SETFL, &flags) == -1) { + LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n"); + return false; + } +#endif + + mReadNonBlocking = val; + return true; +} + +/* + * Enable or disable non-blocking mode for the write descriptor. + * + * As with setReadNonBlocking(), this does not work on the Mac. + */ +bool Pipe::setWriteNonBlocking(bool val) +{ + assert(mWriteHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + // nothing to do +#else + int flags; + + if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) { + LOG(LOG_WARN, "pipe", + "Warning: couldn't get flags for pipe write fd (errno=%d)\n", + errno); + return false; + } + if (val) + flags |= O_NONBLOCK; + else + flags &= ~(O_NONBLOCK); + if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) { + LOG(LOG_WARN, "pipe", + "Warning: couldn't set flags for pipe write fd (errno=%d)\n", + errno); + return false; + } +#endif + + mWriteNonBlocking = val; + return true; +} + +/* + * Specify whether a file descriptor can be inherited by a child process. + * Under Linux this means setting the close-on-exec flag, under Windows + * this is SetHandleInformation(HANDLE_FLAG_INHERIT). + */ +bool Pipe::disallowReadInherit(void) +{ + if (mReadHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0) + return false; +#else + if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0) + return false; +#endif + return true; +} +bool Pipe::disallowWriteInherit(void) +{ + if (mWriteHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0) + return false; +#else + if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0) + return false; +#endif + return true; +} + +/* + * Close read descriptor. + */ +bool Pipe::closeRead(void) +{ + if (mReadHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (mReadHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mReadHandle)) { + LOG(LOG_WARN, "pipe", "failed closing read handle\n"); + return false; + } + } +#else + if (mReadHandle != kInvalidHandle) { + if (close((int) mReadHandle) != 0) { + LOG(LOG_WARN, "pipe", "failed closing read fd\n"); + return false; + } + } +#endif + mReadHandle = kInvalidHandle; + return true; +} + +/* + * Close write descriptor. + */ +bool Pipe::closeWrite(void) +{ + if (mWriteHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (mWriteHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mWriteHandle)) { + LOG(LOG_WARN, "pipe", "failed closing write handle\n"); + return false; + } + } +#else + if (mWriteHandle != kInvalidHandle) { + if (close((int) mWriteHandle) != 0) { + LOG(LOG_WARN, "pipe", "failed closing write fd\n"); + return false; + } + } +#endif + mWriteHandle = kInvalidHandle; + return true; +} + +/* + * Get the read handle. + */ +unsigned long Pipe::getReadHandle(void) +{ + assert(mReadHandle != kInvalidHandle); + + return mReadHandle; +} + +/* + * Get the write handle. + */ +unsigned long Pipe::getWriteHandle(void) +{ + assert(mWriteHandle != kInvalidHandle); + + return mWriteHandle; +} + diff --git a/libs/utils/ProcessState.cpp b/libs/utils/ProcessState.cpp new file mode 100644 index 000000000..4567df60f --- /dev/null +++ b/libs/utils/ProcessState.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2005 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. + */ + +#define LOG_TAG "ProcessState" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BINDER_VM_SIZE (1*1024*1024) + +static bool gSingleProcess = false; + + +// --------------------------------------------------------------------------- + +namespace android { + +// Global variables +int mArgC; +const char* const* mArgV; +int mArgLen; + +class PoolThread : public Thread +{ +public: + PoolThread(bool isMain) + : mIsMain(isMain) + { + } + +protected: + virtual bool threadLoop() + { + IPCThreadState::self()->joinThreadPool(mIsMain); + return false; + } + + const bool mIsMain; +}; + +sp ProcessState::self() +{ + if (gProcess != NULL) return gProcess; + + AutoMutex _l(gProcessMutex); + if (gProcess == NULL) gProcess = new ProcessState; + return gProcess; +} + +void ProcessState::setSingleProcess(bool singleProcess) +{ + gSingleProcess = singleProcess; +} + + +void ProcessState::setContextObject(const sp& object) +{ + setContextObject(object, String16("default")); +} + +sp ProcessState::getContextObject(const sp& caller) +{ + if (supportsProcesses()) { + return getStrongProxyForHandle(0); + } else { + return getContextObject(String16("default"), caller); + } +} + +void ProcessState::setContextObject(const sp& object, const String16& name) +{ + AutoMutex _l(mLock); + mContexts.add(name, object); +} + +sp ProcessState::getContextObject(const String16& name, const sp& caller) +{ + mLock.lock(); + sp object( + mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : NULL); + mLock.unlock(); + + //printf("Getting context object %s for %p\n", String8(name).string(), caller.get()); + + if (object != NULL) return object; + + // Don't attempt to retrieve contexts if we manage them + if (mManagesContexts) { + LOGE("getContextObject(%s) failed, but we manage the contexts!\n", + String8(name).string()); + return NULL; + } + + IPCThreadState* ipc = IPCThreadState::self(); + { + Parcel data, reply; + // no interface token on this magic transaction + data.writeString16(name); + data.writeStrongBinder(caller); + status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0); + if (result == NO_ERROR) { + object = reply.readStrongBinder(); + } + } + + ipc->flushCommands(); + + if (object != NULL) setContextObject(object, name); + return object; +} + +bool ProcessState::supportsProcesses() const +{ + return mDriverFD >= 0; +} + +void ProcessState::startThreadPool() +{ + AutoMutex _l(mLock); + if (!mThreadPoolStarted) { + mThreadPoolStarted = true; + spawnPooledThread(true); + } +} + +bool ProcessState::isContextManager(void) const +{ + return mManagesContexts; +} + +bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData) +{ + if (!mManagesContexts) { + AutoMutex _l(mLock); + mBinderContextCheckFunc = checkFunc; + mBinderContextUserData = userData; + if (mDriverFD >= 0) { + int dummy = 0; +#if defined(HAVE_ANDROID_OS) + status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); +#else + status_t result = INVALID_OPERATION; +#endif + if (result == 0) { + mManagesContexts = true; + } else if (result == -1) { + mBinderContextCheckFunc = NULL; + mBinderContextUserData = NULL; + LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); + } + } else { + // If there is no driver, our only world is the local + // process so we can always become the context manager there. + mManagesContexts = true; + } + } + return mManagesContexts; +} + +ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) +{ + const size_t N=mHandleToObject.size(); + if (N <= (size_t)handle) { + handle_entry e; + e.binder = NULL; + e.refs = NULL; + status_t err = mHandleToObject.insertAt(e, N, handle+1-N); + if (err < NO_ERROR) return NULL; + } + return &mHandleToObject.editItemAt(handle); +} + +sp ProcessState::getStrongProxyForHandle(int32_t handle) +{ + sp result; + + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + if (e != NULL) { + // We need to create a new BpBinder if there isn't currently one, OR we + // are unable to acquire a weak reference on this current one. See comment + // in getWeakProxyForHandle() for more info about this. + IBinder* b = e->binder; + if (b == NULL || !e->refs->attemptIncWeak(this)) { + b = new BpBinder(handle); + e->binder = b; + if (b) e->refs = b->getWeakRefs(); + result = b; + } else { + // This little bit of nastyness is to allow us to add a primary + // reference to the remote proxy when this team doesn't have one + // but another team is sending the handle to us. + result.force_set(b); + e->refs->decWeak(this); + } + } + + return result; +} + +wp ProcessState::getWeakProxyForHandle(int32_t handle) +{ + wp result; + + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + if (e != NULL) { + // We need to create a new BpBinder if there isn't currently one, OR we + // are unable to acquire a weak reference on this current one. The + // attemptIncWeak() is safe because we know the BpBinder destructor will always + // call expungeHandle(), which acquires the same lock we are holding now. + // We need to do this because there is a race condition between someone + // releasing a reference on this BpBinder, and a new reference on its handle + // arriving from the driver. + IBinder* b = e->binder; + if (b == NULL || !e->refs->attemptIncWeak(this)) { + b = new BpBinder(handle); + result = b; + e->binder = b; + if (b) e->refs = b->getWeakRefs(); + } else { + result = b; + e->refs->decWeak(this); + } + } + + return result; +} + +void ProcessState::expungeHandle(int32_t handle, IBinder* binder) +{ + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + // This handle may have already been replaced with a new BpBinder + // (if someone failed the AttemptIncWeak() above); we don't want + // to overwrite it. + if (e && e->binder == binder) e->binder = NULL; +} + +void ProcessState::setArgs(int argc, const char* const argv[]) +{ + mArgC = argc; + mArgV = (const char **)argv; + + mArgLen = 0; + for (int i=0; i t = new PoolThread(isMain); + t->run(buf); + } +} + +static int open_driver() +{ + if (gSingleProcess) { + return -1; + } + + int fd = open("/dev/binder", O_RDWR); + if (fd >= 0) { + fcntl(fd, F_SETFD, FD_CLOEXEC); + int vers; +#if defined(HAVE_ANDROID_OS) + status_t result = ioctl(fd, BINDER_VERSION, &vers); +#else + status_t result = -1; + errno = EPERM; +#endif + if (result == -1) { + LOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); + close(fd); + fd = -1; + } + if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { + LOGE("Binder driver protocol does not match user space protocol!"); + close(fd); + fd = -1; + } +#if defined(HAVE_ANDROID_OS) + size_t maxThreads = 15; + result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); + if (result == -1) { + LOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); + } +#endif + + } else { + LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); + } + return fd; +} + +ProcessState::ProcessState() + : mDriverFD(open_driver()) + , mVMStart(MAP_FAILED) + , mManagesContexts(false) + , mBinderContextCheckFunc(NULL) + , mBinderContextUserData(NULL) + , mThreadPoolStarted(false) + , mThreadPoolSeq(1) +{ + if (mDriverFD >= 0) { + // XXX Ideally, there should be a specific define for whether we + // have mmap (or whether we could possibly have the kernel module + // availabla). +#if !defined(HAVE_WIN32_IPC) + // mmap the binder, providing a chunk of virtual address space to receive transactions. + mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); + if (mVMStart == MAP_FAILED) { + // *sigh* + LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); + close(mDriverFD); + mDriverFD = -1; + } +#else + mDriverFD = -1; +#endif + } + if (mDriverFD < 0) { + // Need to run without the driver, starting our own thread pool. + } +} + +ProcessState::~ProcessState() +{ +} + +}; // namespace android diff --git a/libs/utils/README b/libs/utils/README new file mode 100644 index 000000000..36a706d5c --- /dev/null +++ b/libs/utils/README @@ -0,0 +1,14 @@ +Android Utility Function Library + +If you need a feature that is native to Linux but not present on other +platforms, construct a platform-dependent implementation that shares +the Linux interface. That way the actual device runs as "light" as +possible. + +If that isn't feasible, create a system-independent interface and hide +the details. + +The ultimate goal is *not* to create a super-duper platform abstraction +layer. The goal is to provide an optimized solution for Linux with +reasonable implementations for other platforms. + diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp new file mode 100644 index 000000000..0bd1af4eb --- /dev/null +++ b/libs/utils/RefBase.cpp @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2005 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. + */ + +#define LOG_TAG "RefBase" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// compile with refcounting debugging enabled +#define DEBUG_REFS 0 +#define DEBUG_REFS_ENABLED_BY_DEFAULT 1 +#define DEBUG_REFS_CALLSTACK_ENABLED 1 + +// log all reference counting operations +#define PRINT_REFS 0 + +// --------------------------------------------------------------------------- + +namespace android { + +#define INITIAL_STRONG_VALUE (1<<28) + +// --------------------------------------------------------------------------- + +class RefBase::weakref_impl : public RefBase::weakref_type +{ +public: + volatile int32_t mStrong; + volatile int32_t mWeak; + RefBase* const mBase; + volatile int32_t mFlags; + + +#if !DEBUG_REFS + + weakref_impl(RefBase* base) + : mStrong(INITIAL_STRONG_VALUE) + , mWeak(0) + , mBase(base) + , mFlags(0) + { + } + + void addStrongRef(const void* /*id*/) { } + void removeStrongRef(const void* /*id*/) { } + void addWeakRef(const void* /*id*/) { } + void removeWeakRef(const void* /*id*/) { } + void printRefs() const { } + void trackMe(bool, bool) { } + +#else + + weakref_impl(RefBase* base) + : mStrong(INITIAL_STRONG_VALUE) + , mWeak(0) + , mBase(base) + , mFlags(0) + , mStrongRefs(NULL) + , mWeakRefs(NULL) + , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) + , mRetain(false) + { + //LOGI("NEW weakref_impl %p for RefBase %p", this, base); + } + + ~weakref_impl() + { + LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!"); + LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!"); + } + + void addStrongRef(const void* id) + { + addRef(&mStrongRefs, id, mStrong); + } + + void removeStrongRef(const void* id) + { + if (!mRetain) + removeRef(&mStrongRefs, id); + else + addRef(&mStrongRefs, id, -mStrong); + } + + void addWeakRef(const void* id) + { + addRef(&mWeakRefs, id, mWeak); + } + + void removeWeakRef(const void* id) + { + if (!mRetain) + removeRef(&mWeakRefs, id); + else + addRef(&mWeakRefs, id, -mWeak); + } + + void trackMe(bool track, bool retain) + { + mTrackEnabled = track; + mRetain = retain; + } + + void printRefs() const + { + String8 text; + + { + AutoMutex _l(const_cast(this)->mMutex); + + char buf[128]; + sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this); + text.append(buf); + printRefsLocked(&text, mStrongRefs); + sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this); + text.append(buf); + printRefsLocked(&text, mWeakRefs); + } + + { + char name[100]; + snprintf(name, 100, "/data/%p.stack", this); + int rc = open(name, O_RDWR | O_CREAT | O_APPEND); + if (rc >= 0) { + write(rc, text.string(), text.length()); + close(rc); + LOGD("STACK TRACE for %p saved in %s", this, name); + } + else LOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this, + name, strerror(errno)); + } + } + +private: + struct ref_entry + { + ref_entry* next; + const void* id; +#if DEBUG_REFS_CALLSTACK_ENABLED + CallStack stack; +#endif + int32_t ref; + }; + + void addRef(ref_entry** refs, const void* id, int32_t mRef) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + ref_entry* ref = new ref_entry; + // Reference count at the time of the snapshot, but before the + // update. Positive value means we increment, negative--we + // decrement the reference count. + ref->ref = mRef; + ref->id = id; +#if DEBUG_REFS_CALLSTACK_ENABLED + ref->stack.update(2); +#endif + + ref->next = *refs; + *refs = ref; + } + } + + void removeRef(ref_entry** refs, const void* id) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + + ref_entry* ref = *refs; + while (ref != NULL) { + if (ref->id == id) { + *refs = ref->next; + delete ref; + return; + } + + refs = &ref->next; + ref = *refs; + } + + LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!", + id, mBase, this); + } + } + + void printRefsLocked(String8* out, const ref_entry* refs) const + { + char buf[128]; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + sprintf(buf, "\t%c ID %p (ref %d):\n", + inc, refs->id, refs->ref); + out->append(buf); +#if DEBUG_REFS_CALLSTACK_ENABLED + out->append(refs->stack.toString("\t\t")); +#else + out->append("\t\t(call stacks disabled)"); +#endif + refs = refs->next; + } + } + + Mutex mMutex; + ref_entry* mStrongRefs; + ref_entry* mWeakRefs; + + bool mTrackEnabled; + // Collect stack traces on addref and removeref, instead of deleting the stack references + // on removeref that match the address ones. + bool mRetain; + +#if 0 + void addRef(KeyedVector* refs, const void* id) + { + AutoMutex _l(mMutex); + ssize_t i = refs->indexOfKey(id); + if (i >= 0) { + ++(refs->editValueAt(i)); + } else { + i = refs->add(id, 1); + } + } + + void removeRef(KeyedVector* refs, const void* id) + { + AutoMutex _l(mMutex); + ssize_t i = refs->indexOfKey(id); + LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id); + if (i >= 0) { + int32_t val = --(refs->editValueAt(i)); + if (val == 0) { + refs->removeItemsAt(i); + } + } + } + + void printRefs(const KeyedVector& refs) + { + const size_t N=refs.size(); + for (size_t i=0; i mStrongRefs; + KeyedVector mWeakRefs; +#endif + +#endif +}; + +// --------------------------------------------------------------------------- + +void RefBase::incStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->addWeakRef(id); + refs->incWeak(id); + + refs->addStrongRef(id); + const int32_t c = android_atomic_inc(&refs->mStrong); + LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); +#if PRINT_REFS + LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + if (c != INITIAL_STRONG_VALUE) { + return; + } + + android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); + const_cast(this)->onFirstRef(); +} + +void RefBase::decStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->removeStrongRef(id); + const int32_t c = android_atomic_dec(&refs->mStrong); +#if PRINT_REFS + LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); + if (c == 1) { + const_cast(this)->onLastStrongRef(id); + if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { + delete this; + } + } + refs->removeWeakRef(id); + refs->decWeak(id); +} + +void RefBase::forceIncStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->addWeakRef(id); + refs->incWeak(id); + + refs->addStrongRef(id); + const int32_t c = android_atomic_inc(&refs->mStrong); + LOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow", + refs); +#if PRINT_REFS + LOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + + switch (c) { + case INITIAL_STRONG_VALUE: + android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); + // fall through... + case 0: + const_cast(this)->onFirstRef(); + } +} + +int32_t RefBase::getStrongCount() const +{ + return mRefs->mStrong; +} + + + +RefBase* RefBase::weakref_type::refBase() const +{ + return static_cast(this)->mBase; +} + +void RefBase::weakref_type::incWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + impl->addWeakRef(id); + const int32_t c = android_atomic_inc(&impl->mWeak); + LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); +} + +void RefBase::weakref_type::decWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + impl->removeWeakRef(id); + const int32_t c = android_atomic_dec(&impl->mWeak); + LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); + if (c != 1) return; + + if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { + if (impl->mStrong == INITIAL_STRONG_VALUE) + delete impl->mBase; + else { +// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); + delete impl; + } + } else { + impl->mBase->onLastWeakRef(id); + if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { + delete impl->mBase; + } + } +} + +bool RefBase::weakref_type::attemptIncStrong(const void* id) +{ + incWeak(id); + + weakref_impl* const impl = static_cast(this); + + int32_t curCount = impl->mStrong; + LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow", + this); + while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { + if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { + break; + } + curCount = impl->mStrong; + } + + if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { + bool allow; + if (curCount == INITIAL_STRONG_VALUE) { + // Attempting to acquire first strong reference... this is allowed + // if the object does NOT have a longer lifetime (meaning the + // implementation doesn't need to see this), or if the implementation + // allows it to happen. + allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK + || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + } else { + // Attempting to revive the object... this is allowed + // if the object DOES have a longer lifetime (so we can safely + // call the object with only a weak ref) and the implementation + // allows it to happen. + allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK + && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + } + if (!allow) { + decWeak(id); + return false; + } + curCount = android_atomic_inc(&impl->mStrong); + + // If the strong reference count has already been incremented by + // someone else, the implementor of onIncStrongAttempted() is holding + // an unneeded reference. So call onLastStrongRef() here to remove it. + // (No, this is not pretty.) Note that we MUST NOT do this if we + // are in fact acquiring the first reference. + if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) { + impl->mBase->onLastStrongRef(id); + } + } + + impl->addWeakRef(id); + impl->addStrongRef(id); + +#if PRINT_REFS + LOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); +#endif + + if (curCount == INITIAL_STRONG_VALUE) { + android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); + impl->mBase->onFirstRef(); + } + + return true; +} + +bool RefBase::weakref_type::attemptIncWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + + int32_t curCount = impl->mWeak; + LOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", + this); + while (curCount > 0) { + if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mWeak) == 0) { + break; + } + curCount = impl->mWeak; + } + + if (curCount > 0) { + impl->addWeakRef(id); + } + + return curCount > 0; +} + +int32_t RefBase::weakref_type::getWeakCount() const +{ + return static_cast(this)->mWeak; +} + +void RefBase::weakref_type::printRefs() const +{ + static_cast(this)->printRefs(); +} + +void RefBase::weakref_type::trackMe(bool enable, bool retain) +{ + static_cast(this)->trackMe(enable, retain); +} + +RefBase::weakref_type* RefBase::createWeak(const void* id) const +{ + mRefs->incWeak(id); + return mRefs; +} + +RefBase::weakref_type* RefBase::getWeakRefs() const +{ + return mRefs; +} + +RefBase::RefBase() + : mRefs(new weakref_impl(this)) +{ +// LOGV("Creating refs %p with RefBase %p\n", mRefs, this); +} + +RefBase::~RefBase() +{ +// LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs); + if (mRefs->mWeak == 0) { +// LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this); + delete mRefs; + } +} + +void RefBase::extendObjectLifetime(int32_t mode) +{ + android_atomic_or(mode, &mRefs->mFlags); +} + +void RefBase::onFirstRef() +{ +} + +void RefBase::onLastStrongRef(const void* /*id*/) +{ +} + +bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) +{ + return (flags&FIRST_INC_STRONG) ? true : false; +} + +void RefBase::onLastWeakRef(const void* /*id*/) +{ +} + +}; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp new file mode 100644 index 000000000..71e7cd722 --- /dev/null +++ b/libs/utils/ResourceTypes.cpp @@ -0,0 +1,3983 @@ +/* + * Copyright (C) 2008 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. + */ + +#define LOG_TAG "ResourceType" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef INT32_MAX +#define INT32_MAX ((int32_t)(2147483647)) +#endif + +#define POOL_NOISY(x) //x +#define XML_NOISY(x) //x +#define TABLE_NOISY(x) //x +#define TABLE_GETENTRY(x) //x +#define TABLE_SUPER_NOISY(x) //x +#define LOAD_TABLE_NOISY(x) //x + +namespace android { + +#ifdef HAVE_WINSOCK +#undef nhtol +#undef htonl + +#ifdef HAVE_LITTLE_ENDIAN +#define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +#define htonl(x) ntohl(x) +#define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +#define htons(x) ntohs(x) +#else +#define ntohl(x) (x) +#define htonl(x) (x) +#define ntohs(x) (x) +#define htons(x) (x) +#endif +#endif + +static void printToLogFunc(void* cookie, const char* txt) +{ + LOGV("%s", txt); +} + +// Standard C isspace() is only required to look at the low byte of its input, so +// produces incorrect results for UTF-16 characters. For safety's sake, assume that +// any high-byte UTF-16 code point is not whitespace. +inline int isspace16(char16_t c) { + return (c < 0x0080 && isspace(c)); +} + +// range checked; guaranteed to NUL-terminate within the stated number of available slots +// NOTE: if this truncates the dst string due to running out of space, no attempt is +// made to avoid splitting surrogate pairs. +static void strcpy16_dtoh(uint16_t* dst, const uint16_t* src, size_t avail) +{ + uint16_t* last = dst + avail - 1; + while (*src && (dst < last)) { + char16_t s = dtohs(*src); + *dst++ = s; + src++; + } + *dst = 0; +} + +static status_t validate_chunk(const ResChunk_header* chunk, + size_t minSize, + const uint8_t* dataEnd, + const char* name) +{ + const uint16_t headerSize = dtohs(chunk->headerSize); + const uint32_t size = dtohl(chunk->size); + + if (headerSize >= minSize) { + if (headerSize <= size) { + if (((headerSize|size)&0x3) == 0) { + if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) { + return NO_ERROR; + } + LOGW("%s data size %p extends beyond resource end %p.", + name, (void*)size, + (void*)(dataEnd-((const uint8_t*)chunk))); + return BAD_TYPE; + } + LOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", + name, (int)size, (int)headerSize); + return BAD_TYPE; + } + LOGW("%s size %p is smaller than header size %p.", + name, (void*)size, (void*)(int)headerSize); + return BAD_TYPE; + } + LOGW("%s header size %p is too small.", + name, (void*)(int)headerSize); + return BAD_TYPE; +} + +inline void Res_value::copyFrom_dtoh(const Res_value& src) +{ + size = dtohs(src.size); + res0 = src.res0; + dataType = src.dataType; + data = dtohl(src.data); +} + +void Res_png_9patch::deviceToFile() +{ + for (int i = 0; i < numXDivs; i++) { + xDivs[i] = htonl(xDivs[i]); + } + for (int i = 0; i < numYDivs; i++) { + yDivs[i] = htonl(yDivs[i]); + } + paddingLeft = htonl(paddingLeft); + paddingRight = htonl(paddingRight); + paddingTop = htonl(paddingTop); + paddingBottom = htonl(paddingBottom); + for (int i=0; ixDivs, numXDivs * sizeof(int32_t)); + data += numXDivs * sizeof(int32_t); + memmove(data, this->yDivs, numYDivs * sizeof(int32_t)); + data += numYDivs * sizeof(int32_t); + memmove(data, this->colors, numColors * sizeof(uint32_t)); +} + +static void deserializeInternal(const void* inData, Res_png_9patch* outData) { + char* patch = (char*) inData; + if (inData != outData) { + memmove(&outData->wasDeserialized, patch, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + memmove(&outData->paddingLeft, patch + 12, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + } + outData->wasDeserialized = true; + char* data = (char*)outData; + data += sizeof(Res_png_9patch); + outData->xDivs = (int32_t*) data; + data += outData->numXDivs * sizeof(int32_t); + outData->yDivs = (int32_t*) data; + data += outData->numYDivs * sizeof(int32_t); + outData->colors = (uint32_t*) data; +} + +Res_png_9patch* Res_png_9patch::deserialize(const void* inData) +{ + if (sizeof(void*) != sizeof(int32_t)) { + LOGE("Cannot deserialize on non 32-bit system\n"); + return NULL; + } + deserializeInternal(inData, (Res_png_9patch*) inData); + return (Res_png_9patch*) inData; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +ResStringPool::ResStringPool() + : mError(NO_INIT), mOwnedData(NULL) +{ +} + +ResStringPool::ResStringPool(const void* data, size_t size, bool copyData) + : mError(NO_INIT), mOwnedData(NULL) +{ + setTo(data, size, copyData); +} + +ResStringPool::~ResStringPool() +{ + uninit(); +} + +status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) +{ + if (!data || !size) { + return (mError=BAD_TYPE); + } + + uninit(); + + const bool notDeviceEndian = htods(0xf0) != 0xf0; + + if (copyData || notDeviceEndian) { + mOwnedData = malloc(size); + if (mOwnedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(mOwnedData, data, size); + data = mOwnedData; + } + + mHeader = (const ResStringPool_header*)data; + + if (notDeviceEndian) { + ResStringPool_header* h = const_cast(mHeader); + h->header.headerSize = dtohs(mHeader->header.headerSize); + h->header.type = dtohs(mHeader->header.type); + h->header.size = dtohl(mHeader->header.size); + h->stringCount = dtohl(mHeader->stringCount); + h->styleCount = dtohl(mHeader->styleCount); + h->flags = dtohl(mHeader->flags); + h->stringsStart = dtohl(mHeader->stringsStart); + h->stylesStart = dtohl(mHeader->stylesStart); + } + + if (mHeader->header.headerSize > mHeader->header.size + || mHeader->header.size > size) { + LOGW("Bad string block: header size %d or total size %d is larger than data size %d\n", + (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size); + return (mError=BAD_TYPE); + } + mSize = mHeader->header.size; + mEntries = (const uint32_t*) + (((const uint8_t*)data)+mHeader->header.headerSize); + + if (mHeader->stringCount > 0) { + if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? + || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))) + > size) { + LOGW("Bad string block: entry of %d items extends past data size %d\n", + (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))), + (int)size); + return (mError=BAD_TYPE); + } + mStrings = (const char16_t*) + (((const uint8_t*)data)+mHeader->stringsStart); + if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) { + LOGW("Bad string block: string pool starts at %d, after total size %d\n", + (int)mHeader->stringsStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } + if (mHeader->styleCount == 0) { + mStringPoolSize = + (mHeader->header.size-mHeader->stringsStart)/sizeof(uint16_t); + } else { + // check invariant: styles follow the strings + if (mHeader->stylesStart <= mHeader->stringsStart) { + LOGW("Bad style block: style block starts at %d, before strings at %d\n", + (int)mHeader->stylesStart, (int)mHeader->stringsStart); + return (mError=BAD_TYPE); + } + mStringPoolSize = + (mHeader->stylesStart-mHeader->stringsStart)/sizeof(uint16_t); + } + + // check invariant: stringCount > 0 requires a string pool to exist + if (mStringPoolSize == 0) { + LOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); + return (mError=BAD_TYPE); + } + + if (notDeviceEndian) { + size_t i; + uint32_t* e = const_cast(mEntries); + for (i=0; istringCount; i++) { + e[i] = dtohl(mEntries[i]); + } + char16_t* s = const_cast(mStrings); + for (i=0; istyleCount > 0) { + mEntryStyles = mEntries + mHeader->stringCount; + // invariant: integer overflow in calculating mEntryStyles + if (mEntryStyles < mEntries) { + LOGW("Bad string block: integer overflow finding styles\n"); + return (mError=BAD_TYPE); + } + + if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { + LOGW("Bad string block: entry of %d styles extends past data size %d\n", + (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), + (int)size); + return (mError=BAD_TYPE); + } + mStyles = (const uint32_t*) + (((const uint8_t*)data)+mHeader->stylesStart); + if (mHeader->stylesStart >= mHeader->header.size) { + LOGW("Bad string block: style pool starts %d, after total size %d\n", + (int)mHeader->stylesStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } + mStylePoolSize = + (mHeader->header.size-mHeader->stylesStart)/sizeof(uint32_t); + + if (notDeviceEndian) { + size_t i; + uint32_t* e = const_cast(mEntryStyles); + for (i=0; istyleCount; i++) { + e[i] = dtohl(mEntryStyles[i]); + } + uint32_t* s = const_cast(mStyles); + for (i=0; istringCount) { + const uint32_t off = (mEntries[idx]/sizeof(uint16_t)); + if (off < (mStringPoolSize-1)) { + const char16_t* str = mStrings+off; + *outLen = *str; + if ((*str)&0x8000) { + str++; + *outLen = (((*outLen)&0x7fff)<<16) + *str; + } + if ((uint32_t)(str+1+*outLen-mStrings) < mStringPoolSize) { + return str+1; + } else { + LOGW("Bad string block: string #%d extends to %d, past end at %d\n", + (int)idx, (int)(str+1+*outLen-mStrings), (int)mStringPoolSize); + } + } else { + LOGW("Bad string block: string #%d entry is at %d, past end at %d\n", + (int)idx, (int)(off*sizeof(uint16_t)), + (int)(mStringPoolSize*sizeof(uint16_t))); + } + } + return NULL; +} + +const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const +{ + return styleAt(ref.index); +} + +const ResStringPool_span* ResStringPool::styleAt(size_t idx) const +{ + if (mError == NO_ERROR && idx < mHeader->styleCount) { + const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t)); + if (off < mStylePoolSize) { + return (const ResStringPool_span*)(mStyles+off); + } else { + LOGW("Bad string block: style #%d entry is at %d, past end at %d\n", + (int)idx, (int)(off*sizeof(uint32_t)), + (int)(mStylePoolSize*sizeof(uint32_t))); + } + } + return NULL; +} + +ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const +{ + if (mError != NO_ERROR) { + return mError; + } + + size_t len; + + if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { + // Do a binary search for the string... + ssize_t l = 0; + ssize_t h = mHeader->stringCount-1; + + ssize_t mid; + while (l <= h) { + mid = l + (h - l)/2; + const char16_t* s = stringAt(mid, &len); + int c = s ? strzcmp16(s, len, str, strLen) : -1; + POOL_NOISY(printf("Looking for %s, at %s, cmp=%d, l/mid/h=%d/%d/%d\n", + String8(str).string(), + String8(s).string(), + c, (int)l, (int)mid, (int)h)); + if (c == 0) { + return mid; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + } else { + // It is unusual to get the ID from an unsorted string block... + // most often this happens because we want to get IDs for style + // span tags; since those always appear at the end of the string + // block, start searching at the back. + for (int i=mHeader->stringCount-1; i>=0; i--) { + const char16_t* s = stringAt(i, &len); + POOL_NOISY(printf("Looking for %s, at %s, i=%d\n", + String8(str, strLen).string(), + String8(s).string(), + i)); + if (s && strzcmp16(s, len, str, strLen) == 0) { + return i; + } + } + } + + return NAME_NOT_FOUND; +} + +size_t ResStringPool::size() const +{ + return (mError == NO_ERROR) ? mHeader->stringCount : 0; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +ResXMLParser::ResXMLParser(const ResXMLTree& tree) + : mTree(tree), mEventCode(BAD_DOCUMENT) +{ +} + +void ResXMLParser::restart() +{ + mCurNode = NULL; + mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; +} + +ResXMLParser::event_code_t ResXMLParser::getEventType() const +{ + return mEventCode; +} + +ResXMLParser::event_code_t ResXMLParser::next() +{ + if (mEventCode == START_DOCUMENT) { + mCurNode = mTree.mRootNode; + mCurExt = mTree.mRootExt; + return (mEventCode=mTree.mRootCode); + } else if (mEventCode >= FIRST_CHUNK_CODE) { + return nextNode(); + } + return mEventCode; +} + +const int32_t ResXMLParser::getCommentID() const +{ + return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1; +} + +const uint16_t* ResXMLParser::getComment(size_t* outLen) const +{ + int32_t id = getCommentID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const uint32_t ResXMLParser::getLineNumber() const +{ + return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1; +} + +const int32_t ResXMLParser::getTextID() const +{ + if (mEventCode == TEXT) { + return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getText(size_t* outLen) const +{ + int32_t id = getTextID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +ssize_t ResXMLParser::getTextValue(Res_value* outValue) const +{ + if (mEventCode == TEXT) { + outValue->copyFrom_dtoh(((const ResXMLTree_cdataExt*)mCurExt)->typedData); + return sizeof(Res_value); + } + return BAD_TYPE; +} + +const int32_t ResXMLParser::getNamespacePrefixID() const +{ + if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { + return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const +{ + int32_t id = getNamespacePrefixID(); + //printf("prefix=%d event=%p\n", id, mEventCode); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getNamespaceUriID() const +{ + if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { + return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const +{ + int32_t id = getNamespaceUriID(); + //printf("uri=%d event=%p\n", id, mEventCode); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getElementNamespaceID() const +{ + if (mEventCode == START_TAG) { + return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index); + } + if (mEventCode == END_TAG) { + return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->ns.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const +{ + int32_t id = getElementNamespaceID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getElementNameID() const +{ + if (mEventCode == START_TAG) { + return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index); + } + if (mEventCode == END_TAG) { + return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->name.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getElementName(size_t* outLen) const +{ + int32_t id = getElementNameID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +size_t ResXMLParser::getAttributeCount() const +{ + if (mEventCode == START_TAG) { + return dtohs(((const ResXMLTree_attrExt*)mCurExt)->attributeCount); + } + return 0; +} + +const int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->ns.index); + } + } + return -2; +} + +const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNamespaceID(idx); + //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getAttributeNameID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->name.index); + } + } + return -1; +} + +const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNameID(idx); + //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const +{ + int32_t id = getAttributeNameID(idx); + if (id >= 0 && (size_t)id < mTree.mNumResIds) { + return dtohl(mTree.mResIds[id]); + } + return 0; +} + +const int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->rawValue.index); + } + } + return -1; +} + +const uint16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeValueStringID(idx); + //XML_NOISY(printf("getAttributeValue 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +int32_t ResXMLParser::getAttributeDataType(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return attr->typedValue.dataType; + } + } + return Res_value::TYPE_NULL; +} + +int32_t ResXMLParser::getAttributeData(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->typedValue.data); + } + } + return 0; +} + +ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + outValue->copyFrom_dtoh(attr->typedValue); + return sizeof(Res_value); + } + } + return BAD_TYPE; +} + +ssize_t ResXMLParser::indexOfAttribute(const char* ns, const char* attr) const +{ + String16 nsStr(ns != NULL ? ns : ""); + String16 attrStr(attr); + return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0, + attrStr.string(), attrStr.size()); +} + +ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen, + const char16_t* attr, size_t attrLen) const +{ + if (mEventCode == START_TAG) { + const size_t N = getAttributeCount(); + for (size_t i=0; i attr=%s, curAttr=%s\n", + // String8(attr).string(), String8(curAttr).string()); + if (attr && curAttr && (strzcmp16(attr, attrLen, curAttr, curAttrLen) == 0)) { + if (ns == NULL) { + if (curNs == NULL) return i; + } else if (curNs != NULL) { + //printf(" --> ns=%s, curNs=%s\n", + // String8(ns).string(), String8(curNs).string()); + if (strzcmp16(ns, nsLen, curNs, curNsLen) == 0) return i; + } + } + } + } + + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfID() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->idIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfClass() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->classIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfStyle() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->styleIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ResXMLParser::event_code_t ResXMLParser::nextNode() +{ + if (mEventCode < 0) { + return mEventCode; + } + + do { + const ResXMLTree_node* next = (const ResXMLTree_node*) + (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size)); + //LOGW("Next node: prev=%p, next=%p\n", mCurNode, next); + + if (((const uint8_t*)next) >= mTree.mDataEnd) { + mCurNode = NULL; + return (mEventCode=END_DOCUMENT); + } + + if (mTree.validateNode(next) != NO_ERROR) { + mCurNode = NULL; + return (mEventCode=BAD_DOCUMENT); + } + + mCurNode = next; + const uint16_t headerSize = dtohs(next->header.headerSize); + const uint32_t totalSize = dtohl(next->header.size); + mCurExt = ((const uint8_t*)next) + headerSize; + size_t minExtSize = 0; + event_code_t eventCode = (event_code_t)dtohs(next->header.type); + switch ((mEventCode=eventCode)) { + case RES_XML_START_NAMESPACE_TYPE: + case RES_XML_END_NAMESPACE_TYPE: + minExtSize = sizeof(ResXMLTree_namespaceExt); + break; + case RES_XML_START_ELEMENT_TYPE: + minExtSize = sizeof(ResXMLTree_attrExt); + break; + case RES_XML_END_ELEMENT_TYPE: + minExtSize = sizeof(ResXMLTree_endElementExt); + break; + case RES_XML_CDATA_TYPE: + minExtSize = sizeof(ResXMLTree_cdataExt); + break; + default: + LOGW("Unknown XML block: header type %d in node at %d\n", + (int)dtohs(next->header.type), + (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader))); + continue; + } + + if ((totalSize-headerSize) < minExtSize) { + LOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n", + (int)dtohs(next->header.type), + (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)), + (int)(totalSize-headerSize), (int)minExtSize); + return (mEventCode=BAD_DOCUMENT); + } + + //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n", + // mCurNode, mCurExt, headerSize, minExtSize); + + return eventCode; + } while (true); +} + +void ResXMLParser::getPosition(ResXMLParser::ResXMLPosition* pos) const +{ + pos->eventCode = mEventCode; + pos->curNode = mCurNode; + pos->curExt = mCurExt; +} + +void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos) +{ + mEventCode = pos.eventCode; + mCurNode = pos.curNode; + mCurExt = pos.curExt; +} + + +// -------------------------------------------------------------------- + +static volatile int32_t gCount = 0; + +ResXMLTree::ResXMLTree() + : ResXMLParser(*this) + , mError(NO_INIT), mOwnedData(NULL) +{ + //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + restart(); +} + +ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData) + : ResXMLParser(*this) + , mError(NO_INIT), mOwnedData(NULL) +{ + //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + setTo(data, size, copyData); +} + +ResXMLTree::~ResXMLTree() +{ + //LOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); + uninit(); +} + +status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) +{ + uninit(); + mEventCode = START_DOCUMENT; + + if (copyData) { + mOwnedData = malloc(size); + if (mOwnedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(mOwnedData, data, size); + data = mOwnedData; + } + + mHeader = (const ResXMLTree_header*)data; + mSize = dtohl(mHeader->header.size); + if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) { + LOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n", + (int)dtohs(mHeader->header.headerSize), + (int)dtohl(mHeader->header.size), (int)size); + mError = BAD_TYPE; + restart(); + return mError; + } + mDataEnd = ((const uint8_t*)mHeader) + mSize; + + mStrings.uninit(); + mRootNode = NULL; + mResIds = NULL; + mNumResIds = 0; + + // First look for a couple interesting chunks: the string block + // and first XML node. + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize)); + const ResChunk_header* lastChunk = chunk; + while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML"); + if (err != NO_ERROR) { + mError = err; + goto done; + } + const uint16_t type = dtohs(chunk->type); + const size_t size = dtohl(chunk->size); + XML_NOISY(printf("Scanning @ %p: type=0x%x, size=0x%x\n", + (void*)(((uint32_t)chunk)-((uint32_t)mHeader)), type, size)); + if (type == RES_STRING_POOL_TYPE) { + mStrings.setTo(chunk, size); + } else if (type == RES_XML_RESOURCE_MAP_TYPE) { + mResIds = (const uint32_t*) + (((const uint8_t*)chunk)+dtohs(chunk->headerSize)); + mNumResIds = (dtohl(chunk->size)-dtohs(chunk->headerSize))/sizeof(uint32_t); + } else if (type >= RES_XML_FIRST_CHUNK_TYPE + && type <= RES_XML_LAST_CHUNK_TYPE) { + if (validateNode((const ResXMLTree_node*)chunk) != NO_ERROR) { + mError = BAD_TYPE; + goto done; + } + mCurNode = (const ResXMLTree_node*)lastChunk; + if (nextNode() == BAD_DOCUMENT) { + mError = BAD_TYPE; + goto done; + } + mRootNode = mCurNode; + mRootExt = mCurExt; + mRootCode = mEventCode; + break; + } else { + XML_NOISY(printf("Skipping unknown chunk!\n")); + } + lastChunk = chunk; + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + size); + } + + if (mRootNode == NULL) { + LOGW("Bad XML block: no root element node found\n"); + mError = BAD_TYPE; + goto done; + } + + mError = mStrings.getError(); + +done: + restart(); + return mError; +} + +status_t ResXMLTree::getError() const +{ + return mError; +} + +void ResXMLTree::uninit() +{ + mError = NO_INIT; + if (mOwnedData) { + free(mOwnedData); + mOwnedData = NULL; + } + restart(); +} + +const ResStringPool& ResXMLTree::getStrings() const +{ + return mStrings; +} + +status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const +{ + const uint16_t eventCode = dtohs(node->header.type); + + status_t err = validate_chunk( + &node->header, sizeof(ResXMLTree_node), + mDataEnd, "ResXMLTree_node"); + + if (err >= NO_ERROR) { + // Only perform additional validation on START nodes + if (eventCode != RES_XML_START_ELEMENT_TYPE) { + return NO_ERROR; + } + + const uint16_t headerSize = dtohs(node->header.headerSize); + const uint32_t size = dtohl(node->header.size); + const ResXMLTree_attrExt* attrExt = (const ResXMLTree_attrExt*) + (((const uint8_t*)node) + headerSize); + // check for sensical values pulled out of the stream so far... + if ((size >= headerSize + sizeof(ResXMLTree_attrExt)) + && ((void*)attrExt > (void*)node)) { + const size_t attrSize = ((size_t)dtohs(attrExt->attributeSize)) + * dtohs(attrExt->attributeCount); + if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) { + return NO_ERROR; + } + LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", + (unsigned int)(dtohs(attrExt->attributeStart)+attrSize), + (unsigned int)(size-headerSize)); + } + else { + LOGW("Bad XML start block: node header size 0x%x, size 0x%x\n", + (unsigned int)headerSize, (unsigned int)size); + } + return BAD_TYPE; + } + + return err; + +#if 0 + const bool isStart = dtohs(node->header.type) == RES_XML_START_ELEMENT_TYPE; + + const uint16_t headerSize = dtohs(node->header.headerSize); + const uint32_t size = dtohl(node->header.size); + + if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) { + if (size >= headerSize) { + if (((const uint8_t*)node) <= (mDataEnd-size)) { + if (!isStart) { + return NO_ERROR; + } + if ((((size_t)dtohs(node->attributeSize))*dtohs(node->attributeCount)) + <= (size-headerSize)) { + return NO_ERROR; + } + LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", + ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount), + (int)(size-headerSize)); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), + (int)headerSize, (int)size); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x header size 0x%x too small\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), + (int)headerSize); + return BAD_TYPE; +#endif +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +struct ResTable::Header +{ + Header() : ownedData(NULL), header(NULL) { } + + void* ownedData; + const ResTable_header* header; + size_t size; + const uint8_t* dataEnd; + size_t index; + void* cookie; + + ResStringPool values; +}; + +struct ResTable::Type +{ + Type(const Header* _header, const Package* _package, size_t count) + : header(_header), package(_package), entryCount(count), + typeSpec(NULL), typeSpecFlags(NULL) { } + const Header* const header; + const Package* const package; + const size_t entryCount; + const ResTable_typeSpec* typeSpec; + const uint32_t* typeSpecFlags; + Vector configs; +}; + +struct ResTable::Package +{ + Package(const Header* _header, const ResTable_package* _package) + : header(_header), package(_package) { } + ~Package() + { + size_t i = types.size(); + while (i > 0) { + i--; + delete types[i]; + } + } + + const Header* const header; + const ResTable_package* const package; + Vector types; + + const Type* getType(size_t idx) const { + return idx < types.size() ? types[idx] : NULL; + } +}; + +// A group of objects describing a particular resource package. +// The first in 'package' is always the root object (from the resource +// table that defined the package); the ones after are skins on top of it. +struct ResTable::PackageGroup +{ + PackageGroup(const String16& _name, uint32_t _id) + : name(_name), id(_id), typeCount(0), bags(NULL) { } + ~PackageGroup() { + clearBagCache(); + const size_t N = packages.size(); + for (size_t i=0; igetType(i); + if (type != NULL) { + bag_set** typeBags = bags[i]; + TABLE_NOISY(printf("typeBags=%p\n", typeBags)); + if (typeBags) { + TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount)); + const size_t N = type->entryCount; + for (size_t j=0; j packages; + + // Taken from the root package. + ResStringPool typeStrings; + ResStringPool keyStrings; + size_t typeCount; + + // Computed attribute bags, first indexed by the type and second + // by the entry in that type. + bag_set*** bags; +}; + +struct ResTable::bag_set +{ + size_t numAttrs; // number in array + size_t availAttrs; // total space in array + uint32_t typeSpecFlags; + // Followed by 'numAttr' bag_entry structures. +}; + +ResTable::Theme::Theme(const ResTable& table) + : mTable(table) +{ + memset(mPackages, 0, sizeof(mPackages)); +} + +ResTable::Theme::~Theme() +{ + for (size_t i=0; inumTypes; j++) { + theme_entry* te = pi->types[j].entries; + if (te != NULL) { + free(te); + } + } + free(pi); +} + +ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) +{ + package_info* newpi = (package_info*)malloc( + sizeof(package_info) + (pi->numTypes*sizeof(type_info))); + newpi->numTypes = pi->numTypes; + for (size_t j=0; jnumTypes; j++) { + size_t cnt = pi->types[j].numEntries; + newpi->types[j].numEntries = cnt; + theme_entry* te = pi->types[j].entries; + if (te != NULL) { + theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry)); + newpi->types[j].entries = newte; + memcpy(newte, te, cnt*sizeof(theme_entry)); + } else { + newpi->types[j].entries = NULL; + } + } + return newpi; +} + +status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) +{ + const bag_entry* bag; + uint32_t bagTypeSpecFlags = 0; + mTable.lock(); + const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags); + TABLE_NOISY(LOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N)); + if (N < 0) { + mTable.unlock(); + return N; + } + + uint32_t curPackage = 0xffffffff; + ssize_t curPackageIndex = 0; + package_info* curPI = NULL; + uint32_t curType = 0xffffffff; + size_t numEntries = 0; + theme_entry* curEntries = NULL; + + const bag_entry* end = bag + N; + while (bag < end) { + const uint32_t attrRes = bag->map.name.ident; + const uint32_t p = Res_GETPACKAGE(attrRes); + const uint32_t t = Res_GETTYPE(attrRes); + const uint32_t e = Res_GETENTRY(attrRes); + + if (curPackage != p) { + const ssize_t pidx = mTable.getResourcePackageIndex(attrRes); + if (pidx < 0) { + LOGE("Style contains key with bad package: 0x%08x\n", attrRes); + bag++; + continue; + } + curPackage = p; + curPackageIndex = pidx; + curPI = mPackages[pidx]; + if (curPI == NULL) { + PackageGroup* const grp = mTable.mPackageGroups[pidx]; + int cnt = grp->typeCount; + curPI = (package_info*)malloc( + sizeof(package_info) + (cnt*sizeof(type_info))); + curPI->numTypes = cnt; + memset(curPI->types, 0, cnt*sizeof(type_info)); + mPackages[pidx] = curPI; + } + curType = 0xffffffff; + } + if (curType != t) { + if (t >= curPI->numTypes) { + LOGE("Style contains key with bad type: 0x%08x\n", attrRes); + bag++; + continue; + } + curType = t; + curEntries = curPI->types[t].entries; + if (curEntries == NULL) { + PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; + const Type* type = grp->packages[0]->getType(t); + int cnt = type != NULL ? type->entryCount : 0; + curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); + memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); + curPI->types[t].numEntries = cnt; + curPI->types[t].entries = curEntries; + } + numEntries = curPI->types[t].numEntries; + } + if (e >= numEntries) { + LOGE("Style contains key with bad entry: 0x%08x\n", attrRes); + bag++; + continue; + } + theme_entry* curEntry = curEntries + e; + TABLE_NOISY(LOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", + attrRes, bag->map.value.dataType, bag->map.value.data, + curEntry->value.dataType)); + if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { + curEntry->stringBlock = bag->stringBlock; + curEntry->typeSpecFlags |= bagTypeSpecFlags; + curEntry->value = bag->map.value; + } + + bag++; + } + + mTable.unlock(); + + //LOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); + //dumpToLog(); + + return NO_ERROR; +} + +status_t ResTable::Theme::setTo(const Theme& other) +{ + //LOGI("Setting theme %p from theme %p...\n", this, &other); + //dumpToLog(); + //other.dumpToLog(); + + if (&mTable == &other.mTable) { + for (size_t i=0; i= 0) { + const package_info* const pi = mPackages[p]; + if (pi != NULL) { + if (t < pi->numTypes) { + const type_info& ti = pi->types[t]; + if (e < ti.numEntries) { + const theme_entry& te = ti.entries[e]; + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags |= te.typeSpecFlags; + } + const uint8_t type = te.value.dataType; + if (type == Res_value::TYPE_ATTRIBUTE) { + if (cnt > 0) { + cnt--; + resID = te.value.data; + continue; + } + LOGW("Too many attribute references, stopped at: 0x%08x\n", resID); + return BAD_INDEX; + } else if (type != Res_value::TYPE_NULL) { + *outValue = te.value; + return te.stringBlock; + } + return BAD_INDEX; + } + } + } + } + break; + + } while (true); + + return BAD_INDEX; +} + +ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, + ssize_t blockIndex, uint32_t* outLastRef, + uint32_t* inoutTypeSpecFlags) const +{ + //printf("Resolving type=0x%x\n", inOutValue->dataType); + if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { + uint32_t newTypeSpecFlags; + blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags); + if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags; + //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType); + if (blockIndex < 0) { + return blockIndex; + } + } + return mTable.resolveReference(inOutValue, blockIndex, outLastRef); +} + +void ResTable::Theme::dumpToLog() const +{ + LOGI("Theme %p:\n", this); + for (size_t i=0; inumTypes; j++) { + type_info& ti = pi->types[j]; + if (ti.numEntries == 0) continue; + + LOGI(" Type #0x%02x:\n", (int)(j+1)); + for (size_t k=0; kgetBuffer(true); + if (data == NULL) { + LOGW("Unable to get buffer of resource asset file"); + return UNKNOWN_ERROR; + } + size_t size = (size_t)asset->getLength(); + return add(data, size, cookie, asset, copyData); +} + +status_t ResTable::add(const void* data, size_t size, void* cookie, + Asset* asset, bool copyData) +{ + if (!data) return NO_ERROR; + Header* header = new Header; + header->index = mHeaders.size(); + header->cookie = cookie; + mHeaders.add(header); + + const bool notDeviceEndian = htods(0xf0) != 0xf0; + + LOAD_TABLE_NOISY( + LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d\n", + data, size, cookie, asset, copyData)); + + if (copyData || notDeviceEndian) { + header->ownedData = malloc(size); + if (header->ownedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(header->ownedData, data, size); + data = header->ownedData; + } + + header->header = (const ResTable_header*)data; + header->size = dtohl(header->header->header.size); + //LOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, + // dtohl(header->header->header.size), header->header->header.size); + LOAD_TABLE_NOISY(LOGV("Loading ResTable @%p:\n", header->header)); + LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256, + 16, 16, 0, false, printToLogFunc)); + if (dtohs(header->header->header.headerSize) > header->size + || header->size > size) { + LOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", + (int)dtohs(header->header->header.headerSize), + (int)header->size, (int)size); + return (mError=BAD_TYPE); + } + if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { + LOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n", + (int)dtohs(header->header->header.headerSize), + (int)header->size); + return (mError=BAD_TYPE); + } + header->dataEnd = ((const uint8_t*)header->header) + header->size; + + // Iterate through all chunks. + size_t curPackage = 0; + + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)header->header) + + dtohs(header->header->header.headerSize)); + while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), header->dataEnd, "ResTable"); + if (err != NO_ERROR) { + return (mError=err); + } + TABLE_NOISY(LOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", + dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); + const size_t csize = dtohl(chunk->size); + const uint16_t ctype = dtohs(chunk->type); + if (ctype == RES_STRING_POOL_TYPE) { + if (header->values.getError() != NO_ERROR) { + // Only use the first string chunk; ignore any others that + // may appear. + status_t err = header->values.setTo(chunk, csize); + if (err != NO_ERROR) { + return (mError=err); + } + } else { + LOGW("Multiple string chunks found in resource table."); + } + } else if (ctype == RES_TABLE_PACKAGE_TYPE) { + if (curPackage >= dtohl(header->header->packageCount)) { + LOGW("More package chunks were found than the %d declared in the header.", + dtohl(header->header->packageCount)); + return (mError=BAD_TYPE); + } + if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) { + return mError; + } + curPackage++; + } else { + LOGW("Unknown chunk type %p in table at %p.\n", + (void*)(int)(ctype), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))); + } + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + csize); + } + + if (curPackage < dtohl(header->header->packageCount)) { + LOGW("Fewer package chunks (%d) were found than the %d declared in the header.", + (int)curPackage, dtohl(header->header->packageCount)); + return (mError=BAD_TYPE); + } + mError = header->values.getError(); + if (mError != NO_ERROR) { + LOGW("No string values found in resource table!"); + } + TABLE_NOISY(LOGV("Returning from add with mError=%d\n", mError)); + return mError; +} + +status_t ResTable::getError() const +{ + return mError; +} + +void ResTable::uninit() +{ + mError = NO_INIT; + size_t N = mPackageGroups.size(); + for (size_t i=0; iownedData) { + free(header->ownedData); + } + delete header; + } + + mPackageGroups.clear(); + mHeaders.clear(); +} + +bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const +{ + if (mError != NO_ERROR) { + return false; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("No package identifier when getting name for resource number 0x%08x", resID); + return false; + } + if (t < 0) { + LOGW("No type identifier when getting name for resource number 0x%08x", resID); + return false; + } + + const PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting name for resource number 0x%08x", resID); + return false; + } + if (grp->packages.size() > 0) { + const Package* const package = grp->packages[0]; + + const ResTable_type* type; + const ResTable_entry* entry; + ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL); + if (offset <= 0) { + return false; + } + + outName->package = grp->name.string(); + outName->packageLen = grp->name.size(); + outName->type = grp->typeStrings.stringAt(t, &outName->typeLen); + outName->name = grp->keyStrings.stringAt( + dtohl(entry->key.index), &outName->nameLen); + return true; + } + + return false; +} + +ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, + uint32_t* outSpecFlags, ResTable_config* outConfig) const +{ + if (mError != NO_ERROR) { + return mError; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("No package identifier when getting value for resource number 0x%08x", resID); + return BAD_INDEX; + } + if (t < 0) { + LOGW("No type identifier when getting value for resource number 0x%08x", resID); + return BAD_INDEX; + } + + const Res_value* bestValue = NULL; + const Package* bestPackage = NULL; + ResTable_config bestItem; + memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up + + if (outSpecFlags != NULL) *outSpecFlags = 0; + + // Look through all resource packages, starting with the most + // recently added. + const PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting value for resource number 0x%08x", resID); + return false; + } + size_t ip = grp->packages.size(); + while (ip > 0) { + ip--; + + const Package* const package = grp->packages[ip]; + + const ResTable_type* type; + const ResTable_entry* entry; + const Type* typeClass; + ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + if (offset <= 0) { + if (offset < 0) { + LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %d: 0x%08x\n", + resID, t, e, (int)ip, (int)offset); + return offset; + } + continue; + } + + if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { + if (!mayBeBag) { + LOGW("Requesting resource %p failed because it is complex\n", + (void*)resID); + } + continue; + } + + TABLE_NOISY(aout << "Resource type data: " + << HexDump(type, dtohl(type->header.size)) << endl); + + if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { + LOGW("ResTable_item at %d is beyond type chunk data %d", + (int)offset, dtohl(type->header.size)); + return BAD_TYPE; + } + + const Res_value* item = + (const Res_value*)(((const uint8_t*)type) + offset); + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + + if (outSpecFlags != NULL) { + if (typeClass->typeSpecFlags != NULL) { + *outSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); + } else { + *outSpecFlags = -1; + } + } + + if (bestPackage != NULL && bestItem.isBetterThan(thisConfig)) { + continue; + } + + bestItem = thisConfig; + bestValue = item; + bestPackage = package; + } + + TABLE_NOISY(printf("Found result: package %p\n", bestPackage)); + + if (bestValue) { + outValue->size = dtohs(bestValue->size); + outValue->res0 = bestValue->res0; + outValue->dataType = bestValue->dataType; + outValue->data = dtohl(bestValue->data); + if (outConfig != NULL) { + *outConfig = bestItem; + } + TABLE_NOISY(size_t len; + printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", + bestPackage->header->index, + outValue->dataType, + outValue->dataType == bestValue->TYPE_STRING + ? String8(bestPackage->header->values.stringAt( + outValue->data, &len)).string() + : "", + outValue->data)); + return bestPackage->header->index; + } + + return BAD_INDEX; +} + +ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, + uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags) const +{ + int count=0; + while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE + && value->data != 0 && count < 20) { + if (outLastRef) *outLastRef = value->data; + uint32_t lastRef = value->data; + uint32_t newFlags = 0; + const ssize_t newIndex = getResource(value->data, value, true, &newFlags); + //LOGI("Resolving reference d=%p: newIndex=%d, t=0x%02x, d=%p\n", + // (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data); + //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); + if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags; + if (newIndex < 0) { + // This can fail if the resource being referenced is a style... + // in this case, just return the reference, and expect the + // caller to deal with. + return blockIndex; + } + blockIndex = newIndex; + count++; + } + return blockIndex; +} + +const char16_t* ResTable::valueToString( + const Res_value* value, size_t stringBlock, + char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen) +{ + if (!value) { + return NULL; + } + if (value->dataType == value->TYPE_STRING) { + return getTableStringBlock(stringBlock)->stringAt(value->data, outLen); + } + // XXX do int to string conversions. + return NULL; +} + +ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const +{ + mLock.lock(); + ssize_t err = getBagLocked(resID, outBag); + if (err < NO_ERROR) { + //printf("*** get failed! unlocking\n"); + mLock.unlock(); + } + return err; +} + +void ResTable::unlockBag(const bag_entry* bag) const +{ + //printf("<<< unlockBag %p\n", this); + mLock.unlock(); +} + +void ResTable::lock() const +{ + mLock.lock(); +} + +void ResTable::unlock() const +{ + mLock.unlock(); +} + +ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, + uint32_t* outTypeSpecFlags) const +{ + if (mError != NO_ERROR) { + return mError; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID); + return BAD_INDEX; + } + if (t < 0) { + LOGW("No type identifier when getting bag for resource number 0x%08x", resID); + return BAD_INDEX; + } + + //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t); + PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting bag for resource number 0x%08x", resID); + return false; + } + + if (t >= (int)grp->typeCount) { + LOGW("Type identifier 0x%x is larger than type count 0x%x", + t+1, (int)grp->typeCount); + return BAD_INDEX; + } + + const Package* const basePackage = grp->packages[0]; + + const Type* const typeConfigs = basePackage->getType(t); + + const size_t NENTRY = typeConfigs->entryCount; + if (e >= (int)NENTRY) { + LOGW("Entry identifier 0x%x is larger than entry count 0x%x", + e, (int)typeConfigs->entryCount); + return BAD_INDEX; + } + + // First see if we've already computed this bag... + if (grp->bags) { + bag_set** typeSet = grp->bags[t]; + if (typeSet) { + bag_set* set = typeSet[e]; + if (set) { + if (set != (bag_set*)0xFFFFFFFF) { + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags = set->typeSpecFlags; + } + *outBag = (bag_entry*)(set+1); + //LOGI("Found existing bag for: %p\n", (void*)resID); + return set->numAttrs; + } + LOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", + resID); + return BAD_INDEX; + } + } + } + + // Bag not found, we need to compute it! + if (!grp->bags) { + grp->bags = (bag_set***)malloc(sizeof(bag_set*)*grp->typeCount); + if (!grp->bags) return NO_MEMORY; + memset(grp->bags, 0, sizeof(bag_set*)*grp->typeCount); + } + + bag_set** typeSet = grp->bags[t]; + if (!typeSet) { + typeSet = (bag_set**)malloc(sizeof(bag_set*)*NENTRY); + if (!typeSet) return NO_MEMORY; + memset(typeSet, 0, sizeof(bag_set*)*NENTRY); + grp->bags[t] = typeSet; + } + + // Mark that we are currently working on this one. + typeSet[e] = (bag_set*)0xFFFFFFFF; + + // This is what we are building. + bag_set* set = NULL; + + TABLE_NOISY(LOGI("Building bag: %p\n", (void*)resID)); + + // Now collect all bag attributes from all packages. + size_t ip = grp->packages.size(); + while (ip > 0) { + ip--; + + const Package* const package = grp->packages[ip]; + + const ResTable_type* type; + const ResTable_entry* entry; + const Type* typeClass; + LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, t, e); + ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + LOGV("Resulting offset=%d\n", offset); + if (offset <= 0) { + if (offset < 0) { + if (set) free(set); + return offset; + } + continue; + } + + if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { + LOGW("Skipping entry %p in package table %d because it is not complex!\n", + (void*)resID, (int)ip); + continue; + } + + const uint16_t entrySize = dtohs(entry->size); + const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; + const uint32_t count = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry)->count) : 0; + + size_t N = count; + + TABLE_NOISY(LOGI("Found map: size=%p parent=%p count=%d\n", + entrySize, parent, count)); + + if (set == NULL) { + // If this map inherits from another, we need to start + // with its parent's values. Otherwise start out empty. + TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", + entrySize, parent)); + if (parent) { + const bag_entry* parentBag; + uint32_t parentTypeSpecFlags = 0; + const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags); + const size_t NT = ((NP >= 0) ? NP : 0) + N; + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); + if (set == NULL) { + return NO_MEMORY; + } + if (NP > 0) { + memcpy(set+1, parentBag, NP*sizeof(bag_entry)); + set->numAttrs = NP; + TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP)); + } else { + TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n")); + set->numAttrs = 0; + } + set->availAttrs = NT; + set->typeSpecFlags = parentTypeSpecFlags; + } else { + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); + if (set == NULL) { + return NO_MEMORY; + } + set->numAttrs = 0; + set->availAttrs = N; + set->typeSpecFlags = 0; + } + } + + if (typeClass->typeSpecFlags != NULL) { + set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); + } else { + set->typeSpecFlags = -1; + } + + // Now merge in the new attributes... + ssize_t curOff = offset; + const ResTable_map* map; + bag_entry* entries = (bag_entry*)(set+1); + size_t curEntry = 0; + uint32_t pos = 0; + TABLE_NOISY(LOGI("Starting with set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + while (pos < count) { + TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); + + if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { + LOGW("ResTable_map at %d is beyond type chunk data %d", + (int)curOff, dtohl(type->header.size)); + return BAD_TYPE; + } + map = (const ResTable_map*)(((const uint8_t*)type) + curOff); + N++; + + const uint32_t newName = htodl(map->name.ident); + bool isInside; + uint32_t oldName = 0; + while ((isInside=(curEntry < set->numAttrs)) + && (oldName=entries[curEntry].map.name.ident) < newName) { + TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", + curEntry, entries[curEntry].map.name.ident)); + curEntry++; + } + + if ((!isInside) || oldName != newName) { + // This is a new attribute... figure out what to do with it. + if (set->numAttrs >= set->availAttrs) { + // Need to alloc more memory... + const size_t newAvail = set->availAttrs+N; + set = (bag_set*)realloc(set, + sizeof(bag_set) + + sizeof(bag_entry)*newAvail); + if (set == NULL) { + return NO_MEMORY; + } + set->availAttrs = newAvail; + entries = (bag_entry*)(set+1); + TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + } + if (isInside) { + // Going in the middle, need to make space. + memmove(entries+curEntry+1, entries+curEntry, + sizeof(bag_entry)*(set->numAttrs-curEntry)); + set->numAttrs++; + } + TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", + curEntry, newName)); + } else { + TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", + curEntry, oldName)); + } + + bag_entry* cur = entries+curEntry; + + cur->stringBlock = package->header->index; + cur->map.name.ident = newName; + cur->map.value.copyFrom_dtoh(map->value); + TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", + curEntry, cur, cur->stringBlock, cur->map.name.ident, + cur->map.value.dataType, cur->map.value.data)); + + // On to the next! + curEntry++; + pos++; + const size_t size = dtohs(map->value.size); + curOff += size + sizeof(*map)-sizeof(map->value); + }; + if (curEntry > set->numAttrs) { + set->numAttrs = curEntry; + } + } + + // And this is it... + typeSet[e] = set; + if (set) { + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags = set->typeSpecFlags; + } + *outBag = (bag_entry*)(set+1); + TABLE_NOISY(LOGI("Returning %d attrs\n", set->numAttrs)); + return set->numAttrs; + } + return BAD_INDEX; +} + +void ResTable::setParameters(const ResTable_config* params) +{ + mLock.lock(); + TABLE_GETENTRY(LOGI("Setting parameters: imsi:%d/%d lang:%c%c cnt:%c%c " + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + params->mcc, params->mnc, + params->language[0] ? params->language[0] : '-', + params->language[1] ? params->language[1] : '-', + params->country[0] ? params->country[0] : '-', + params->country[1] ? params->country[1] : '-', + params->orientation, + params->touchscreen, + params->density, + params->keyboard, + params->inputFlags, + params->navigation, + params->screenWidth, + params->screenHeight)); + mParams = *params; + for (size_t i=0; iclearBagCache(); + } + mLock.unlock(); +} + +void ResTable::getParameters(ResTable_config* params) const +{ + mLock.lock(); + *params = mParams; + mLock.unlock(); +} + +struct id_name_map { + uint32_t id; + size_t len; + char16_t name[6]; +}; + +const static id_name_map ID_NAMES[] = { + { ResTable_map::ATTR_TYPE, 5, { '^', 't', 'y', 'p', 'e' } }, + { ResTable_map::ATTR_L10N, 5, { '^', 'l', '1', '0', 'n' } }, + { ResTable_map::ATTR_MIN, 4, { '^', 'm', 'i', 'n' } }, + { ResTable_map::ATTR_MAX, 4, { '^', 'm', 'a', 'x' } }, + { ResTable_map::ATTR_OTHER, 6, { '^', 'o', 't', 'h', 'e', 'r' } }, + { ResTable_map::ATTR_ZERO, 5, { '^', 'z', 'e', 'r', 'o' } }, + { ResTable_map::ATTR_ONE, 4, { '^', 'o', 'n', 'e' } }, + { ResTable_map::ATTR_TWO, 4, { '^', 't', 'w', 'o' } }, + { ResTable_map::ATTR_FEW, 4, { '^', 'f', 'e', 'w' } }, + { ResTable_map::ATTR_MANY, 5, { '^', 'm', 'a', 'n', 'y' } }, +}; + +uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen, + const char16_t* type, size_t typeLen, + const char16_t* package, + size_t packageLen, + uint32_t* outTypeSpecFlags) const +{ + TABLE_SUPER_NOISY(printf("Identifier for name: error=%d\n", mError)); + + // Check for internal resource identifier as the very first thing, so + // that we will always find them even when there are no resources. + if (name[0] == '^') { + const int N = (sizeof(ID_NAMES)/sizeof(ID_NAMES[0])); + size_t len; + for (int i=0; ilen; + if (len != nameLen) { + continue; + } + for (size_t j=1; jname[j] != name[j]) { + goto nope; + } + } + return m->id; +nope: + ; + } + if (nameLen > 7) { + if (name[1] == 'i' && name[2] == 'n' + && name[3] == 'd' && name[4] == 'e' && name[5] == 'x' + && name[6] == '_') { + int index = atoi(String8(name + 7, nameLen - 7).string()); + if (Res_CHECKID(index)) { + LOGW("Array resource index: %d is too large.", + index); + return 0; + } + return Res_MAKEARRAY(index); + } + } + return 0; + } + + if (mError != NO_ERROR) { + return 0; + } + + // Figure out the package and type we are looking in... + + const char16_t* packageEnd = NULL; + const char16_t* typeEnd = NULL; + const char16_t* const nameEnd = name+nameLen; + const char16_t* p = name; + while (p < nameEnd) { + if (*p == ':') packageEnd = p; + else if (*p == '/') typeEnd = p; + p++; + } + if (*name == '@') name++; + if (name >= nameEnd) { + return 0; + } + + if (packageEnd) { + package = name; + packageLen = packageEnd-name; + name = packageEnd+1; + } else if (!package) { + return 0; + } + + if (typeEnd) { + type = name; + typeLen = typeEnd-name; + name = typeEnd+1; + } else if (!type) { + return 0; + } + + if (name >= nameEnd) { + return 0; + } + nameLen = nameEnd-name; + + TABLE_NOISY(printf("Looking for identifier: type=%s, name=%s, package=%s\n", + String8(type, typeLen).string(), + String8(name, nameLen).string(), + String8(package, packageLen).string())); + + const size_t NG = mPackageGroups.size(); + for (size_t ig=0; igname.string(), group->name.size())) { + TABLE_NOISY(printf("Skipping package group: %s\n", String8(group->name).string())); + continue; + } + + const ssize_t ti = group->typeStrings.indexOfString(type, typeLen); + if (ti < 0) { + TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); + continue; + } + + const ssize_t ei = group->keyStrings.indexOfString(name, nameLen); + if (ei < 0) { + TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); + continue; + } + + TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei)); + + const Type* const typeConfigs = group->packages[0]->getType(ti); + if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) { + TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n", + String8(group->name).string(), ti)); + } + + size_t NTC = typeConfigs->configs.size(); + for (size_t tci=0; tciconfigs[tci]; + const uint32_t typeOffset = dtohl(ty->entriesStart); + + const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)ty) + dtohs(ty->header.headerSize)); + + const size_t NE = dtohl(ty->entryCount); + for (size_t i=0; i (dtohl(ty->header.size)-sizeof(ResTable_entry))) { + LOGW("ResTable_entry at %d is beyond type chunk data %d", + offset, dtohl(ty->header.size)); + return 0; + } + if ((offset&0x3) != 0) { + LOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", + (int)offset, (int)group->id, (int)ti+1, (int)i, + String8(package, packageLen).string(), + String8(type, typeLen).string(), + String8(name, nameLen).string()); + return 0; + } + + const ResTable_entry* const entry = (const ResTable_entry*) + (((const uint8_t*)ty) + offset); + if (dtohs(entry->size) < sizeof(*entry)) { + LOGW("ResTable_entry size %d is too small", dtohs(entry->size)); + return BAD_TYPE; + } + + TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n", + i, ei, dtohl(entry->key.index))); + if (dtohl(entry->key.index) == (size_t)ei) { + if (outTypeSpecFlags) { + *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; + } + return Res_MAKEID(group->id-1, ti, i); + } + } + } + } + + return 0; +} + +bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, + String16* outPackage, + String16* outType, + String16* outName, + const String16* defType, + const String16* defPackage, + const char** outErrorMsg) +{ + const char16_t* packageEnd = NULL; + const char16_t* typeEnd = NULL; + const char16_t* p = refStr; + const char16_t* const end = p + refLen; + while (p < end) { + if (*p == ':') packageEnd = p; + else if (*p == '/') { + typeEnd = p; + break; + } + p++; + } + p = refStr; + if (*p == '@') p++; + + if (packageEnd) { + *outPackage = String16(p, packageEnd-p); + p = packageEnd+1; + } else { + if (!defPackage) { + if (outErrorMsg) { + *outErrorMsg = "No resource package specified"; + } + return false; + } + *outPackage = *defPackage; + } + if (typeEnd) { + *outType = String16(p, typeEnd-p); + p = typeEnd+1; + } else { + if (!defType) { + if (outErrorMsg) { + *outErrorMsg = "No resource type specified"; + } + return false; + } + *outType = *defType; + } + *outName = String16(p, end-p); + return true; +} + +static uint32_t get_hex(char c, bool* outError) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 0xa; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 0xa; + } + *outError = true; + return 0; +} + +struct unit_entry +{ + const char* name; + size_t len; + uint8_t type; + uint32_t unit; + float scale; +}; + +static const unit_entry unitNames[] = { + { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f }, + { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, + { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, + { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f }, + { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f }, + { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f }, + { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f }, + { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 }, + { "%p", strlen("%p"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 }, + { NULL, 0, 0, 0, 0 } +}; + +static bool parse_unit(const char* str, Res_value* outValue, + float* outScale, const char** outEnd) +{ + const char* end = str; + while (*end != 0 && !isspace((unsigned char)*end)) { + end++; + } + const size_t len = end-str; + + const char* realEnd = end; + while (*realEnd != 0 && isspace((unsigned char)*realEnd)) { + realEnd++; + } + if (*realEnd != 0) { + return false; + } + + const unit_entry* cur = unitNames; + while (cur->name) { + if (len == cur->len && strncmp(cur->name, str, len) == 0) { + outValue->dataType = cur->type; + outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT; + *outScale = cur->scale; + *outEnd = end; + //printf("Found unit %s for %s\n", cur->name, str); + return true; + } + cur++; + } + + return false; +} + + +bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) +{ + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + + if (len <= 0) { + return false; + } + + size_t i = 0; + int32_t val = 0; + bool neg = false; + + if (*s == '-') { + neg = true; + i++; + } + + if (s[i] < '0' || s[i] > '9') { + return false; + } + + // Decimal or hex? + if (s[i] == '0' && s[i+1] == 'x') { + if (outValue) + outValue->dataType = outValue->TYPE_INT_HEX; + i += 2; + bool error = false; + while (i < len && !error) { + val = (val*16) + get_hex(s[i], &error); + i++; + } + if (error) { + return false; + } + } else { + if (outValue) + outValue->dataType = outValue->TYPE_INT_DEC; + while (i < len) { + if (s[i] < '0' || s[i] > '9') { + return false; + } + val = (val*10) + s[i]-'0'; + i++; + } + } + + if (neg) val = -val; + + while (i < len && isspace16(s[i])) { + i++; + } + + if (i == len) { + if (outValue) + outValue->data = val; + return true; + } + + return false; +} + +bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) +{ + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + + if (len <= 0) { + return false; + } + + char buf[128]; + int i=0; + while (len > 0 && *s != 0 && i < 126) { + if (*s > 255) { + return false; + } + buf[i++] = *s++; + len--; + } + + if (len > 0) { + return false; + } + if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { + return false; + } + + buf[i] = 0; + const char* end; + float f = strtof(buf, (char**)&end); + + if (*end != 0 && !isspace((unsigned char)*end)) { + // Might be a unit... + float scale; + if (parse_unit(end, outValue, &scale, &end)) { + f *= scale; + const bool neg = f < 0; + if (neg) f = -f; + uint64_t bits = (uint64_t)(f*(1<<23)+.5f); + uint32_t radix; + uint32_t shift; + if ((bits&0x7fffff) == 0) { + // Always use 23p0 if there is no fraction, just to make + // things easier to read. + radix = Res_value::COMPLEX_RADIX_23p0; + shift = 23; + } else if ((bits&0xffffffffff800000LL) == 0) { + // Magnitude is zero -- can fit in 0 bits of precision. + radix = Res_value::COMPLEX_RADIX_0p23; + shift = 0; + } else if ((bits&0xffffffff80000000LL) == 0) { + // Magnitude can fit in 8 bits of precision. + radix = Res_value::COMPLEX_RADIX_8p15; + shift = 8; + } else if ((bits&0xffffff8000000000LL) == 0) { + // Magnitude can fit in 16 bits of precision. + radix = Res_value::COMPLEX_RADIX_16p7; + shift = 16; + } else { + // Magnitude needs entire range, so no fractional part. + radix = Res_value::COMPLEX_RADIX_23p0; + shift = 23; + } + int32_t mantissa = (int32_t)( + (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK); + if (neg) { + mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK; + } + outValue->data |= + (radix<data); + return true; + } + return false; + } + + while (*end != 0 && isspace((unsigned char)*end)) { + end++; + } + + if (*end == 0) { + if (outValue) { + outValue->dataType = outValue->TYPE_FLOAT; + *(float*)(&outValue->data) = f; + return true; + } + } + + return false; +} + +bool ResTable::stringToValue(Res_value* outValue, String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, bool coerceType, + uint32_t attrID, + const String16* defType, + const String16* defPackage, + Accessor* accessor, + void* accessorCookie, + uint32_t attrType, + bool enforcePrivate) const +{ + bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting(); + const char* errorMsg = NULL; + + outValue->size = sizeof(Res_value); + outValue->res0 = 0; + + // First strip leading/trailing whitespace. Do this before handling + // escapes, so they can be used to force whitespace into the string. + if (!preserveSpaces) { + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + while (len > 0 && isspace16(s[len-1])) { + len--; + } + // If the string ends with '\', then we keep the space after it. + if (len > 0 && s[len-1] == '\\' && s[len] != 0) { + len++; + } + } + + //printf("Value for: %s\n", String8(s, len).string()); + + uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED; + uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff; + bool fromAccessor = false; + if (attrID != 0 && !Res_INTERNALID(attrID)) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("For attr 0x%08x got bag of %d\n", attrID, cnt); + if (cnt >= 0) { + while (cnt > 0) { + //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data); + switch (bag->map.name.ident) { + case ResTable_map::ATTR_TYPE: + attrType = bag->map.value.data; + break; + case ResTable_map::ATTR_MIN: + attrMin = bag->map.value.data; + break; + case ResTable_map::ATTR_MAX: + attrMax = bag->map.value.data; + break; + case ResTable_map::ATTR_L10N: + l10nReq = bag->map.value.data; + break; + } + bag++; + cnt--; + } + unlockBag(bag); + } else if (accessor && accessor->getAttributeType(attrID, &attrType)) { + fromAccessor = true; + if (attrType == ResTable_map::TYPE_ENUM + || attrType == ResTable_map::TYPE_FLAGS + || attrType == ResTable_map::TYPE_INTEGER) { + accessor->getAttributeMin(attrID, &attrMin); + accessor->getAttributeMax(attrID, &attrMax); + } + if (localizationSetting) { + l10nReq = accessor->getAttributeL10N(attrID); + } + } + } + + const bool canStringCoerce = + coerceType && (attrType&ResTable_map::TYPE_STRING) != 0; + + if (*s == '@') { + outValue->dataType = outValue->TYPE_REFERENCE; + + // Note: we don't check attrType here because the reference can + // be to any other type; we just need to count on the client making + // sure the referenced type is correct. + + //printf("Looking up ref: %s\n", String8(s, len).string()); + + // It's a reference! + if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') { + outValue->data = 0; + return true; + } else { + bool createIfNotFound = false; + const char16_t* resourceRefName; + int resourceNameLen; + if (len > 2 && s[1] == '+') { + createIfNotFound = true; + resourceRefName = s + 2; + resourceNameLen = len - 2; + } else if (len > 2 && s[1] == '*') { + enforcePrivate = false; + resourceRefName = s + 2; + resourceNameLen = len - 2; + } else { + createIfNotFound = false; + resourceRefName = s + 1; + resourceNameLen = len - 1; + } + String16 package, type, name; + if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name, + defType, defPackage, &errorMsg)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return false; + } + + uint32_t specFlags = 0; + uint32_t rid = identifierForName(name.string(), name.size(), type.string(), + type.size(), package.string(), package.size(), &specFlags); + if (rid != 0) { + if (enforcePrivate) { + if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Resource is not public."); + } + return false; + } + } + if (!accessor) { + outValue->data = rid; + return true; + } + rid = Res_MAKEID( + accessor->getRemappedPackage(Res_GETPACKAGE(rid)), + Res_GETTYPE(rid), Res_GETENTRY(rid)); + TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid)); + outValue->data = rid; + return true; + } + + if (accessor) { + uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name, + createIfNotFound); + if (rid != 0) { + TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid)); + outValue->data = rid; + return true; + } + } + } + + if (accessor != NULL) { + accessor->reportError(accessorCookie, "No resource found that matches the given name"); + } + return false; + } + + // if we got to here, and localization is required and it's not a reference, + // complain and bail. + if (l10nReq == ResTable_map::L10N_SUGGESTED) { + if (localizationSetting) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "This attribute must be localized."); + } + } + } + + if (*s == '#') { + // It's a color! Convert to an integer of the form 0xaarrggbb. + uint32_t color = 0; + bool error = false; + if (len == 4) { + outValue->dataType = outValue->TYPE_INT_COLOR_RGB4; + color |= 0xFF000000; + color |= get_hex(s[1], &error) << 20; + color |= get_hex(s[1], &error) << 16; + color |= get_hex(s[2], &error) << 12; + color |= get_hex(s[2], &error) << 8; + color |= get_hex(s[3], &error) << 4; + color |= get_hex(s[3], &error); + } else if (len == 5) { + outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4; + color |= get_hex(s[1], &error) << 28; + color |= get_hex(s[1], &error) << 24; + color |= get_hex(s[2], &error) << 20; + color |= get_hex(s[2], &error) << 16; + color |= get_hex(s[3], &error) << 12; + color |= get_hex(s[3], &error) << 8; + color |= get_hex(s[4], &error) << 4; + color |= get_hex(s[4], &error); + } else if (len == 7) { + outValue->dataType = outValue->TYPE_INT_COLOR_RGB8; + color |= 0xFF000000; + color |= get_hex(s[1], &error) << 20; + color |= get_hex(s[2], &error) << 16; + color |= get_hex(s[3], &error) << 12; + color |= get_hex(s[4], &error) << 8; + color |= get_hex(s[5], &error) << 4; + color |= get_hex(s[6], &error); + } else if (len == 9) { + outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8; + color |= get_hex(s[1], &error) << 28; + color |= get_hex(s[2], &error) << 24; + color |= get_hex(s[3], &error) << 20; + color |= get_hex(s[4], &error) << 16; + color |= get_hex(s[5], &error) << 12; + color |= get_hex(s[6], &error) << 8; + color |= get_hex(s[7], &error) << 4; + color |= get_hex(s[8], &error); + } else { + error = true; + } + if (!error) { + if ((attrType&ResTable_map::TYPE_COLOR) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, + "Color types not allowed"); + } + return false; + } + } else { + outValue->data = color; + //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color); + return true; + } + } else { + if ((attrType&ResTable_map::TYPE_COLOR) != 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Color value not valid --" + " must be #rgb, #argb, #rrggbb, or #aarrggbb"); + } + #if 0 + fprintf(stderr, "%s: Color ID %s value %s is not valid\n", + "Resource File", //(const char*)in->getPrintableSource(), + String8(*curTag).string(), + String8(s, len).string()); + #endif + return false; + } + } + } + + if (*s == '?') { + outValue->dataType = outValue->TYPE_ATTRIBUTE; + + // Note: we don't check attrType here because the reference can + // be to any other type; we just need to count on the client making + // sure the referenced type is correct. + + //printf("Looking up attr: %s\n", String8(s, len).string()); + + static const String16 attr16("attr"); + String16 package, type, name; + if (!expandResourceRef(s+1, len-1, &package, &type, &name, + &attr16, defPackage, &errorMsg)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return false; + } + + //printf("Pkg: %s, Type: %s, Name: %s\n", + // String8(package).string(), String8(type).string(), + // String8(name).string()); + uint32_t specFlags = 0; + uint32_t rid = + identifierForName(name.string(), name.size(), + type.string(), type.size(), + package.string(), package.size(), &specFlags); + if (rid != 0) { + if (enforcePrivate) { + if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Attribute is not public."); + } + return false; + } + } + if (!accessor) { + outValue->data = rid; + return true; + } + rid = Res_MAKEID( + accessor->getRemappedPackage(Res_GETPACKAGE(rid)), + Res_GETTYPE(rid), Res_GETENTRY(rid)); + //printf("Incl %s:%s/%s: 0x%08x\n", + // String8(package).string(), String8(type).string(), + // String8(name).string(), rid); + outValue->data = rid; + return true; + } + + if (accessor) { + uint32_t rid = accessor->getCustomResource(package, type, name); + if (rid != 0) { + //printf("Mine %s:%s/%s: 0x%08x\n", + // String8(package).string(), String8(type).string(), + // String8(name).string(), rid); + outValue->data = rid; + return true; + } + } + + if (accessor != NULL) { + accessor->reportError(accessorCookie, "No resource found that matches the given name"); + } + return false; + } + + if (stringToInt(s, len, outValue)) { + if ((attrType&ResTable_map::TYPE_INTEGER) == 0) { + // If this type does not allow integers, but does allow floats, + // fall through on this error case because the float type should + // be able to accept any integer value. + if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Integer types not allowed"); + } + return false; + } + } else { + if (((int32_t)outValue->data) < ((int32_t)attrMin) + || ((int32_t)outValue->data) > ((int32_t)attrMax)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Integer value out of range"); + } + return false; + } + return true; + } + } + + if (stringToFloat(s, len, outValue)) { + if (outValue->dataType == Res_value::TYPE_DIMENSION) { + if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) { + return true; + } + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Dimension types not allowed"); + } + return false; + } + } else if (outValue->dataType == Res_value::TYPE_FRACTION) { + if ((attrType&ResTable_map::TYPE_FRACTION) != 0) { + return true; + } + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Fraction types not allowed"); + } + return false; + } + } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Float types not allowed"); + } + return false; + } + } else { + return true; + } + } + + if (len == 4) { + if ((s[0] == 't' || s[0] == 'T') && + (s[1] == 'r' || s[1] == 'R') && + (s[2] == 'u' || s[2] == 'U') && + (s[3] == 'e' || s[3] == 'E')) { + if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Boolean types not allowed"); + } + return false; + } + } else { + outValue->dataType = outValue->TYPE_INT_BOOLEAN; + outValue->data = (uint32_t)-1; + return true; + } + } + } + + if (len == 5) { + if ((s[0] == 'f' || s[0] == 'F') && + (s[1] == 'a' || s[1] == 'A') && + (s[2] == 'l' || s[2] == 'L') && + (s[3] == 's' || s[3] == 'S') && + (s[4] == 'e' || s[4] == 'E')) { + if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Boolean types not allowed"); + } + return false; + } + } else { + outValue->dataType = outValue->TYPE_INT_BOOLEAN; + outValue->data = 0; + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_ENUM) != 0) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("Got %d for enum\n", cnt); + if (cnt >= 0) { + resource_name rname; + while (cnt > 0) { + if (!Res_INTERNALID(bag->map.name.ident)) { + //printf("Trying attr #%08x\n", bag->map.name.ident); + if (getResourceName(bag->map.name.ident, &rname)) { + #if 0 + printf("Matching %s against %s (0x%08x)\n", + String8(s, len).string(), + String8(rname.name, rname.nameLen).string(), + bag->map.name.ident); + #endif + if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) { + outValue->dataType = bag->map.value.dataType; + outValue->data = bag->map.value.data; + unlockBag(bag); + return true; + } + } + + } + bag++; + cnt--; + } + unlockBag(bag); + } + + if (fromAccessor) { + if (accessor->getAttributeEnum(attrID, s, len, outValue)) { + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_FLAGS) != 0) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("Got %d for flags\n", cnt); + if (cnt >= 0) { + bool failed = false; + resource_name rname; + outValue->dataType = Res_value::TYPE_INT_HEX; + outValue->data = 0; + const char16_t* end = s + len; + const char16_t* pos = s; + while (pos < end && !failed) { + const char16_t* start = pos; + end++; + while (pos < end && *pos != '|') { + pos++; + } + //printf("Looking for: %s\n", String8(start, pos-start).string()); + const bag_entry* bagi = bag; + ssize_t i; + for (i=0; imap.name.ident)) { + //printf("Trying attr #%08x\n", bagi->map.name.ident); + if (getResourceName(bagi->map.name.ident, &rname)) { + #if 0 + printf("Matching %s against %s (0x%08x)\n", + String8(start,pos-start).string(), + String8(rname.name, rname.nameLen).string(), + bagi->map.name.ident); + #endif + if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) { + outValue->data |= bagi->map.value.data; + break; + } + } + } + } + if (i >= cnt) { + // Didn't find this flag identifier. + failed = true; + } + if (pos < end) { + pos++; + } + } + unlockBag(bag); + if (!failed) { + //printf("Final flag value: 0x%lx\n", outValue->data); + return true; + } + } + + + if (fromAccessor) { + if (accessor->getAttributeFlags(attrID, s, len, outValue)) { + //printf("Final flag value: 0x%lx\n", outValue->data); + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_STRING) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "String types not allowed"); + } + return false; + } + + // Generic string handling... + outValue->dataType = outValue->TYPE_STRING; + if (outString) { + bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg); + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return failed; + } + + return true; +} + +bool ResTable::collectString(String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, + const char** outErrorMsg, + bool append) +{ + String16 tmp; + + char quoted = 0; + const char16_t* p = s; + while (p < (s+len)) { + while (p < (s+len)) { + const char16_t c = *p; + if (c == '\\') { + break; + } + if (!preserveSpaces) { + if (quoted == 0 && isspace16(c) + && (c != ' ' || isspace16(*(p+1)))) { + break; + } + if (c == '"' && (quoted == 0 || quoted == '"')) { + break; + } + if (c == '\'' && (quoted == 0 || quoted == '\'')) { + break; + } + } + p++; + } + if (p < (s+len)) { + if (p > s) { + tmp.append(String16(s, p-s)); + } + if (!preserveSpaces && (*p == '"' || *p == '\'')) { + if (quoted == 0) { + quoted = *p; + } else { + quoted = 0; + } + p++; + } else if (!preserveSpaces && isspace16(*p)) { + // Space outside of a quote -- consume all spaces and + // leave a single plain space char. + tmp.append(String16(" ")); + p++; + while (p < (s+len) && isspace16(*p)) { + p++; + } + } else if (*p == '\\') { + p++; + if (p < (s+len)) { + switch (*p) { + case 't': + tmp.append(String16("\t")); + break; + case 'n': + tmp.append(String16("\n")); + break; + case '#': + tmp.append(String16("#")); + break; + case '@': + tmp.append(String16("@")); + break; + case '?': + tmp.append(String16("?")); + break; + case '"': + tmp.append(String16("\"")); + break; + case '\'': + tmp.append(String16("'")); + break; + case '\\': + tmp.append(String16("\\")); + break; + case 'u': + { + char16_t chr = 0; + int i = 0; + while (i < 4 && p[1] != 0) { + p++; + i++; + int c; + if (*p >= '0' && *p <= '9') { + c = *p - '0'; + } else if (*p >= 'a' && *p <= 'f') { + c = *p - 'a' + 10; + } else if (*p >= 'A' && *p <= 'F') { + c = *p - 'A' + 10; + } else { + if (outErrorMsg) { + *outErrorMsg = "Bad character in \\u unicode escape sequence"; + } + return false; + } + chr = (chr<<4) | c; + } + tmp.append(String16(&chr, 1)); + } break; + default: + // ignore unknown escape chars. + break; + } + p++; + } + } + len -= (p-s); + s = p; + } + } + + if (tmp.size() != 0) { + if (len > 0) { + tmp.append(String16(s, len)); + } + if (append) { + outString->append(tmp); + } else { + outString->setTo(tmp); + } + } else { + if (append) { + outString->append(String16(s, len)); + } else { + outString->setTo(s, len); + } + } + + return true; +} + +size_t ResTable::getBasePackageCount() const +{ + if (mError != NO_ERROR) { + return 0; + } + return mPackageGroups.size(); +} + +const char16_t* ResTable::getBasePackageName(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + return mPackageGroups[idx]->name.string(); +} + +uint32_t ResTable::getBasePackageId(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + return mPackageGroups[idx]->id; +} + +size_t ResTable::getTableCount() const +{ + return mHeaders.size(); +} + +const ResStringPool* ResTable::getTableStringBlock(size_t index) const +{ + return &mHeaders[index]->values; +} + +void* ResTable::getTableCookie(size_t index) const +{ + return mHeaders[index]->cookie; +} + +void ResTable::getConfigurations(Vector* configs) const +{ + const size_t I = mPackageGroups.size(); + for (size_t i=0; ipackages.size(); + for (size_t j=0; jpackages[j]; + const size_t K = package->types.size(); + for (size_t k=0; ktypes[k]; + if (type == NULL) continue; + const size_t L = type->configs.size(); + for (size_t l=0; lconfigs[l]; + const ResTable_config* cfg = &config->config; + // only insert unique + const size_t M = configs->size(); + size_t m; + for (m=0; madd(*cfg); + } + } + } + } + } +} + +void ResTable::getLocales(Vector* locales) const +{ + Vector configs; + LOGD("calling getConfigurations"); + getConfigurations(&configs); + LOGD("called getConfigurations size=%d", (int)configs.size()); + const size_t I = configs.size(); + for (size_t i=0; isize(); + size_t j; + for (j=0; jadd(String8(locale)); + } + } +} + +ssize_t ResTable::getEntry( + const Package* package, int typeIndex, int entryIndex, + const ResTable_config* config, + const ResTable_type** outType, const ResTable_entry** outEntry, + const Type** outTypeClass) const +{ + LOGV("Getting entry from package %p\n", package); + const ResTable_package* const pkg = package->package; + + const Type* allTypes = package->getType(typeIndex); + LOGV("allTypes=%p\n", allTypes); + if (allTypes == NULL) { + LOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); + return 0; + } + + if ((size_t)entryIndex >= allTypes->entryCount) { + LOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", + entryIndex, (int)allTypes->entryCount); + return BAD_TYPE; + } + + const ResTable_type* type = NULL; + uint32_t offset = ResTable_type::NO_ENTRY; + ResTable_config bestConfig; + memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up + + const size_t NT = allTypes->configs.size(); + for (size_t i=0; iconfigs[i]; + if (thisType == NULL) continue; + + ResTable_config thisConfig; + thisConfig.copyFromDtoH(thisType->config); + + TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d lang:%c%c=%c%c cnt:%c%c=%c%c " + "orien:%d=%d touch:%d=%d density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d\n", + entryIndex, typeIndex+1, dtohl(thisType->config.size), + thisConfig.mcc, thisConfig.mnc, + config ? config->mcc : 0, config ? config->mnc : 0, + thisConfig.language[0] ? thisConfig.language[0] : '-', + thisConfig.language[1] ? thisConfig.language[1] : '-', + config && config->language[0] ? config->language[0] : '-', + config && config->language[1] ? config->language[1] : '-', + thisConfig.country[0] ? thisConfig.country[0] : '-', + thisConfig.country[1] ? thisConfig.country[1] : '-', + config && config->country[0] ? config->country[0] : '-', + config && config->country[1] ? config->country[1] : '-', + thisConfig.orientation, + config ? config->orientation : 0, + thisConfig.touchscreen, + config ? config->touchscreen : 0, + thisConfig.density, + config ? config->density : 0, + thisConfig.keyboard, + config ? config->keyboard : 0, + thisConfig.inputFlags, + config ? config->inputFlags : 0, + thisConfig.navigation, + config ? config->navigation : 0, + thisConfig.screenWidth, + config ? config->screenWidth : 0, + thisConfig.screenHeight, + config ? config->screenHeight : 0)); + + // Check to make sure this one is valid for the current parameters. + if (config && !thisConfig.match(*config)) { + TABLE_GETENTRY(LOGI("Does not match config!\n")); + continue; + } + + // Check if there is the desired entry in this type. + + const uint8_t* const end = ((const uint8_t*)thisType) + + dtohl(thisType->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + TABLE_GETENTRY(LOGI("Skipping because it is not defined!\n")); + continue; + } + + if (type != NULL) { + // Check if this one is less specific than the last found. If so, + // we will skip it. We check starting with things we most care + // about to those we least care about. + if (!thisConfig.isBetterThan(bestConfig, config)) { + TABLE_GETENTRY(LOGI("This config is worse than last!\n")); + continue; + } + } + + type = thisType; + offset = thisOffset; + bestConfig = thisConfig; + TABLE_GETENTRY(LOGI("Best entry so far -- using it!\n")); + if (!config) break; + } + + if (type == NULL) { + TABLE_GETENTRY(LOGI("No value found for requested entry!\n")); + return BAD_INDEX; + } + + offset += dtohl(type->entriesStart); + TABLE_NOISY(aout << "Looking in resource table " << package->header->header + << ", typeOff=" + << (void*)(((const char*)type)-((const char*)package->header->header)) + << ", offset=" << (void*)offset << endl); + + if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { + LOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", + offset, dtohl(type->header.size)); + return BAD_TYPE; + } + if ((offset&0x3) != 0) { + LOGW("ResTable_entry at 0x%x is not on an integer boundary", + offset); + return BAD_TYPE; + } + + const ResTable_entry* const entry = (const ResTable_entry*) + (((const uint8_t*)type) + offset); + if (dtohs(entry->size) < sizeof(*entry)) { + LOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); + return BAD_TYPE; + } + + *outType = type; + *outEntry = entry; + if (outTypeClass != NULL) { + *outTypeClass = allTypes; + } + return offset + dtohs(entry->size); +} + +status_t ResTable::parsePackage(const ResTable_package* const pkg, + const Header* const header) +{ + const uint8_t* base = (const uint8_t*)pkg; + status_t err = validate_chunk(&pkg->header, sizeof(*pkg), + header->dataEnd, "ResTable_package"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t pkgSize = dtohl(pkg->header.size); + + if (dtohl(pkg->typeStrings) >= pkgSize) { + LOGW("ResTable_package type strings at %p are past chunk size %p.", + (void*)dtohl(pkg->typeStrings), (void*)pkgSize); + return (mError=BAD_TYPE); + } + if ((dtohl(pkg->typeStrings)&0x3) != 0) { + LOGW("ResTable_package type strings at %p is not on an integer boundary.", + (void*)dtohl(pkg->typeStrings)); + return (mError=BAD_TYPE); + } + if (dtohl(pkg->keyStrings) >= pkgSize) { + LOGW("ResTable_package key strings at %p are past chunk size %p.", + (void*)dtohl(pkg->keyStrings), (void*)pkgSize); + return (mError=BAD_TYPE); + } + if ((dtohl(pkg->keyStrings)&0x3) != 0) { + LOGW("ResTable_package key strings at %p is not on an integer boundary.", + (void*)dtohl(pkg->keyStrings)); + return (mError=BAD_TYPE); + } + + Package* package = NULL; + PackageGroup* group = NULL; + uint32_t id = dtohl(pkg->id); + if (id != 0 && id < 256) { + size_t idx = mPackageMap[id]; + if (idx == 0) { + idx = mPackageGroups.size()+1; + + char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; + strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); + group = new PackageGroup(String16(tmpName), id); + if (group == NULL) { + return (mError=NO_MEMORY); + } + + err = group->typeStrings.setTo(base+dtohl(pkg->typeStrings), + header->dataEnd-(base+dtohl(pkg->typeStrings))); + if (err != NO_ERROR) { + return (mError=err); + } + err = group->keyStrings.setTo(base+dtohl(pkg->keyStrings), + header->dataEnd-(base+dtohl(pkg->keyStrings))); + if (err != NO_ERROR) { + return (mError=err); + } + + //printf("Adding new package id %d at index %d\n", id, idx); + err = mPackageGroups.add(group); + if (err < NO_ERROR) { + return (mError=err); + } + mPackageMap[id] = (uint8_t)idx; + } else { + group = mPackageGroups.itemAt(idx-1); + if (group == NULL) { + return (mError=UNKNOWN_ERROR); + } + } + package = new Package(header, pkg); + if (package == NULL) { + return (mError=NO_MEMORY); + } + err = group->packages.add(package); + if (err < NO_ERROR) { + return (mError=err); + } + } else { + LOG_ALWAYS_FATAL("Skins not supported!"); + return NO_ERROR; + } + + + // Iterate through all chunks. + size_t curPackage = 0; + + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)pkg) + + dtohs(pkg->header.headerSize)); + const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size); + while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) { + TABLE_NOISY(LOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", + dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); + const size_t csize = dtohl(chunk->size); + const uint16_t ctype = dtohs(chunk->type); + if (ctype == RES_TABLE_TYPE_SPEC_TYPE) { + const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk); + err = validate_chunk(&typeSpec->header, sizeof(*typeSpec), + endPos, "ResTable_typeSpec"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t typeSpecSize = dtohl(typeSpec->header.size); + + LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", + (void*)(base-(const uint8_t*)chunk), + dtohs(typeSpec->header.type), + dtohs(typeSpec->header.headerSize), + (void*)typeSize)); + // look for block overrun or int overflow when multiplying by 4 + if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) + || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) + > typeSpecSize)) { + LOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", + (void*)(dtohs(typeSpec->header.headerSize) + +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), + (void*)typeSpecSize); + return (mError=BAD_TYPE); + } + + if (typeSpec->id == 0) { + LOGW("ResTable_type has an id of 0."); + return (mError=BAD_TYPE); + } + + while (package->types.size() < typeSpec->id) { + package->types.add(NULL); + } + Type* t = package->types[typeSpec->id-1]; + if (t == NULL) { + t = new Type(header, package, dtohl(typeSpec->entryCount)); + package->types.editItemAt(typeSpec->id-1) = t; + } else if (dtohl(typeSpec->entryCount) != t->entryCount) { + LOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", + (int)dtohl(typeSpec->entryCount), (int)t->entryCount); + return (mError=BAD_TYPE); + } + t->typeSpecFlags = (const uint32_t*)( + ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); + t->typeSpec = typeSpec; + + } else if (ctype == RES_TABLE_TYPE_TYPE) { + const ResTable_type* type = (const ResTable_type*)(chunk); + err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4, + endPos, "ResTable_type"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t typeSize = dtohl(type->header.size); + + LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", + (void*)(base-(const uint8_t*)chunk), + dtohs(type->header.type), + dtohs(type->header.headerSize), + (void*)typeSize)); + if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) + > typeSize) { + LOGW("ResTable_type entry index to %p extends beyond chunk end %p.", + (void*)(dtohs(type->header.headerSize) + +(sizeof(uint32_t)*dtohl(type->entryCount))), + (void*)typeSize); + return (mError=BAD_TYPE); + } + if (dtohl(type->entryCount) != 0 + && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { + LOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.", + (void*)dtohl(type->entriesStart), (void*)typeSize); + return (mError=BAD_TYPE); + } + if (type->id == 0) { + LOGW("ResTable_type has an id of 0."); + return (mError=BAD_TYPE); + } + + while (package->types.size() < type->id) { + package->types.add(NULL); + } + Type* t = package->types[type->id-1]; + if (t == NULL) { + t = new Type(header, package, dtohl(type->entryCount)); + package->types.editItemAt(type->id-1) = t; + } else if (dtohl(type->entryCount) != t->entryCount) { + LOGW("ResTable_type entry count inconsistent: given %d, previously %d", + (int)dtohl(type->entryCount), (int)t->entryCount); + return (mError=BAD_TYPE); + } + + TABLE_GETENTRY( + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + LOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + type->id, + thisConfig.mcc, thisConfig.mnc, + thisConfig.language[0] ? thisConfig.language[0] : '-', + thisConfig.language[1] ? thisConfig.language[1] : '-', + thisConfig.country[0] ? thisConfig.country[0] : '-', + thisConfig.country[1] ? thisConfig.country[1] : '-', + thisConfig.orientation, + thisConfig.touchscreen, + thisConfig.density, + thisConfig.keyboard, + thisConfig.inputFlags, + thisConfig.navigation, + thisConfig.screenWidth, + thisConfig.screenHeight)); + t->configs.add(type); + } else { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), + endPos, "ResTable_package:unknown"); + if (err != NO_ERROR) { + return (mError=err); + } + } + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + csize); + } + + if (group->typeCount == 0) { + group->typeCount = package->types.size(); + } + + return NO_ERROR; +} + +#ifndef HAVE_ANDROID_OS +#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string()) + +#define CHAR16_ARRAY_EQ(constant, var, len) \ + ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) + +void ResTable::print() const +{ + printf("mError=0x%x (%s)\n", mError, strerror(mError)); +#if 0 + printf("mParams=%c%c-%c%c,\n", + mParams.language[0], mParams.language[1], + mParams.country[0], mParams.country[1]); +#endif + size_t pgCount = mPackageGroups.size(); + printf("Package Groups (%d)\n", (int)pgCount); + for (size_t pgIndex=0; pgIndexid, (int)pg->packages.size(), + String8(pg->name).string()); + + size_t pkgCount = pg->packages.size(); + for (size_t pkgIndex=0; pkgIndexpackages[pkgIndex]; + size_t typeCount = pkg->types.size(); + printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex, + pkg->package->id, String8(String16(pkg->package->name)).string(), + (int)typeCount); + for (size_t typeIndex=0; typeIndexgetType(typeIndex); + if (typeConfigs == NULL) { + printf(" type %d NULL\n", (int)typeIndex); + continue; + } + const size_t NTC = typeConfigs->configs.size(); + printf(" type %d configCount=%d entryCount=%d\n", + (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); + if (typeConfigs->typeSpecFlags != NULL) { + for (size_t entryIndex=0; entryIndexentryCount; entryIndex++) { + uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + resource_name resName; + this->getResourceName(resID, &resName); + printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", + resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen), + dtohl(typeConfigs->typeSpecFlags[entryIndex])); + } + } + for (size_t configIndex=0; configIndexconfigs[configIndex]; + if ((((uint64_t)type)&0x3) != 0) { + printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); + continue; + } + printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d\n", + (int)configIndex, + type->config.language[0] ? type->config.language[0] : '-', + type->config.language[1] ? type->config.language[1] : '-', + type->config.country[0] ? type->config.country[0] : '-', + type->config.country[1] ? type->config.country[1] : '-', + type->config.orientation, + type->config.touchscreen, + dtohs(type->config.density), + type->config.keyboard, + type->config.inputFlags, + type->config.navigation, + dtohs(type->config.screenWidth), + dtohs(type->config.screenHeight)); + size_t entryCount = dtohl(type->entryCount); + uint32_t entriesStart = dtohl(type->entriesStart); + if ((entriesStart&0x3) != 0) { + printf(" NON-INTEGER ResTable_type entriesStart OFFSET: %p\n", (void*)entriesStart); + continue; + } + uint32_t typeSize = dtohl(type->header.size); + if ((typeSize&0x3) != 0) { + printf(" NON-INTEGER ResTable_type header.size: %p\n", (void*)typeSize); + continue; + } + for (size_t entryIndex=0; entryIndexheader.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)type) + dtohs(type->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + continue; + } + + uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + resource_name resName; + this->getResourceName(resID, &resName); + printf(" resource 0x%08x %s:%s/%s: ", resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen)); + if ((thisOffset&0x3) != 0) { + printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset); + continue; + } + if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { + printf("OFFSET OUT OF BOUNDS: %p+%p (size is %p)\n", + (void*)entriesStart, (void*)thisOffset, + (void*)typeSize); + continue; + } + + const ResTable_entry* ent = (const ResTable_entry*) + (((const uint8_t*)type) + entriesStart + thisOffset); + if (((entriesStart + thisOffset)&0x3) != 0) { + printf("NON-INTEGER ResTable_entry OFFSET: %p\n", + (void*)(entriesStart + thisOffset)); + continue; + } + if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { + printf(""); + } else { + uint16_t esize = dtohs(ent->size); + if ((esize&0x3) != 0) { + printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize); + continue; + } + if ((thisOffset+esize) > typeSize) { + printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n", + (void*)entriesStart, (void*)thisOffset, + (void*)esize, (void*)typeSize); + continue; + } + + const Res_value* value = (const Res_value*) + (((const uint8_t*)ent) + esize); + printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", + (int)value->dataType, (int)dtohl(value->data), + (int)dtohs(value->size), (int)value->res0); + } + + if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { + printf(" (PUBLIC)"); + } + printf("\n"); + } + } + } + } + } +} + +#endif // HAVE_ANDROID_OS + +} // namespace android diff --git a/libs/utils/SharedBuffer.cpp b/libs/utils/SharedBuffer.cpp new file mode 100644 index 000000000..3555fb712 --- /dev/null +++ b/libs/utils/SharedBuffer.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005 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 + +// --------------------------------------------------------------------------- + +namespace android { + +SharedBuffer* SharedBuffer::alloc(size_t size) +{ + SharedBuffer* sb = static_cast(malloc(sizeof(SharedBuffer) + size)); + if (sb) { + sb->mRefs = 1; + sb->mSize = size; + } + return sb; +} + + +ssize_t SharedBuffer::dealloc(const SharedBuffer* released) +{ + if (released->mRefs != 0) return -1; // XXX: invalid operation + free(const_cast(released)); + return 0; +} + +SharedBuffer* SharedBuffer::edit() const +{ + if (onlyOwner()) { + return const_cast(this); + } + SharedBuffer* sb = alloc(mSize); + if (sb) { + memcpy(sb->data(), data(), size()); + release(); + } + return sb; +} + +SharedBuffer* SharedBuffer::editResize(size_t newSize) const +{ + if (onlyOwner()) { + SharedBuffer* buf = const_cast(this); + if (buf->mSize == newSize) return buf; + buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize); + if (buf != NULL) { + buf->mSize = newSize; + return buf; + } + } + SharedBuffer* sb = alloc(newSize); + if (sb) { + const size_t mySize = mSize; + memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); + release(); + } + return sb; +} + +SharedBuffer* SharedBuffer::attemptEdit() const +{ + if (onlyOwner()) { + return const_cast(this); + } + return 0; +} + +SharedBuffer* SharedBuffer::reset(size_t new_size) const +{ + // cheap-o-reset. + SharedBuffer* sb = alloc(new_size); + if (sb) { + release(); + } + return sb; +} + +void SharedBuffer::acquire() const { + android_atomic_inc(&mRefs); +} + +int32_t SharedBuffer::release(uint32_t flags) const +{ + int32_t prev = 1; + if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) { + mRefs = 0; + if ((flags & eKeepStorage) == 0) { + free(const_cast(this)); + } + } + return prev; +} + + +}; // namespace android diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp new file mode 100644 index 000000000..51509a304 --- /dev/null +++ b/libs/utils/Socket.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Internet address class. +// + +#ifdef HAVE_WINSOCK +// This needs to come first, or Cygwin gets concerned about a potential +// clash between WinSock and . +# include +#endif + +#include +#include +#include +#include + +#ifndef HAVE_WINSOCK +# include +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include + +using namespace android; + + +/* + * =========================================================================== + * Socket + * =========================================================================== + */ + +#ifndef INVALID_SOCKET +# define INVALID_SOCKET (-1) +#endif +#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET) + +/*static*/ bool Socket::mBootInitialized = false; + +/* + * Extract system-dependent error code. + */ +static inline int getSocketError(void) { +#ifdef HAVE_WINSOCK + return WSAGetLastError(); +#else + return errno; +#endif +} + +/* + * One-time initialization for socket code. + */ +/*static*/ bool Socket::bootInit(void) +{ +#ifdef HAVE_WINSOCK + WSADATA wsaData; + int err; + + err = WSAStartup(MAKEWORD(2, 0), &wsaData); + if (err != 0) { + LOG(LOG_ERROR, "socket", "Unable to start WinSock\n"); + return false; + } + + LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n", + LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); +#endif + + mBootInitialized = true; + return true; +} + +/* + * One-time shutdown for socket code. + */ +/*static*/ void Socket::finalShutdown(void) +{ +#ifdef HAVE_WINSOCK + WSACleanup(); +#endif + mBootInitialized = false; +} + + +/* + * Simple constructor. Allow the application to create us and then make + * bind/connect calls. + */ +Socket::Socket(void) + : mSock(UNDEF_SOCKET) +{ + if (!mBootInitialized) + LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n"); +} + +/* + * Destructor. Closes the socket and resets our storage. + */ +Socket::~Socket(void) +{ + close(); +} + + +/* + * Create a socket and connect to the specified host and port. + */ +int Socket::connect(const char* host, int port) +{ + if (mSock != UNDEF_SOCKET) { + LOG(LOG_WARN, "socket", "Socket already connected\n"); + return -1; + } + + InetSocketAddress sockAddr; + if (!sockAddr.create(host, port)) + return -1; + + //return doConnect(sockAddr); + int foo; + foo = doConnect(sockAddr); + return foo; +} + +/* + * Create a socket and connect to the specified host and port. + */ +int Socket::connect(const InetAddress* addr, int port) +{ + if (mSock != UNDEF_SOCKET) { + LOG(LOG_WARN, "socket", "Socket already connected\n"); + return -1; + } + + InetSocketAddress sockAddr; + if (!sockAddr.create(addr, port)) + return -1; + + return doConnect(sockAddr); +} + +/* + * Finish creating a socket by connecting to the remote host. + * + * Returns 0 on success. + */ +int Socket::doConnect(const InetSocketAddress& sockAddr) +{ +#ifdef HAVE_WINSOCK + SOCKET sock; +#else + int sock; +#endif + const InetAddress* addr = sockAddr.getAddress(); + int port = sockAddr.getPort(); + struct sockaddr_in inaddr; + DurationTimer connectTimer; + + assert(sizeof(struct sockaddr_in) == addr->getAddressLength()); + memcpy(&inaddr, addr->getAddress(), addr->getAddressLength()); + inaddr.sin_port = htons(port); + + //fprintf(stderr, "--- connecting to %s:%d\n", + // sockAddr.getHostName(), port); + + sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == INVALID_SOCKET) { + int err = getSocketError(); + LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err); + return (err != 0) ? err : -1; + } + + connectTimer.start(); + + if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) { + int err = getSocketError(); + LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n", + sockAddr.getHostName(), port, err); + return (err != 0) ? err : -1; + } + + connectTimer.stop(); + if ((long) connectTimer.durationUsecs() > 100000) { + LOG(LOG_INFO, "socket", + "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(), + port, ((long) connectTimer.durationUsecs()) / 1000000.0); + } + + mSock = (unsigned long) sock; + LOG(LOG_VERBOSE, "socket", + "--- connected to %s:%d\n", sockAddr.getHostName(), port); + return 0; +} + + +/* + * Close the socket if it needs closing. + */ +bool Socket::close(void) +{ + if (mSock != UNDEF_SOCKET) { + //fprintf(stderr, "--- closing socket %lu\n", mSock); +#ifdef HAVE_WINSOCK + if (::closesocket((SOCKET) mSock) != 0) + return false; +#else + if (::close((int) mSock) != 0) + return false; +#endif + } + + mSock = UNDEF_SOCKET; + + return true; +} + +/* + * Read data from socket. + * + * Standard semantics: read up to "len" bytes into "buf". Returns the + * number of bytes read, or less than zero on error. + */ +int Socket::read(void* buf, ssize_t len) const +{ + if (mSock == UNDEF_SOCKET) { + LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n"); + return -500; + } + +#ifdef HAVE_WINSOCK + SOCKET sock = (SOCKET) mSock; +#else + int sock = (int) mSock; +#endif + int cc; + + cc = recv(sock, (char*)buf, len, 0); + if (cc < 0) { + int err = getSocketError(); + return (err > 0) ? -err : -1; + } + + return cc; +} + +/* + * Write data to a socket. + * + * Standard semantics: write up to "len" bytes into "buf". Returns the + * number of bytes written, or less than zero on error. + */ +int Socket::write(const void* buf, ssize_t len) const +{ + if (mSock == UNDEF_SOCKET) { + LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n"); + return -500; + } + +#ifdef HAVE_WINSOCK + SOCKET sock = (SOCKET) mSock; +#else + int sock = (int) mSock; +#endif + int cc; + + cc = send(sock, (const char*)buf, len, 0); + if (cc < 0) { + int err = getSocketError(); + return (err > 0) ? -err : -1; + } + + return cc; +} + + +/* + * =========================================================================== + * Socket tests + * =========================================================================== + */ + +/* + * Read all data from the socket. The data is read into a buffer that + * expands as needed. + * + * On exit, the buffer is returned, and the length of the data is stored + * in "*sz". A null byte is added to the end, but is not included in + * the length. + */ +static char* socketReadAll(const Socket& s, int *sz) +{ + int max, r; + char *data, *ptr, *tmp; + + data = (char*) malloc(max = 32768); + if (data == NULL) + return NULL; + + ptr = data; + + for (;;) { + if ((ptr - data) == max) { + tmp = (char*) realloc(data, max *= 2); + if(tmp == 0) { + free(data); + return 0; + } + } + r = s.read(ptr, max - (ptr - data)); + if (r == 0) + break; + if (r < 0) { + LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r); + break; + } + ptr += r; + } + + if ((ptr - data) == max) { + tmp = (char*) realloc(data, max + 1); + if (tmp == NULL) { + free(data); + return NULL; + } + } + *ptr = '\0'; + *sz = (ptr - data); + return data; +} + +/* + * Exercise the Socket class. + */ +void android::TestSockets(void) +{ + printf("----- SOCKET TEST ------\n"); + Socket::bootInit(); + + char* buf = NULL; + int len, cc; + const char* kTestStr = + "GET / HTTP/1.0\n" + "Connection: close\n" + "\n"; + + Socket sock; + if (sock.connect("www.google.com", 80) != 0) { + fprintf(stderr, "socket connected failed\n"); + goto bail; + } + + cc = sock.write(kTestStr, strlen(kTestStr)); + if (cc != (int) strlen(kTestStr)) { + fprintf(stderr, "write failed, res=%d\n", cc); + goto bail; + } + buf = socketReadAll(sock, &len); + + printf("GOT '%s'\n", buf); + +bail: + sock.close(); + free(buf); +} + diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp new file mode 100644 index 000000000..93f7e4f0c --- /dev/null +++ b/libs/utils/Static.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2008 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. + */ + +// All static variables go here, to control initialization and +// destruction order in the library. + +#include + +#include +#include +#include + +namespace android { + +class LibUtilsFirstStatics +{ +public: + LibUtilsFirstStatics() + { + initialize_string8(); + initialize_string16(); + } + + ~LibUtilsFirstStatics() + { + terminate_string16(); + terminate_string8(); + } +}; + +static LibUtilsFirstStatics gFirstStatics; +int gDarwinCantLoadAllObjects = 1; + +// ------------ Text output streams + +Vector gTextBuffers; + +class LogTextOutput : public BufferedTextOutput +{ +public: + LogTextOutput() : BufferedTextOutput(MULTITHREADED) { } + virtual ~LogTextOutput() { }; + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) + { + android_writevLog(&vec, N); + return NO_ERROR; + } +}; + +class FdTextOutput : public BufferedTextOutput +{ +public: + FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { } + virtual ~FdTextOutput() { }; + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) + { + writev(mFD, &vec, N); + return NO_ERROR; + } + +private: + int mFD; +}; + +static LogTextOutput gLogTextOutput; +static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); +static FdTextOutput gStderrTextOutput(STDERR_FILENO); + +TextOutput& alog(gLogTextOutput); +TextOutput& aout(gStdoutTextOutput); +TextOutput& aerr(gStderrTextOutput); + +#ifndef LIBUTILS_NATIVE + +// ------------ ProcessState.cpp + +Mutex gProcessMutex; +sp gProcess; + +class LibUtilsIPCtStatics +{ +public: + LibUtilsIPCtStatics() + { + } + + ~LibUtilsIPCtStatics() + { + IPCThreadState::shutdown(); + } +}; + +static LibUtilsIPCtStatics gIPCStatics; + +// ------------ ServiceManager.cpp + +Mutex gDefaultServiceManagerLock; +sp gDefaultServiceManager; +sp gPermissionController; + +#endif + +} // namespace android diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp new file mode 100644 index 000000000..68a1c5217 --- /dev/null +++ b/libs/utils/StopWatch.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005 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. + */ + +#define LOG_TAG "StopWatch" + +#include +#include +#include + +#include +#include +#include + +/*****************************************************************************/ + +namespace android { + + +StopWatch::StopWatch(const char *name, int clock, uint32_t flags) + : mName(name), mClock(clock), mFlags(flags), + mStartTime(0), mNumLaps(0) +{ + mStartTime = systemTime(mClock); +} + +StopWatch::~StopWatch() +{ + nsecs_t elapsed = elapsedTime(); + const int n = mNumLaps; + LOGD("StopWatch %s (us): %lld ", mName, ns2us(elapsed)); + for (int i=0 ; i= 8) { + elapsed = 0; + } else { + const int n = mNumLaps; + mLaps[n].soFar = elapsed; + mLaps[n].thisLap = n ? (elapsed - mLaps[n-1].soFar) : elapsed; + mNumLaps = n+1; + } + return elapsed; +} + +nsecs_t StopWatch::elapsedTime() const +{ + return systemTime(mClock) - mStartTime; +} + + +/*****************************************************************************/ + +}; // namespace android + diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp new file mode 100644 index 000000000..1f81cadb7 --- /dev/null +++ b/libs/utils/String16.cpp @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2005 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 +#include + +#include + +#ifdef HAVE_WINSOCK +# undef nhtol +# undef htonl +# undef nhtos +# undef htons + +# ifdef HAVE_LITTLE_ENDIAN +# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +# define htonl(x) ntohl(x) +# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +# define htons(x) ntohs(x) +# else +# define ntohl(x) (x) +# define htonl(x) (x) +# define ntohs(x) (x) +# define htons(x) (x) +# endif +#else +# include +#endif + +#include +#include +#include + +// --------------------------------------------------------------------------- + +int strcmp16(const char16_t *s1, const char16_t *s2) +{ + char16_t ch; + int d = 0; + + while ( 1 ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) +{ + char16_t ch; + int d = 0; + + while ( n-- ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +char16_t *strcpy16(char16_t *dst, const char16_t *src) +{ + char16_t *q = dst; + const char16_t *p = src; + char16_t ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return dst; +} + +size_t strlen16(const char16_t *s) +{ + const char16_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + + +char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) +{ + char16_t *q = dst; + const char16_t *p = src; + char ch; + + while (n) { + n--; + *q++ = ch = *p++; + if ( !ch ) + break; + } + + *q = 0; + + return dst; +} + +size_t strnlen16(const char16_t *s, size_t maxlen) +{ + const char16_t *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) +{ + const char16_t* e1 = s1+n1; + const char16_t* e2 = s2+n2; + + while (s1 < e1 && s2 < e2) { + const int d = (int)*s1++ - (int)*s2++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)*s2) + : (n1 > n2 + ? ((int)*s1 - 0) + : 0); +} + +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) +{ + const char16_t* e1 = s1H+n1; + const char16_t* e2 = s2N+n2; + + while (s1H < e1 && s2N < e2) { + const char16_t c2 = ntohs(*s2N); + const int d = (int)*s1H++ - (int)c2; + s2N++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)ntohs(*s2N)) + : (n1 > n2 + ? ((int)*s1H - 0) + : 0); +} + +// --------------------------------------------------------------------------- + +namespace android { + +static inline size_t +utf8_char_len(uint8_t ch) +{ + return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; +} + +#define UTF8_SHIFT_AND_MASK(unicode, byte) (unicode)<<=6; (unicode) |= (0x3f & (byte)); + +static inline uint32_t +utf8_to_utf32(const uint8_t *src, size_t length) +{ + uint32_t unicode; + + switch (length) + { + case 1: + return src[0]; + case 2: + unicode = src[0] & 0x1f; + UTF8_SHIFT_AND_MASK(unicode, src[1]) + return unicode; + case 3: + unicode = src[0] & 0x0f; + UTF8_SHIFT_AND_MASK(unicode, src[1]) + UTF8_SHIFT_AND_MASK(unicode, src[2]) + return unicode; + case 4: + unicode = src[0] & 0x07; + UTF8_SHIFT_AND_MASK(unicode, src[1]) + UTF8_SHIFT_AND_MASK(unicode, src[2]) + UTF8_SHIFT_AND_MASK(unicode, src[3]) + return unicode; + default: + return 0xffff; + } + + //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); +} + +// --------------------------------------------------------------------------- + +static SharedBuffer* gEmptyStringBuf = NULL; +static char16_t* gEmptyString = NULL; + +static inline char16_t* getEmptyString() +{ + gEmptyStringBuf->acquire(); + return gEmptyString; +} + +void initialize_string16() +{ + SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)); + char16_t* str = (char16_t*)buf->data(); + *str = 0; + gEmptyStringBuf = buf; + gEmptyString = str; +} + +void terminate_string16() +{ + SharedBuffer::bufferFromData(gEmptyString)->release(); + gEmptyStringBuf = NULL; + gEmptyString = NULL; +} + +// --------------------------------------------------------------------------- + +// Note: not dealing with generating surrogate pairs. +static char16_t* allocFromUTF8(const char* in, size_t len) +{ + if (len == 0) return getEmptyString(); + + size_t chars = 0; + const char* end = in+len; + const char* p = in; + + while (p < end) { + chars++; + p += utf8_char_len(*p); + } + + SharedBuffer* buf = SharedBuffer::alloc((chars+1)*sizeof(char16_t)); + if (buf) { + p = in; + char16_t* str = (char16_t*)buf->data(); + char16_t* d = str; + while (p < end) { + size_t len = utf8_char_len(*p); + *d++ = (char16_t)utf8_to_utf32((const uint8_t*)p, len); + p += len; + } + *d = 0; + + //printf("Created UTF-16 string from UTF-8 \"%s\":", in); + //printHexData(1, str, buf->size(), 16, 1); + //printf("\n"); + + return str; + } + + return getEmptyString(); +} + +// --------------------------------------------------------------------------- + +String16::String16() + : mString(getEmptyString()) +{ +} + +String16::String16(const String16& o) + : mString(o.mString) +{ + SharedBuffer::bufferFromData(mString)->acquire(); +} + +String16::String16(const String16& o, size_t len, size_t begin) + : mString(getEmptyString()) +{ + setTo(o, len, begin); +} + +String16::String16(const char16_t* o) +{ + size_t len = strlen16(o); + SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + strcpy16(str, o); + mString = str; + return; + } + + mString = getEmptyString(); +} + +String16::String16(const char16_t* o, size_t len) +{ + SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str, o, len*sizeof(char16_t)); + str[len] = 0; + mString = str; + return; + } + + mString = getEmptyString(); +} + +String16::String16(const String8& o) + : mString(allocFromUTF8(o.string(), o.size())) +{ +} + +String16::String16(const char* o) + : mString(allocFromUTF8(o, strlen(o))) +{ +} + +String16::String16(const char* o, size_t len) + : mString(allocFromUTF8(o, len)) +{ +} + +String16::~String16() +{ + SharedBuffer::bufferFromData(mString)->release(); +} + +void String16::setTo(const String16& other) +{ + SharedBuffer::bufferFromData(other.mString)->acquire(); + SharedBuffer::bufferFromData(mString)->release(); + mString = other.mString; +} + +status_t String16::setTo(const String16& other, size_t len, size_t begin) +{ + const size_t N = other.size(); + if (begin >= N) { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); + return NO_ERROR; + } + if ((begin+len) > N) len = N-begin; + if (begin == 0 && len == N) { + setTo(other); + return NO_ERROR; + } + + if (&other == this) { + LOG_ALWAYS_FATAL("Not implemented"); + } + + return setTo(other.string()+begin, len); +} + +status_t String16::setTo(const char16_t* other) +{ + return setTo(other, strlen16(other)); +} + +status_t String16::setTo(const char16_t* other, size_t len) +{ + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str, other, len*sizeof(char16_t)); + str[len] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::append(const String16& other) +{ + const size_t myLen = size(); + const size_t otherLen = other.size(); + if (myLen == 0) { + setTo(other); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+otherLen+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t)); + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::append(const char16_t* chrs, size_t otherLen) +{ + const size_t myLen = size(); + if (myLen == 0) { + setTo(chrs, otherLen); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+otherLen+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str+myLen, chrs, otherLen*sizeof(char16_t)); + str[myLen+otherLen] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::insert(size_t pos, const char16_t* chrs) +{ + return insert(pos, chrs, strlen16(chrs)); +} + +status_t String16::insert(size_t pos, const char16_t* chrs, size_t len) +{ + const size_t myLen = size(); + if (myLen == 0) { + return setTo(chrs, len); + return NO_ERROR; + } else if (len == 0) { + return NO_ERROR; + } + + if (pos > myLen) pos = myLen; + + #if 0 + printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n", + String8(*this).string(), pos, + len, myLen, String8(chrs, len).string()); + #endif + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + if (pos < myLen) { + memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t)); + } + memcpy(str+pos, chrs, len*sizeof(char16_t)); + str[myLen+len] = 0; + mString = str; + #if 0 + printf("Result (%d chrs): %s\n", size(), String8(*this).string()); + #endif + return NO_ERROR; + } + return NO_MEMORY; +} + +ssize_t String16::findFirst(char16_t c) const +{ + const char16_t* str = string(); + const char16_t* p = str; + const char16_t* e = p + size(); + while (p < e) { + if (*p == c) { + return p-str; + } + p++; + } + return -1; +} + +ssize_t String16::findLast(char16_t c) const +{ + const char16_t* str = string(); + const char16_t* p = str; + const char16_t* e = p + size(); + while (p < e) { + e--; + if (*e == c) { + return e-str; + } + } + return -1; +} + +bool String16::startsWith(const String16& prefix) const +{ + const size_t ps = prefix.size(); + if (ps > size()) return false; + return strzcmp16(mString, ps, prefix.string(), ps) == 0; +} + +bool String16::startsWith(const char16_t* prefix) const +{ + const size_t ps = strlen16(prefix); + if (ps > size()) return false; + return strncmp16(mString, prefix, ps) == 0; +} + +status_t String16::makeLower() +{ + const size_t N = size(); + const char16_t* str = string(); + char16_t* edit = NULL; + for (size_t i=0; i= 'A' && v <= 'Z') { + if (!edit) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit(); + if (!buf) { + return NO_MEMORY; + } + edit = (char16_t*)buf->data(); + mString = str = edit; + } + edit[i] = tolower((char)v); + } + } + return NO_ERROR; +} + +status_t String16::replaceAll(char16_t replaceThis, char16_t withThis) +{ + const size_t N = size(); + const char16_t* str = string(); + char16_t* edit = NULL; + for (size_t i=0; iedit(); + if (!buf) { + return NO_MEMORY; + } + edit = (char16_t*)buf->data(); + mString = str = edit; + } + edit[i] = withThis; + } + } + return NO_ERROR; +} + +status_t String16::remove(size_t len, size_t begin) +{ + const size_t N = size(); + if (begin >= N) { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); + return NO_ERROR; + } + if ((begin+len) > N) len = N-begin; + if (begin == 0 && len == N) { + return NO_ERROR; + } + + if (begin > 0) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((N+1)*sizeof(char16_t)); + if (!buf) { + return NO_MEMORY; + } + char16_t* str = (char16_t*)buf->data(); + memmove(str, str+begin, (N-begin+1)*sizeof(char16_t)); + mString = str; + } + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + str[len] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +TextOutput& operator<<(TextOutput& to, const String16& val) +{ + to << String8(val).string(); + return to; +} + +}; // namespace android diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp new file mode 100644 index 000000000..c50d343a7 --- /dev/null +++ b/libs/utils/String8.cpp @@ -0,0 +1,604 @@ +/* + * Copyright (C) 2005 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 + +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +static const uint32_t kByteMask = 0x000000BF; +static const uint32_t kByteMark = 0x00000080; + +// Surrogates aren't valid for UTF-32 characters, so define some +// constants that will let us screen them out. +static const uint32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const uint32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const uint32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const uint32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const uint32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const uint32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; + +// Mask used to set appropriate bits in first byte of UTF-8 sequence, +// indexed by number of bytes in the sequence. +static const uint32_t kFirstByteMark[] = { + 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 +}; + +// Separator used by resource paths. This is not platform dependent contrary +// to OS_PATH_SEPARATOR. +#define RES_PATH_SEPARATOR '/' + +// Return number of utf8 bytes required for the character. +static size_t utf32_to_utf8_bytes(uint32_t srcChar) +{ + size_t bytesToWrite; + + // Figure out how many bytes the result will require. + if (srcChar < 0x00000080) + { + bytesToWrite = 1; + } + else if (srcChar < 0x00000800) + { + bytesToWrite = 2; + } + else if (srcChar < 0x00010000) + { + if ((srcChar < kUnicodeSurrogateStart) + || (srcChar > kUnicodeSurrogateEnd)) + { + bytesToWrite = 3; + } + else + { + // Surrogates are invalid UTF-32 characters. + return 0; + } + } + // Max code point for Unicode is 0x0010FFFF. + else if (srcChar < 0x00110000) + { + bytesToWrite = 4; + } + else + { + // Invalid UTF-32 character. + return 0; + } + + return bytesToWrite; +} + +// Write out the source character to . + +static void utf32_to_utf8(uint8_t* dstP, uint32_t srcChar, size_t bytes) +{ + dstP += bytes; + switch (bytes) + { /* note: everything falls through. */ + case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); + } +} + +// --------------------------------------------------------------------------- + +static SharedBuffer* gEmptyStringBuf = NULL; +static char* gEmptyString = NULL; + +extern int gDarwinCantLoadAllObjects; +int gDarwinIsReallyAnnoying; + +static inline char* getEmptyString() +{ + gEmptyStringBuf->acquire(); + return gEmptyString; +} + +void initialize_string8() +{ +#ifdef LIBUTILS_NATIVE + // Bite me, Darwin! + gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; +#endif + + SharedBuffer* buf = SharedBuffer::alloc(1); + char* str = (char*)buf->data(); + *str = 0; + gEmptyStringBuf = buf; + gEmptyString = str; +} + +void terminate_string8() +{ + SharedBuffer::bufferFromData(gEmptyString)->release(); + gEmptyStringBuf = NULL; + gEmptyString = NULL; +} + +// --------------------------------------------------------------------------- + +static char* allocFromUTF8(const char* in, size_t len) +{ + if (len > 0) { + SharedBuffer* buf = SharedBuffer::alloc(len+1); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char* str = (char*)buf->data(); + memcpy(str, in, len); + str[len] = 0; + return str; + } + return NULL; + } + + return getEmptyString(); +} + +// Note: not dealing with expanding surrogate pairs. +static char* allocFromUTF16(const char16_t* in, size_t len) +{ + if (len == 0) return getEmptyString(); + + size_t bytes = 0; + const char16_t* end = in+len; + const char16_t* p = in; + + while (p < end) { + bytes += utf32_to_utf8_bytes(*p); + p++; + } + + SharedBuffer* buf = SharedBuffer::alloc(bytes+1); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + p = in; + char* str = (char*)buf->data(); + char* d = str; + while (p < end) { + uint32_t c = *p++; + size_t len = utf32_to_utf8_bytes(c); + utf32_to_utf8((uint8_t*)d, c, len); + d += len; + } + *d = 0; + + return str; + } + + return getEmptyString(); +} + +// --------------------------------------------------------------------------- + +String8::String8() + : mString(getEmptyString()) +{ +} + +String8::String8(const String8& o) + : mString(o.mString) +{ + SharedBuffer::bufferFromData(mString)->acquire(); +} + +String8::String8(const char* o) + : mString(allocFromUTF8(o, strlen(o))) +{ + if (mString == NULL) { + mString = getEmptyString(); + } +} + +String8::String8(const char* o, size_t len) + : mString(allocFromUTF8(o, len)) +{ + if (mString == NULL) { + mString = getEmptyString(); + } +} + +String8::String8(const String16& o) + : mString(allocFromUTF16(o.string(), o.size())) +{ +} + +String8::String8(const char16_t* o) + : mString(allocFromUTF16(o, strlen16(o))) +{ +} + +String8::String8(const char16_t* o, size_t len) + : mString(allocFromUTF16(o, len)) +{ +} + +String8::~String8() +{ + SharedBuffer::bufferFromData(mString)->release(); +} + +void String8::setTo(const String8& other) +{ + SharedBuffer::bufferFromData(other.mString)->acquire(); + SharedBuffer::bufferFromData(mString)->release(); + mString = other.mString; +} + +status_t String8::setTo(const char* other) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF8(other, strlen(other)); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char* other, size_t len) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF8(other, len); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char16_t* other, size_t len) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF16(other, len); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::append(const String8& other) +{ + const size_t otherLen = other.bytes(); + if (bytes() == 0) { + setTo(other); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + return real_append(other.string(), otherLen); +} + +status_t String8::append(const char* other) +{ + return append(other, strlen(other)); +} + +status_t String8::append(const char* other, size_t otherLen) +{ + if (bytes() == 0) { + return setTo(other, otherLen); + } else if (otherLen == 0) { + return NO_ERROR; + } + + return real_append(other, otherLen); +} + +status_t String8::real_append(const char* other, size_t otherLen) +{ + const size_t myLen = bytes(); + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(myLen+otherLen+1); + if (buf) { + char* str = (char*)buf->data(); + mString = str; + str += myLen; + memcpy(str, other, otherLen); + str[otherLen] = '\0'; + return NO_ERROR; + } + return NO_MEMORY; +} + +char* String8::lockBuffer(size_t size) +{ + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(size+1); + if (buf) { + char* str = (char*)buf->data(); + mString = str; + return str; + } + return NULL; +} + +void String8::unlockBuffer() +{ + unlockBuffer(strlen(mString)); +} + +status_t String8::unlockBuffer(size_t size) +{ + if (size != this->size()) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(size+1); + if (buf) { + char* str = (char*)buf->data(); + str[size] = 0; + mString = str; + return NO_ERROR; + } + } + + return NO_MEMORY; +} + +ssize_t String8::find(const char* other, size_t start) const +{ + size_t len = size(); + if (start >= len) { + return -1; + } + const char* s = mString+start; + const char* p = strstr(s, other); + return p ? p-mString : -1; +} + +void String8::toLower() +{ + toLower(0, size()); +} + +void String8::toLower(size_t start, size_t length) +{ + const size_t len = size(); + if (start >= len) { + return; + } + if (start+length > len) { + length = len-start; + } + char* buf = lockBuffer(len); + buf += start; + while (length > 0) { + *buf = tolower(*buf); + buf++; + length--; + } + unlockBuffer(len); +} + +void String8::toUpper() +{ + toUpper(0, size()); +} + +void String8::toUpper(size_t start, size_t length) +{ + const size_t len = size(); + if (start >= len) { + return; + } + if (start+length > len) { + length = len-start; + } + char* buf = lockBuffer(len); + buf += start; + while (length > 0) { + *buf = toupper(*buf); + buf++; + length--; + } + unlockBuffer(len); +} + +TextOutput& operator<<(TextOutput& to, const String8& val) +{ + to << val.string(); + return to; +} + +// --------------------------------------------------------------------------- +// Path functions + + +void String8::setPathName(const char* name) +{ + setPathName(name, strlen(name)); +} + +void String8::setPathName(const char* name, size_t len) +{ + char* buf = lockBuffer(len); + + memcpy(buf, name, len); + + // remove trailing path separator, if present + if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR) + len--; + + buf[len] = '\0'; + + unlockBuffer(len); +} + +String8 String8::getPathLeaf(void) const +{ + const char* cp; + const char*const buf = mString; + + cp = strrchr(buf, OS_PATH_SEPARATOR); + if (cp == NULL) + return String8(*this); + else + return String8(cp+1); +} + +String8 String8::getPathDir(void) const +{ + const char* cp; + const char*const str = mString; + + cp = strrchr(str, OS_PATH_SEPARATOR); + if (cp == NULL) + return String8(""); + else + return String8(str, cp - str); +} + +String8 String8::walkPath(String8* outRemains) const +{ + const char* cp; + const char*const str = mString; + const char* buf = str; + + cp = strchr(buf, OS_PATH_SEPARATOR); + if (cp == buf) { + // don't include a leading '/'. + buf = buf+1; + cp = strchr(buf, OS_PATH_SEPARATOR); + } + + if (cp == NULL) { + String8 res = buf != str ? String8(buf) : *this; + if (outRemains) *outRemains = String8(""); + return res; + } + + String8 res(buf, cp-buf); + if (outRemains) *outRemains = String8(cp+1); + return res; +} + +/* + * Helper function for finding the start of an extension in a pathname. + * + * Returns a pointer inside mString, or NULL if no extension was found. + */ +char* String8::find_extension(void) const +{ + const char* lastSlash; + const char* lastDot; + int extLen; + const char* const str = mString; + + // only look at the filename + lastSlash = strrchr(str, OS_PATH_SEPARATOR); + if (lastSlash == NULL) + lastSlash = str; + else + lastSlash++; + + // find the last dot + lastDot = strrchr(lastSlash, '.'); + if (lastDot == NULL) + return NULL; + + // looks good, ship it + return const_cast(lastDot); +} + +String8 String8::getPathExtension(void) const +{ + char* ext; + + ext = find_extension(); + if (ext != NULL) + return String8(ext); + else + return String8(""); +} + +String8 String8::getBasePath(void) const +{ + char* ext; + const char* const str = mString; + + ext = find_extension(); + if (ext == NULL) + return String8(*this); + else + return String8(str, ext - str); +} + +String8& String8::appendPath(const char* name) +{ + // TODO: The test below will fail for Win32 paths. Fix later or ignore. + if (name[0] != OS_PATH_SEPARATOR) { + if (*name == '\0') { + // nothing to do + return *this; + } + + size_t len = length(); + if (len == 0) { + // no existing filename, just use the new one + setPathName(name); + return *this; + } + + // make room for oldPath + '/' + newPath + int newlen = strlen(name); + + char* buf = lockBuffer(len+1+newlen); + + // insert a '/' if needed + if (buf[len-1] != OS_PATH_SEPARATOR) + buf[len++] = OS_PATH_SEPARATOR; + + memcpy(buf+len, name, newlen+1); + len += newlen; + + unlockBuffer(len); + + return *this; + } else { + setPathName(name); + return *this; + } +} + +String8& String8::convertToResPath() +{ +#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR + size_t len = length(); + if (len > 0) { + char * buf = lockBuffer(len); + for (char * end = buf + len; buf < end; ++buf) { + if (*buf == OS_PATH_SEPARATOR) + *buf = RES_PATH_SEPARATOR; + } + unlockBuffer(len); + } +#endif + return *this; +} + + +}; // namespace android diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp new file mode 100644 index 000000000..2bdc0ce27 --- /dev/null +++ b/libs/utils/SystemClock.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2008 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. + */ + + +/* + * System clock functions. + */ + +#if HAVE_ANDROID_OS +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "SystemClock" +#include "utils/Log.h" + +namespace android { + +/* + * Set the current time. This only works when running as root. + */ +int setCurrentTimeMillis(int64_t millis) +{ +#if WIN32 + // not implemented + return -1; +#else + struct timeval tv; +#if HAVE_ANDROID_OS + struct timespec ts; + int fd; + int res; +#endif + int ret = 0; + + if (millis <= 0 || millis / 1000LL >= INT_MAX) { + return -1; + } + + tv.tv_sec = (time_t) (millis / 1000LL); + tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL); + + LOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); + +#if HAVE_ANDROID_OS + fd = open("/dev/alarm", O_RDWR); + if(fd < 0) { + LOGW("Unable to open alarm driver: %s\n", strerror(errno)); + return -1; + } + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); + if(res < 0) { + LOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno)); + ret = -1; + } + close(fd); +#else + if (settimeofday(&tv, NULL) != 0) { + LOGW("Unable to set clock to %d.%d: %s\n", + (int) tv.tv_sec, (int) tv.tv_usec, strerror(errno)); + ret = -1; + } +#endif + + return ret; +#endif // WIN32 +} + +/* + * native public static long uptimeMillis(); + */ +int64_t uptimeMillis() +{ + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); +} + +/* + * native public static long elapsedRealtime(); + */ +int64_t elapsedRealtime() +{ +#if HAVE_ANDROID_OS + static int s_fd = -1; + + if (s_fd == -1) { + int fd = open("/dev/alarm", O_RDONLY); + if (android_atomic_cmpxchg(-1, fd, &s_fd)) { + close(fd); + } + } + + struct timespec ts; + int result = ioctl(s_fd, + ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); + + if (result == 0) { + int64_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + return (int64_t) nanoseconds_to_milliseconds(when); + } else { + // XXX: there was an error, probably because the driver didn't + // exist ... this should return + // a real error, like an exception! + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); + } +#else + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); +#endif +} + +}; // namespace android diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp new file mode 100644 index 000000000..cebee99e5 --- /dev/null +++ b/libs/utils/TextOutput.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005 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 + +// --------------------------------------------------------------------------- + +namespace android { + +TextOutput& operator<<(TextOutput& to, bool val) +{ + if (val) to.print("true", 4); + else to.print("false", 5); + return to; +} + +TextOutput& operator<<(TextOutput& to, int val) +{ + char buf[16]; + sprintf(buf, "%d", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, long val) +{ + char buf[16]; + sprintf(buf, "%ld", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned int val) +{ + char buf[16]; + sprintf(buf, "%u", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned long val) +{ + char buf[16]; + sprintf(buf, "%lu", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, long long val) +{ + char buf[32]; + sprintf(buf, "%Ld", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned long long val) +{ + char buf[32]; + sprintf(buf, "%Lu", val); + to.print(buf, strlen(buf)); + return to; +} + +static TextOutput& print_float(TextOutput& to, double value) +{ + char buf[64]; + sprintf(buf, "%g", value); + if( !strchr(buf, '.') && !strchr(buf, 'e') && + !strchr(buf, 'E') ) { + strncat(buf, ".0", sizeof(buf)-1); + } + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, float val) +{ + return print_float(to,val); +} + +TextOutput& operator<<(TextOutput& to, double val) +{ + return print_float(to,val); +} + +TextOutput& operator<<(TextOutput& to, const void* val) +{ + char buf[16]; + sprintf(buf, "%p", val); + to.print(buf, strlen(buf)); + return to; +} + +static void textOutputPrinter(void* cookie, const char* txt) +{ + ((TextOutput*)cookie)->print(txt, strlen(txt)); +} + +TextOutput& operator<<(TextOutput& to, const TypeCode& val) +{ + printTypeCode(val.typeCode(), textOutputPrinter, (void*)&to); + return to; +} + +HexDump::HexDump(const void *buf, size_t size, size_t bytesPerLine) + : mBuffer(buf) + , mSize(size) + , mBytesPerLine(bytesPerLine) + , mSingleLineCutoff(16) + , mAlignment(4) + , mCArrayStyle(false) +{ + if (bytesPerLine >= 16) mAlignment = 4; + else if (bytesPerLine >= 8) mAlignment = 2; + else mAlignment = 1; +} + +TextOutput& operator<<(TextOutput& to, const HexDump& val) +{ + printHexData(0, val.buffer(), val.size(), val.bytesPerLine(), + val.singleLineCutoff(), val.alignment(), val.carrayStyle(), + textOutputPrinter, (void*)&to); + return to; +} + +}; // namespace android diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp new file mode 100644 index 000000000..5f407a990 --- /dev/null +++ b/libs/utils/Threads.cpp @@ -0,0 +1,1128 @@ +/* + * Copyright (C) 2007 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. + */ + +#define LOG_TAG "libutils.threads" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_PTHREADS) +# include +# include +# include +#elif defined(HAVE_WIN32_THREADS) +# include +# include +# include +# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW +#endif + +#if defined(HAVE_FUTEX) +#include +#endif + +#if defined(HAVE_PRCTL) +#include +#endif + +/* + * =========================================================================== + * Thread wrappers + * =========================================================================== + */ + +using namespace android; + +// ---------------------------------------------------------------------------- +#if defined(HAVE_PTHREADS) +#if 0 +#pragma mark - +#pragma mark PTHREAD +#endif +// ---------------------------------------------------------------------------- + +/* + * Create and run a new thead. + * + * We create it "detached", so it cleans up after itself. + */ + +typedef void* (*android_pthread_entry)(void*); + +struct thread_data_t { + thread_func_t entryFunction; + void* userData; + int priority; + char * threadName; + + // we use this trampoline when we need to set the priority with + // nice/setpriority. + static int trampoline(const thread_data_t* t) { + thread_func_t f = t->entryFunction; + void* u = t->userData; + int prio = t->priority; + char * name = t->threadName; + delete t; + setpriority(PRIO_PROCESS, 0, prio); + if (name) { +#if defined(HAVE_PRCTL) + // Mac OS doesn't have this, and we build libutil for the host too + int hasAt = 0; + int hasDot = 0; + char *s = name; + while (*s) { + if (*s == '.') hasDot = 1; + else if (*s == '@') hasAt = 1; + s++; + } + int len = s - name; + if (len < 15 || hasAt || !hasDot) { + s = name; + } else { + s = name + len - 15; + } + prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); +#endif + free(name); + } + return f(u); + } +}; + +int androidCreateRawThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */ + if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) { + // We could avoid the trampoline if there was a way to get to the + // android_thread_id_t (pid) from pthread_t + thread_data_t* t = new thread_data_t; + t->priority = threadPriority; + t->threadName = threadName ? strdup(threadName) : NULL; + t->entryFunction = entryFunction; + t->userData = userData; + entryFunction = (android_thread_func_t)&thread_data_t::trampoline; + userData = t; + } +#endif + + if (threadStackSize) { + pthread_attr_setstacksize(&attr, threadStackSize); + } + + errno = 0; + pthread_t thread; + int result = pthread_create(&thread, &attr, + (android_pthread_entry)entryFunction, userData); + if (result != 0) { + LOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" + "(android threadPriority=%d)", + entryFunction, result, errno, threadPriority); + return 0; + } + + if (threadId != NULL) { + *threadId = (android_thread_id_t)thread; // XXX: this is not portable + } + return 1; +} + +android_thread_id_t androidGetThreadId() +{ + return (android_thread_id_t)pthread_self(); +} + +// ---------------------------------------------------------------------------- +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#pragma mark WIN32_THREADS +#endif +// ---------------------------------------------------------------------------- + +/* + * Trampoline to make us __stdcall-compliant. + * + * We're expected to delete "vDetails" when we're done. + */ +struct threadDetails { + int (*func)(void*); + void* arg; +}; +static __stdcall unsigned int threadIntermediary(void* vDetails) +{ + struct threadDetails* pDetails = (struct threadDetails*) vDetails; + int result; + + result = (*(pDetails->func))(pDetails->arg); + + delete pDetails; + + LOG(LOG_VERBOSE, "thread", "thread exiting\n"); + return (unsigned int) result; +} + +/* + * Create and run a new thread. + */ +static bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_id_t *id) +{ + HANDLE hThread; + struct threadDetails* pDetails = new threadDetails; // must be on heap + unsigned int thrdaddr; + + pDetails->func = fn; + pDetails->arg = arg; + +#if defined(HAVE__BEGINTHREADEX) + hThread = (HANDLE) _beginthreadex(NULL, 0, threadIntermediary, pDetails, 0, + &thrdaddr); + if (hThread == 0) +#elif defined(HAVE_CREATETHREAD) + hThread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) threadIntermediary, + (void*) pDetails, 0, (DWORD*) &thrdaddr); + if (hThread == NULL) +#endif + { + LOG(LOG_WARN, "thread", "WARNING: thread create failed\n"); + return false; + } + +#if defined(HAVE_CREATETHREAD) + /* close the management handle */ + CloseHandle(hThread); +#endif + + if (id != NULL) { + *id = (android_thread_id_t)thrdaddr; + } + + return true; +} + +int androidCreateRawThreadEtc(android_thread_func_t fn, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + return doCreateThread( fn, userData, threadId); +} + +android_thread_id_t androidGetThreadId() +{ + return (android_thread_id_t)GetCurrentThreadId(); +} + +// ---------------------------------------------------------------------------- +#else +#error "Threads not supported" +#endif + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Common Thread functions +#endif + +int androidCreateThread(android_thread_func_t fn, void* arg) +{ + return createThreadEtc(fn, arg); +} + +int androidCreateThreadGetID(android_thread_func_t fn, void *arg, android_thread_id_t *id) +{ + return createThreadEtc(fn, arg, "android:unnamed_thread", + PRIORITY_DEFAULT, 0, id); +} + +static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc; + +int androidCreateThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + return gCreateThreadFn(entryFunction, userData, threadName, + threadPriority, threadStackSize, threadId); +} + +void androidSetCreateThreadFunc(android_create_thread_fn func) +{ + gCreateThreadFn = func; +} + +namespace android { + +/* + * =========================================================================== + * Mutex class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark Mutex +#endif + +#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) +/* + * Simple pthread wrapper. + */ + +Mutex::Mutex() +{ + _init(); +} + +Mutex::Mutex(const char* name) +{ + // XXX: name not used for now + _init(); +} + +void Mutex::_init() +{ + pthread_mutex_t* pMutex = new pthread_mutex_t; + pthread_mutex_init(pMutex, NULL); + mState = pMutex; +} + +Mutex::~Mutex() +{ + delete (pthread_mutex_t*) mState; +} + +status_t Mutex::lock() +{ + int res; + while ((res=pthread_mutex_lock((pthread_mutex_t*) mState)) == EINTR) ; + return -res; +} + +void Mutex::unlock() +{ + pthread_mutex_unlock((pthread_mutex_t*) mState); +} + +status_t Mutex::tryLock() +{ + int res; + while ((res=pthread_mutex_trylock((pthread_mutex_t*) mState)) == EINTR) ; + return -res; +} + +#elif defined(HAVE_FUTEX) +#if 0 +#pragma mark - +#endif + +#define STATE ((futex_mutex_t*) (&mState)) + +Mutex::Mutex() +{ + _init(); +} + +Mutex::Mutex(const char* name) +{ + _init(); +} + +void +Mutex::_init() +{ + futex_mutex_init(STATE); +} + +Mutex::~Mutex() +{ +} + +status_t Mutex::lock() +{ + int res; + while ((res=futex_mutex_lock(STATE, FUTEX_WAIT_INFINITE)) == EINTR) ; + return -res; +} + +void Mutex::unlock() +{ + futex_mutex_unlock(STATE); +} + +status_t Mutex::tryLock() +{ + int res; + while ((res=futex_mutex_trylock(STATE)) == EINTR) ; + return -res; +} +#undef STATE + +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#endif + +Mutex::Mutex() +{ + HANDLE hMutex; + + assert(sizeof(hMutex) == sizeof(mState)); + + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; +} + +Mutex::Mutex(const char* name) +{ + // XXX: name not used for now + HANDLE hMutex; + + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; +} + +Mutex::~Mutex() +{ + CloseHandle((HANDLE) mState); +} + +status_t Mutex::lock() +{ + DWORD dwWaitResult; + dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE); + return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR; +} + +void Mutex::unlock() +{ + if (!ReleaseMutex((HANDLE) mState)) + LOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n"); +} + +status_t Mutex::tryLock() +{ + DWORD dwWaitResult; + + dwWaitResult = WaitForSingleObject((HANDLE) mState, 0); + if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) + LOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n"); + return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1; +} + +#else +#error "Somebody forgot to implement threads for this platform." +#endif + + +/* + * =========================================================================== + * Condition class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark Condition +#endif + +#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) + +/* + * Constructor. This is a simple pthread wrapper. + */ +Condition::Condition() +{ + pthread_cond_t* pCond = new pthread_cond_t; + + pthread_cond_init(pCond, NULL); + mState = pCond; +} + +/* + * Destructor. + */ +Condition::~Condition() +{ + pthread_cond_destroy((pthread_cond_t*) mState); + delete (pthread_cond_t*) mState; +} + +/* + * Wait on a condition variable. Lock the mutex before calling. + */ + +status_t Condition::wait(Mutex& mutex) +{ + assert(mutex.mState != NULL); + + int cc; + while ((cc = pthread_cond_wait((pthread_cond_t*)mState, + (pthread_mutex_t*) mutex.mState)) == EINTR) ; + return -cc; +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + assert(mutex.mState != NULL); + + struct timespec ts; + ts.tv_sec = abstime/1000000000; + ts.tv_nsec = abstime-(ts.tv_sec*1000000000); + + int cc; + while ((cc = pthread_cond_timedwait((pthread_cond_t*)mState, + (pthread_mutex_t*) mutex.mState, &ts)) == EINTR) ; + return -cc; +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + return wait(mutex, systemTime()+reltime); +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + pthread_cond_signal((pthread_cond_t*) mState); +} + +/* + * Signal the condition variable, allowing all threads to continue. + */ +void Condition::broadcast() +{ + pthread_cond_broadcast((pthread_cond_t*) mState); +} + +#elif defined(HAVE_FUTEX) +#if 0 +#pragma mark - +#endif + +#define STATE ((futex_cond_t*) (&mState)) + +/* + * Constructor. This is a simple pthread wrapper. + */ +Condition::Condition() +{ + futex_cond_init(STATE); +} + +/* + * Destructor. + */ +Condition::~Condition() +{ +} + +/* + * Wait on a condition variable. Lock the mutex before calling. + */ + +status_t Condition::wait(Mutex& mutex) +{ + assert(mutex.mState != NULL); + + int res; + while ((res = futex_cond_wait(STATE, + (futex_mutex_t*)(&mutex.mState), FUTEX_WAIT_INFINITE)) == -EINTR) ; + + return -res; +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + nsecs_t reltime = abstime - systemTime(); + if (reltime <= 0) return true; + return waitRelative(mutex, reltime); +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + assert(mutex.mState != NULL); + int res; + unsigned msec = ns2ms(reltime); + if(msec == 0) + return true; + // This code will not time out at the correct time if interrupted by signals + while ((res = futex_cond_wait(STATE, + (futex_mutex_t*)(&mutex.mState), msec)) == -EINTR) ; + return res; +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + futex_cond_signal(STATE); +} + +/* + * Signal the condition variable, allowing all threads to continue. + */ +void Condition::broadcast() +{ + futex_cond_broadcast(STATE); +} + +#undef STATE + +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#endif + +/* + * Windows doesn't have a condition variable solution. It's possible + * to create one, but it's easy to get it wrong. For a discussion, and + * the origin of this implementation, see: + * + * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + * + * The implementation shown on the page does NOT follow POSIX semantics. + * As an optimization they require acquiring the external mutex before + * calling signal() and broadcast(), whereas POSIX only requires grabbing + * it before calling wait(). The implementation here has been un-optimized + * to have the correct behavior. + */ +typedef struct WinCondition { + // Number of waiting threads. + int waitersCount; + + // Serialize access to waitersCount. + CRITICAL_SECTION waitersCountLock; + + // Semaphore used to queue up threads waiting for the condition to + // become signaled. + HANDLE sema; + + // An auto-reset event used by the broadcast/signal thread to wait + // for all the waiting thread(s) to wake up and be released from + // the semaphore. + HANDLE waitersDone; + + // This mutex wouldn't be necessary if we required that the caller + // lock the external mutex before calling signal() and broadcast(). + // I'm trying to mimic pthread semantics though. + HANDLE internalMutex; + + // Keeps track of whether we were broadcasting or signaling. This + // allows us to optimize the code if we're just signaling. + bool wasBroadcast; + + status_t wait(WinCondition* condState, HANDLE hMutex, nsecs_t* abstime) + { + // Increment the wait count, avoiding race conditions. + EnterCriticalSection(&condState->waitersCountLock); + condState->waitersCount++; + //printf("+++ wait: incr waitersCount to %d (tid=%ld)\n", + // condState->waitersCount, getThreadId()); + LeaveCriticalSection(&condState->waitersCountLock); + + DWORD timeout = INFINITE; + if (abstime) { + nsecs_t reltime = *abstime - systemTime(); + if (reltime < 0) + reltime = 0; + timeout = reltime/1000000; + } + + // Atomically release the external mutex and wait on the semaphore. + DWORD res = + SignalObjectAndWait(hMutex, condState->sema, timeout, FALSE); + + //printf("+++ wait: awake (tid=%ld)\n", getThreadId()); + + // Reacquire lock to avoid race conditions. + EnterCriticalSection(&condState->waitersCountLock); + + // No longer waiting. + condState->waitersCount--; + + // Check to see if we're the last waiter after a broadcast. + bool lastWaiter = (condState->wasBroadcast && condState->waitersCount == 0); + + //printf("+++ wait: lastWaiter=%d (wasBc=%d wc=%d)\n", + // lastWaiter, condState->wasBroadcast, condState->waitersCount); + + LeaveCriticalSection(&condState->waitersCountLock); + + // If we're the last waiter thread during this particular broadcast + // then signal broadcast() that we're all awake. It'll drop the + // internal mutex. + if (lastWaiter) { + // Atomically signal the "waitersDone" event and wait until we + // can acquire the internal mutex. We want to do this in one step + // because it ensures that everybody is in the mutex FIFO before + // any thread has a chance to run. Without it, another thread + // could wake up, do work, and hop back in ahead of us. + SignalObjectAndWait(condState->waitersDone, condState->internalMutex, + INFINITE, FALSE); + } else { + // Grab the internal mutex. + WaitForSingleObject(condState->internalMutex, INFINITE); + } + + // Release the internal and grab the external. + ReleaseMutex(condState->internalMutex); + WaitForSingleObject(hMutex, INFINITE); + + return res == WAIT_OBJECT_0 ? NO_ERROR : -1; + } +} WinCondition; + +/* + * Constructor. Set up the WinCondition stuff. + */ +Condition::Condition() +{ + WinCondition* condState = new WinCondition; + + condState->waitersCount = 0; + condState->wasBroadcast = false; + // semaphore: no security, initial value of 0 + condState->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); + InitializeCriticalSection(&condState->waitersCountLock); + // auto-reset event, not signaled initially + condState->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL); + // used so we don't have to lock external mutex on signal/broadcast + condState->internalMutex = CreateMutex(NULL, FALSE, NULL); + + mState = condState; +} + +/* + * Destructor. Free Windows resources as well as our allocated storage. + */ +Condition::~Condition() +{ + WinCondition* condState = (WinCondition*) mState; + if (condState != NULL) { + CloseHandle(condState->sema); + CloseHandle(condState->waitersDone); + delete condState; + } +} + + +status_t Condition::wait(Mutex& mutex) +{ + WinCondition* condState = (WinCondition*) mState; + HANDLE hMutex = (HANDLE) mutex.mState; + + return ((WinCondition*)mState)->wait(condState, hMutex, NULL); +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + WinCondition* condState = (WinCondition*) mState; + HANDLE hMutex = (HANDLE) mutex.mState; + + return ((WinCondition*)mState)->wait(condState, hMutex, &abstime); +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + return wait(mutex, systemTime()+reltime); +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + WinCondition* condState = (WinCondition*) mState; + + // Lock the internal mutex. This ensures that we don't clash with + // broadcast(). + WaitForSingleObject(condState->internalMutex, INFINITE); + + EnterCriticalSection(&condState->waitersCountLock); + bool haveWaiters = (condState->waitersCount > 0); + LeaveCriticalSection(&condState->waitersCountLock); + + // If no waiters, then this is a no-op. Otherwise, knock the semaphore + // down a notch. + if (haveWaiters) + ReleaseSemaphore(condState->sema, 1, 0); + + // Release internal mutex. + ReleaseMutex(condState->internalMutex); +} + +/* + * Signal the condition variable, allowing all threads to continue. + * + * First we have to wake up all threads waiting on the semaphore, then + * we wait until all of the threads have actually been woken before + * releasing the internal mutex. This ensures that all threads are woken. + */ +void Condition::broadcast() +{ + WinCondition* condState = (WinCondition*) mState; + + // Lock the internal mutex. This keeps the guys we're waking up + // from getting too far. + WaitForSingleObject(condState->internalMutex, INFINITE); + + EnterCriticalSection(&condState->waitersCountLock); + bool haveWaiters = false; + + if (condState->waitersCount > 0) { + haveWaiters = true; + condState->wasBroadcast = true; + } + + if (haveWaiters) { + // Wake up all the waiters. + ReleaseSemaphore(condState->sema, condState->waitersCount, 0); + + LeaveCriticalSection(&condState->waitersCountLock); + + // Wait for all awakened threads to acquire the counting semaphore. + // The last guy who was waiting sets this. + WaitForSingleObject(condState->waitersDone, INFINITE); + + // Reset wasBroadcast. (No crit section needed because nobody + // else can wake up to poke at it.) + condState->wasBroadcast = 0; + } else { + // nothing to do + LeaveCriticalSection(&condState->waitersCountLock); + } + + // Release internal mutex. + ReleaseMutex(condState->internalMutex); +} + +#else +#error "condition variables not supported on this platform" +#endif + + +/* + * =========================================================================== + * ReadWriteLock class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark ReadWriteLock +#endif + +/* + * Add a reader. Readers are nice. They share. + */ +void ReadWriteLock::lockForRead() +{ + mLock.lock(); + while (mNumWriters > 0) { + LOG(LOG_DEBUG, "thread", "+++ lockForRead: waiting\n"); + mReadWaiter.wait(mLock); + } + assert(mNumWriters == 0); + mNumReaders++; +#if defined(PRINT_RENDER_TIMES) + if (mNumReaders == 1) + mDebugTimer.start(); +#endif + mLock.unlock(); +} + +/* + * Try to add a reader. If it doesn't work right away, return "false". + */ +bool ReadWriteLock::tryLockForRead() +{ + mLock.lock(); + if (mNumWriters > 0) { + mLock.unlock(); + return false; + } + assert(mNumWriters == 0); + mNumReaders++; +#if defined(PRINT_RENDER_TIMES) + if (mNumReaders == 1) + mDebugTimer.start(); +#endif + mLock.unlock(); + return true; +} + +/* + * Remove a reader. + */ +void ReadWriteLock::unlockForRead() +{ + mLock.lock(); + if (mNumReaders == 0) { + mLock.unlock(); + LOG(LOG_WARN, "thread", + "WARNING: unlockForRead requested, but not locked\n"); + return; + } + assert(mNumReaders > 0); + assert(mNumWriters == 0); + mNumReaders--; + if (mNumReaders == 0) { // last reader? +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.stop(); + printf(" rdlk held %.3f msec\n", + (double) mDebugTimer.durationUsecs() / 1000.0); +#endif + //printf("+++ signaling writers (if any)\n"); + mWriteWaiter.signal(); // wake one writer (if any) + } + mLock.unlock(); +} + +/* + * Add a writer. This requires exclusive access to the object. + */ +void ReadWriteLock::lockForWrite() +{ + mLock.lock(); + while (mNumReaders > 0 || mNumWriters > 0) { + LOG(LOG_DEBUG, "thread", "+++ lockForWrite: waiting\n"); + mWriteWaiter.wait(mLock); + } + assert(mNumReaders == 0); + assert(mNumWriters == 0); + mNumWriters++; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.start(); +#endif + mLock.unlock(); +} + +/* + * Try to add a writer. If it doesn't work right away, return "false". + */ +bool ReadWriteLock::tryLockForWrite() +{ + mLock.lock(); + if (mNumReaders > 0 || mNumWriters > 0) { + mLock.unlock(); + return false; + } + assert(mNumReaders == 0); + assert(mNumWriters == 0); + mNumWriters++; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.start(); +#endif + mLock.unlock(); + return true; +} + +/* + * Remove a writer. + */ +void ReadWriteLock::unlockForWrite() +{ + mLock.lock(); + if (mNumWriters == 0) { + mLock.unlock(); + LOG(LOG_WARN, "thread", + "WARNING: unlockForWrite requested, but not locked\n"); + return; + } + assert(mNumWriters == 1); + mNumWriters--; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.stop(); + //printf(" wrlk held %.3f msec\n", + // (double) mDebugTimer.durationUsecs() / 1000.0); +#endif + mWriteWaiter.signal(); // should other writers get first dibs? + //printf("+++ signaling readers (if any)\n"); + mReadWaiter.broadcast(); // wake all readers (if any) + mLock.unlock(); +} + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Thread::Thread +#endif + +/* + * This is our thread object! + */ + +Thread::Thread(bool canCallJava) + : mCanCallJava(canCallJava), + mThread(thread_id_t(-1)), + mLock("Thread::mLock"), + mStatus(NO_ERROR), + mExitPending(false), mRunning(false) +{ +} + +Thread::~Thread() +{ +} + +status_t Thread::readyToRun() +{ + return NO_ERROR; +} + +status_t Thread::run(const char* name, int32_t priority, size_t stack) +{ + Mutex::Autolock _l(mLock); + + if (mRunning) { + // thread already started + return INVALID_OPERATION; + } + + // reset status and exitPending to their default value, so we can + // try again after an error happened (either below, or in readyToRun()) + mStatus = NO_ERROR; + mExitPending = false; + mThread = thread_id_t(-1); + + // hold a strong reference on ourself + mHoldSelf = this; + + bool res; + if (mCanCallJava) { + res = createThreadEtc(_threadLoop, + this, name, priority, stack, &mThread); + } else { + res = androidCreateRawThreadEtc(_threadLoop, + this, name, priority, stack, &mThread); + } + + if (res == false) { + mStatus = UNKNOWN_ERROR; // something happened! + mRunning = false; + mThread = thread_id_t(-1); + } + + if (mStatus < 0) { + // something happened, don't leak + mHoldSelf.clear(); + } + + return mStatus; +} + +int Thread::_threadLoop(void* user) +{ + Thread* const self = static_cast(user); + sp strong(self->mHoldSelf); + wp weak(strong); + self->mHoldSelf.clear(); + + // we're about to run... + self->mStatus = self->readyToRun(); + if (self->mStatus!=NO_ERROR || self->mExitPending) { + // pretend the thread never started... + self->mExitPending = false; + self->mRunning = false; + return 0; + } + + // thread is running now + self->mRunning = true; + + do { + bool result = self->threadLoop(); + if (result == false || self->mExitPending) { + self->mExitPending = true; + self->mLock.lock(); + self->mRunning = false; + self->mThreadExitedCondition.signal(); + self->mLock.unlock(); + break; + } + + // Release our strong reference, to let a chance to the thread + // to die a peaceful death. + strong.clear(); + // And immediately, reacquire a strong reference for the next loop + strong = weak.promote(); + } while(strong != 0); + + return 0; +} + +void Thread::requestExit() +{ + mExitPending = true; +} + +status_t Thread::requestExitAndWait() +{ + if (mStatus == OK) { + + if (mThread == getThreadId()) { + LOGW( + "Thread (this=%p): don't call waitForExit() from this " + "Thread object's thread. It's a guaranteed deadlock!", + this); + return WOULD_BLOCK; + } + + requestExit(); + + Mutex::Autolock _l(mLock); + while (mRunning == true) { + mThreadExitedCondition.wait(mLock); + } + mExitPending = false; + } + return mStatus; +} + +bool Thread::exitPending() const +{ + return mExitPending; +} + + + +}; // namespace android diff --git a/libs/utils/TimerProbe.cpp b/libs/utils/TimerProbe.cpp new file mode 100644 index 000000000..835480d36 --- /dev/null +++ b/libs/utils/TimerProbe.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2008 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 + +#if ENABLE_TIMER_PROBE + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "time" + +namespace android { + +Vector TimerProbe::gBuckets; +TimerProbe* TimerProbe::gExecuteChain; +int TimerProbe::gIndent; +timespec TimerProbe::gRealBase; + +TimerProbe::TimerProbe(const char tag[], int* slot) : mTag(tag) +{ + mNext = gExecuteChain; + gExecuteChain = this; + mIndent = gIndent; + gIndent += 1; + if (mIndent > 0) { + if (*slot == 0) { + int count = gBuckets.add(); + *slot = count; + Bucket& bucket = gBuckets.editItemAt(count); + memset(&bucket, 0, sizeof(Bucket)); + bucket.mTag = tag; + bucket.mSlotPtr = slot; + bucket.mIndent = mIndent; + } + mBucket = *slot; + } + clock_gettime(CLOCK_REALTIME, &mRealStart); + if (gRealBase.tv_sec == 0) + gRealBase = mRealStart; + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mPStart); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mTStart); +} + +void TimerProbe::end() +{ + timespec realEnd, pEnd, tEnd; + clock_gettime(CLOCK_REALTIME, &realEnd); + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pEnd); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tEnd); + print(realEnd, pEnd, tEnd); + mTag = NULL; +} + +TimerProbe::~TimerProbe() +{ + if (mTag != NULL) + end(); + gExecuteChain = mNext; + gIndent--; +} + + +uint32_t TimerProbe::ElapsedTime(const timespec& start, const timespec& end) +{ + int sec = end.tv_sec - start.tv_sec; + int nsec = end.tv_nsec - start.tv_nsec; + if (nsec < 0) { + sec--; + nsec += 1000000000; + } + return sec * 1000000 + nsec / 1000; +} + +void TimerProbe::print(const timespec& r, const timespec& p, + const timespec& t) const +{ + uint32_t es = ElapsedTime(gRealBase, mRealStart); + uint32_t er = ElapsedTime(mRealStart, r); + uint32_t ep = ElapsedTime(mPStart, p); + uint32_t et = ElapsedTime(mTStart, t); + if (mIndent > 0) { + Bucket& bucket = gBuckets.editItemAt(mBucket); + if (bucket.mStart == 0) + bucket.mStart = es; + bucket.mReal += er; + bucket.mProcess += ep; + bucket.mThread += et; + bucket.mCount++; + return; + } + int index = 0; + int buckets = gBuckets.size(); + int count = 1; + const char* tag = mTag; + int indent = mIndent; + do { + LOGD("%-30.30s: (%3d) %-5.*s time=%-10.3f real=%7dus process=%7dus (%3d%%) thread=%7dus (%3d%%)\n", + tag, count, indent > 5 ? 5 : indent, "+++++", es / 1000000.0, + er, ep, ep * 100 / er, et, et * 100 / er); + if (index >= buckets) + break; + Bucket& bucket = gBuckets.editItemAt(index); + count = bucket.mCount; + es = bucket.mStart; + er = bucket.mReal; + ep = bucket.mProcess; + et = bucket.mThread; + tag = bucket.mTag; + indent = bucket.mIndent; + *bucket.mSlotPtr = 0; + } while (++index); // always true + gBuckets.clear(); +} + +}; // namespace android + +#endif diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp new file mode 100644 index 000000000..2abc811a0 --- /dev/null +++ b/libs/utils/Timers.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Timer functions. +// +#include +#include // may need usleep +#include + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_WIN32_THREADS +#include +#endif + +nsecs_t systemTime(int clock) +{ +#if defined(HAVE_POSIX_CLOCKS) + static const clockid_t clocks[] = { + CLOCK_REALTIME, + CLOCK_MONOTONIC, + CLOCK_PROCESS_CPUTIME_ID, + CLOCK_THREAD_CPUTIME_ID + }; + struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(clocks[clock], &t); + return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec; +#else + // we don't support the clocks here. + struct timeval t; + t.tv_sec = t.tv_usec = 0; + gettimeofday(&t, NULL); + return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL; +#endif +} + +//#define MONITOR_USLEEP + +/* + * Sleep long enough that we'll wake up "interval" milliseconds after + * the previous snooze. + * + * The "nextTick" argument is updated on each call, and should be passed + * in every time. Set its fields to zero on the first call. + * + * Returns the #of intervals we have overslept, which will be zero if we're + * on time. [Currently just returns 0 or 1.] + */ +int sleepForInterval(long interval, struct timeval* pNextTick) +{ + struct timeval now; + long long timeBeforeNext; + long sleepTime = 0; + bool overSlept = false; + //int usleepBias = 0; + +#ifdef USLEEP_BIAS + /* + * Linux likes to add 9000ms or so. + * [not using this for now] + */ + //usleepBias = USLEEP_BIAS; +#endif + + gettimeofday(&now, NULL); + + if (pNextTick->tv_sec == 0) { + /* special-case for first time through */ + *pNextTick = now; + sleepTime = interval; + android::DurationTimer::addToTimeval(pNextTick, interval); + } else { + /* + * Compute how much time there is before the next tick. If this + * value is negative, we've run over. If we've run over a little + * bit we can shorten the next frame to keep the pace steady, but + * if we've dramatically overshot we need to re-sync. + */ + timeBeforeNext = android::DurationTimer::subtractTimevals(pNextTick, &now); + //printf("TOP: now=%ld.%ld next=%ld.%ld diff=%ld\n", + // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, + // (long) timeBeforeNext); + if (timeBeforeNext < -interval) { + /* way over */ + overSlept = true; + sleepTime = 0; + *pNextTick = now; + } else if (timeBeforeNext <= 0) { + /* slightly over, keep the pace steady */ + overSlept = true; + sleepTime = 0; + } else if (timeBeforeNext <= interval) { + /* right on schedule */ + sleepTime = timeBeforeNext; + } else if (timeBeforeNext > interval && timeBeforeNext <= 2*interval) { + /* sleep call returned early; do a longer sleep this time */ + sleepTime = timeBeforeNext; + } else if (timeBeforeNext > interval) { + /* we went back in time -- somebody updated system clock? */ + /* (could also be a *seriously* broken usleep()) */ + LOG(LOG_DEBUG, "", + " Impossible: timeBeforeNext = %ld\n", (long)timeBeforeNext); + sleepTime = 0; + *pNextTick = now; + } + android::DurationTimer::addToTimeval(pNextTick, interval); + } + //printf(" Before sleep: now=%ld.%ld next=%ld.%ld sleepTime=%ld\n", + // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, + // sleepTime); + + /* + * Sleep for the designated period of time. + * + * Linux tends to sleep for longer than requested, often by 17-18ms. + * MinGW tends to sleep for less than requested, by as much as 14ms, + * but occasionally oversleeps for 40+ms (looks like some external + * factors plus round-off on a 64Hz clock). Cygwin is pretty steady. + * + * If you start the MinGW version, and then launch the Cygwin version, + * the MinGW clock becomes more erratic. Not entirely sure why. + * + * (There's a lot of stuff here; it's really just a usleep() call with + * a bunch of instrumentation.) + */ + if (sleepTime > 0) { +#if defined(MONITOR_USLEEP) + struct timeval before, after; + long long actual; + + gettimeofday(&before, NULL); + usleep((long) sleepTime); + gettimeofday(&after, NULL); + + /* check usleep() accuracy; default Linux threads are pretty sloppy */ + actual = android::DurationTimer::subtractTimevals(&after, &before); + if ((long) actual < sleepTime - 14000 /*(sleepTime/10)*/ || + (long) actual > sleepTime + 20000 /*(sleepTime/10)*/) + { + LOG(LOG_DEBUG, "", " Odd usleep: req=%ld, actual=%ld\n", sleepTime, + (long) actual); + } +#else +#ifdef HAVE_WIN32_THREADS + Sleep( sleepTime/1000 ); +#else + usleep((long) sleepTime); +#endif +#endif + } + + //printf("slept %d\n", sleepTime); + + if (overSlept) + return 1; // close enough + else + return 0; +} + + +/* + * =========================================================================== + * DurationTimer + * =========================================================================== + */ + +using namespace android; + +// Start the timer. +void DurationTimer::start(void) +{ + gettimeofday(&mStartWhen, NULL); +} + +// Stop the timer. +void DurationTimer::stop(void) +{ + gettimeofday(&mStopWhen, NULL); +} + +// Get the duration in microseconds. +long long DurationTimer::durationUsecs(void) const +{ + return (long) subtractTimevals(&mStopWhen, &mStartWhen); +} + +// Subtract two timevals. Returns the difference (ptv1-ptv2) in +// microseconds. +/*static*/ long long DurationTimer::subtractTimevals(const struct timeval* ptv1, + const struct timeval* ptv2) +{ + long long stop = ((long long) ptv1->tv_sec) * 1000000LL + + ((long long) ptv1->tv_usec); + long long start = ((long long) ptv2->tv_sec) * 1000000LL + + ((long long) ptv2->tv_usec); + return stop - start; +} + +// Add the specified amount of time to the timeval. +/*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec) +{ + if (usec < 0) { + LOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n"); + return; + } + + // normalize tv_usec if necessary + if (ptv->tv_usec >= 1000000) { + ptv->tv_sec += ptv->tv_usec / 1000000; + ptv->tv_usec %= 1000000; + } + + ptv->tv_usec += usec % 1000000; + if (ptv->tv_usec >= 1000000) { + ptv->tv_usec -= 1000000; + ptv->tv_sec++; + } + ptv->tv_sec += usec / 1000000; +} + diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp new file mode 100644 index 000000000..33f535fd1 --- /dev/null +++ b/libs/utils/Unicode.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2008 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 "utils/AndroidUnicode.h" +#include "characterData.h" + +#define LOG_TAG "Unicode" +#include "utils/Log.h" + +// ICU headers for using macros +#include + +#define MIN_RADIX 2 +#define MAX_RADIX 36 + +#define TYPE_SHIFT 0 +#define TYPE_MASK ((1<<5)-1) + +#define DIRECTION_SHIFT (TYPE_SHIFT+5) +#define DIRECTION_MASK ((1<<5)-1) + +#define MIRRORED_SHIFT (DIRECTION_SHIFT+5) +#define MIRRORED_MASK ((1<<1)-1) + +#define TOUPPER_SHIFT (MIRRORED_SHIFT+1) +#define TOUPPER_MASK ((1<<6)-1) + +#define TOLOWER_SHIFT (TOUPPER_SHIFT+6) +#define TOLOWER_MASK ((1<<6)-1) + +#define TOTITLE_SHIFT (TOLOWER_SHIFT+6) +#define TOTITLE_MASK ((1<<2)-1) + +#define MIRROR_SHIFT (TOTITLE_SHIFT+2) +#define MIRROR_MASK ((1<<5)-1) + +#define NUMERIC_SHIFT (TOTITLE_SHIFT+2) +#define NUMERIC_MASK ((1<<7)-1) + +#define DECOMPOSITION_SHIFT (11) +#define DECOMPOSITION_MASK ((1<<5)-1) + +/* + * Returns the value stored in the CharacterData tables that contains + * an index into the packed data table and the decomposition type. + */ +static uint16_t findCharacterValue(UChar32 c) +{ + LOG_ASSERT(c >= 0 && c <= 0x10FFFF, "findCharacterValue received an invalid codepoint"); + if (c < 256) + return CharacterData::LATIN1_DATA[c]; + + // Rotate the bits because the tables are separated into even and odd codepoints + c = (c >> 1) | ((c & 1) << 20); + + CharacterData::Range search = CharacterData::FULL_DATA[c >> 16]; + const uint32_t* array = search.array; + + // This trick is so that that compare in the while loop does not + // need to shift the array entry down by 16 + c <<= 16; + c |= 0xFFFF; + + int high = (int)search.length - 1; + int low = 0; + + if (high < 0) + return 0; + + while (low < high - 1) + { + int probe = (high + low) >> 1; + + // The entries contain the codepoint in the high 16 bits and the index + // into PACKED_DATA in the low 16. + if (array[probe] > (unsigned)c) + high = probe; + else + low = probe; + } + + LOG_ASSERT((array[low] <= (unsigned)c), "A suitable range was not found"); + return array[low] & 0xFFFF; +} + +uint32_t android::Unicode::getPackedData(UChar32 c) +{ + // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type + // and the remaining bits containing an index. + return CharacterData::PACKED_DATA[findCharacterValue(c) & 0x7FF]; +} + +android::Unicode::CharType android::Unicode::getType(UChar32 c) +{ + if (c < 0 || c >= 0x10FFFF) + return CHARTYPE_UNASSIGNED; + return (CharType)((getPackedData(c) >> TYPE_SHIFT) & TYPE_MASK); +} + +android::Unicode::DecompositionType android::Unicode::getDecompositionType(UChar32 c) +{ + // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type + // and the remaining bits containing an index. + return (DecompositionType)((findCharacterValue(c) >> DECOMPOSITION_SHIFT) & DECOMPOSITION_MASK); +} + +int android::Unicode::getDigitValue(UChar32 c, int radix) +{ + if (radix < MIN_RADIX || radix > MAX_RADIX) + return -1; + + int tempValue = radix; + + if (c >= '0' && c <= '9') + tempValue = c - '0'; + else if (c >= 'a' && c <= 'z') + tempValue = c - 'a' + 10; + else if (c >= 'A' && c <= 'Z') + tempValue = c - 'A' + 10; + + return tempValue < radix ? tempValue : -1; +} + +int android::Unicode::getNumericValue(UChar32 c) +{ + if (isMirrored(c)) + return -1; + + return (int) CharacterData::NUMERICS[((getPackedData(c) >> NUMERIC_SHIFT) & NUMERIC_MASK)]; +} + +UChar32 android::Unicode::toLower(UChar32 c) +{ + return c + CharacterData::LCDIFF[(getPackedData(c) >> TOLOWER_SHIFT) & TOLOWER_MASK]; +} + +UChar32 android::Unicode::toUpper(UChar32 c) +{ + return c + CharacterData::UCDIFF[(getPackedData(c) >> TOUPPER_SHIFT) & TOUPPER_MASK]; +} + +android::Unicode::Direction android::Unicode::getDirectionality(UChar32 c) +{ + uint32_t data = getPackedData(c); + + if (0 == data) + return DIRECTIONALITY_UNDEFINED; + + Direction d = (Direction) ((data >> DIRECTION_SHIFT) & DIRECTION_MASK); + + if (DIRECTION_MASK == d) + return DIRECTIONALITY_UNDEFINED; + + return d; +} + +bool android::Unicode::isMirrored(UChar32 c) +{ + return ((getPackedData(c) >> MIRRORED_SHIFT) & MIRRORED_MASK) != 0; +} + +UChar32 android::Unicode::toMirror(UChar32 c) +{ + if (!isMirrored(c)) + return c; + + return c + CharacterData::MIRROR_DIFF[(getPackedData(c) >> MIRROR_SHIFT) & MIRROR_MASK]; +} + +UChar32 android::Unicode::toTitle(UChar32 c) +{ + int32_t diff = CharacterData::TCDIFF[(getPackedData(c) >> TOTITLE_SHIFT) & TOTITLE_MASK]; + + if (TOTITLE_MASK == diff) + return toUpper(c); + + return c + diff; +} + + diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp new file mode 100644 index 000000000..2c2d6675c --- /dev/null +++ b/libs/utils/VectorImpl.cpp @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2005 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. + */ + +#define LOG_TAG "Vector" + +#include +#include +#include + +#include +#include +#include +#include + +/*****************************************************************************/ + + +namespace android { + +// ---------------------------------------------------------------------------- + +const size_t kMinVectorCapacity = 4; + +static inline size_t max(size_t a, size_t b) { + return a>b ? a : b; +} + +// ---------------------------------------------------------------------------- + +VectorImpl::VectorImpl(size_t itemSize, uint32_t flags) + : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize) +{ +} + +VectorImpl::VectorImpl(const VectorImpl& rhs) + : mStorage(rhs.mStorage), mCount(rhs.mCount), + mFlags(rhs.mFlags), mItemSize(rhs.mItemSize) +{ + if (mStorage) { + SharedBuffer::sharedBuffer(mStorage)->acquire(); + } +} + +VectorImpl::~VectorImpl() +{ + LOG_ASSERT(!mCount, + "[%p] " + "subclasses of VectorImpl must call finish_vector()" + " in their destructor. Leaking %d bytes.", + this, (int)(mCount*mItemSize)); + // We can't call _do_destroy() here because the vtable is already gone. +} + +VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) +{ + LOG_ASSERT(mItemSize == rhs.mItemSize, + "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); + if (this != &rhs) { + release_storage(); + if (rhs.mCount) { + mStorage = rhs.mStorage; + mCount = rhs.mCount; + SharedBuffer::sharedBuffer(mStorage)->acquire(); + } else { + mStorage = 0; + mCount = 0; + } + } + return *this; +} + +void* VectorImpl::editArrayImpl() +{ + if (mStorage) { + SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit(); + if (sb == 0) { + sb = SharedBuffer::alloc(capacity() * mItemSize); + if (sb) { + _do_copy(sb->data(), mStorage, mCount); + release_storage(); + mStorage = sb->data(); + } + } + } + return mStorage; +} + +size_t VectorImpl::capacity() const +{ + if (mStorage) { + return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize; + } + return 0; +} + +ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, vector.size()); + if (where) { + _do_copy(where, vector.arrayImpl(), vector.size()); + } + return where ? index : (ssize_t)NO_MEMORY; +} + +ssize_t VectorImpl::appendVector(const VectorImpl& vector) +{ + return insertVectorAt(vector, size()); +} + +ssize_t VectorImpl::insertAt(size_t index, size_t numItems) +{ + return insertAt(0, index, numItems); +} + +ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, numItems); + if (where) { + if (item) { + _do_splat(where, item, numItems); + } else { + _do_construct(where, numItems); + } + } + return where ? index : (ssize_t)NO_MEMORY; +} + +static int sortProxy(const void* lhs, const void* rhs, void* func) +{ + return (*(VectorImpl::compar_t)func)(lhs, rhs); +} + +status_t VectorImpl::sort(VectorImpl::compar_t cmp) +{ + return sort(sortProxy, (void*)cmp); +} + +status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state) +{ + // the sort must be stable. we're using insertion sort which + // is well suited for small and already sorted arrays + // for big arrays, it could be better to use mergesort + const ssize_t count = size(); + if (count > 1) { + void* array = const_cast(arrayImpl()); + void* temp = 0; + ssize_t i = 1; + while (i < count) { + void* item = reinterpret_cast(array) + mItemSize*(i); + void* curr = reinterpret_cast(array) + mItemSize*(i-1); + if (cmp(curr, item, state) > 0) { + + if (!temp) { + // we're going to have to modify the array... + array = editArrayImpl(); + if (!array) return NO_MEMORY; + temp = malloc(mItemSize); + if (!temp) return NO_MEMORY; + _do_construct(temp, 1); + item = reinterpret_cast(array) + mItemSize*(i); + curr = reinterpret_cast(array) + mItemSize*(i-1); + } + + _do_copy(temp, item, 1); + + ssize_t j = i-1; + void* next = reinterpret_cast(array) + mItemSize*(i); + do { + _do_copy(next, curr, 1); + next = curr; + --j; + curr = reinterpret_cast(array) + mItemSize*(j); + } while (j>=0 && (cmp(curr, temp, state) > 0)); + + _do_copy(next, temp, 1); + } + i++; + } + + if (temp) { + _do_destroy(temp, 1); + free(temp); + } + } + return NO_ERROR; +} + +void VectorImpl::pop() +{ + if (size()) + removeItemsAt(size()-1, 1); +} + +void VectorImpl::push() +{ + push(0); +} + +void VectorImpl::push(const void* item) +{ + insertAt(item, size()); +} + +ssize_t VectorImpl::add() +{ + return add(0); +} + +ssize_t VectorImpl::add(const void* item) +{ + return insertAt(item, size()); +} + +ssize_t VectorImpl::replaceAt(size_t index) +{ + return replaceAt(0, index); +} + +ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) +{ + LOG_ASSERT(index size()) + return BAD_VALUE; + _shrink(index, count); + return index; +} + +void VectorImpl::finish_vector() +{ + release_storage(); + mStorage = 0; + mCount = 0; +} + +void VectorImpl::clear() +{ + _shrink(0, mCount); +} + +void* VectorImpl::editItemLocation(size_t index) +{ + LOG_ASSERT(index(buffer) + index*mItemSize; + return 0; +} + +const void* VectorImpl::itemLocation(size_t index) const +{ + LOG_ASSERT(index(buffer) + index*mItemSize; + return 0; +} + +ssize_t VectorImpl::setCapacity(size_t new_capacity) +{ + size_t current_capacity = capacity(); + ssize_t amount = new_capacity - size(); + if (amount <= 0) { + // we can't reduce the capacity + return current_capacity; + } + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + _do_copy(array, mStorage, size()); + release_storage(); + mStorage = const_cast(array); + } else { + return NO_MEMORY; + } + return new_capacity; +} + +void VectorImpl::release_storage() +{ + if (mStorage) { + const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage); + if (sb->release(SharedBuffer::eKeepStorage) == 1) { + _do_destroy(mStorage, mCount); + SharedBuffer::dealloc(sb); + } + } +} + +void* VectorImpl::_grow(size_t where, size_t amount) +{ +// LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + if (where > mCount) + where = mCount; + + const size_t new_size = mCount + amount; + if (capacity() < new_size) { + const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); +// LOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); + if ((mStorage) && + (mCount==where) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + if (where>0) { + _do_copy(array, mStorage, where); + } + if (mCount>where) { + const void* from = reinterpret_cast(mStorage) + where*mItemSize; + void* dest = reinterpret_cast(array) + (where+amount)*mItemSize; + _do_copy(dest, from, mCount-where); + } + release_storage(); + mStorage = const_cast(array); + } + } + } else { + ssize_t s = mCount-where; + if (s>0) { + void* array = editArrayImpl(); + void* to = reinterpret_cast(array) + (where+amount)*mItemSize; + const void* from = reinterpret_cast(array) + where*mItemSize; + _do_move_forward(to, from, s); + } + } + mCount += amount; + void* free_space = const_cast(itemLocation(where)); + return free_space; +} + +void VectorImpl::_shrink(size_t where, size_t amount) +{ + if (!mStorage) + return; + +// LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + if (where >= mCount) + where = mCount - amount; + + const size_t new_size = mCount - amount; + if (new_size*3 < capacity()) { + const size_t new_capacity = max(kMinVectorCapacity, new_size*2); +// LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); + if ((where == mCount-amount) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + if (where>0) { + _do_copy(array, mStorage, where); + } + if (mCount > where+amount) { + const void* from = reinterpret_cast(mStorage) + (where+amount)*mItemSize; + void* dest = reinterpret_cast(array) + where*mItemSize; + _do_copy(dest, from, mCount-(where+amount)); + } + release_storage(); + mStorage = const_cast(array); + } + } + } else { + void* array = editArrayImpl(); + void* to = reinterpret_cast(array) + where*mItemSize; + _do_destroy(to, amount); + ssize_t s = mCount-(where+amount); + if (s>0) { + const void* from = reinterpret_cast(array) + (where+amount)*mItemSize; + _do_move_backward(to, from, s); + } + } + + // adjust the number of items... + mCount -= amount; +} + +size_t VectorImpl::itemSize() const { + return mItemSize; +} + +void VectorImpl::_do_construct(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_CTOR)) { + do_construct(storage, num); + } +} + +void VectorImpl::_do_destroy(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_DTOR)) { + do_destroy(storage, num); + } +} + +void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_COPY)) { + do_copy(dest, from, num); + } else { + memcpy(dest, from, num*itemSize()); + } +} + +void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const { + do_splat(dest, item, num); +} + +void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const { + do_move_forward(dest, from, num); +} + +void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const { + do_move_backward(dest, from, num); +} + +void VectorImpl::reservedVectorImpl1() { } +void VectorImpl::reservedVectorImpl2() { } +void VectorImpl::reservedVectorImpl3() { } +void VectorImpl::reservedVectorImpl4() { } +void VectorImpl::reservedVectorImpl5() { } +void VectorImpl::reservedVectorImpl6() { } +void VectorImpl::reservedVectorImpl7() { } +void VectorImpl::reservedVectorImpl8() { } + +/*****************************************************************************/ + +SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) + : VectorImpl(itemSize, flags) +{ +} + +SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs) +: VectorImpl(rhs) +{ +} + +SortedVectorImpl::~SortedVectorImpl() +{ +} + +SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs) +{ + return static_cast( VectorImpl::operator = (static_cast(rhs)) ); +} + +ssize_t SortedVectorImpl::indexOf(const void* item) const +{ + return _indexOrderOf(item); +} + +size_t SortedVectorImpl::orderOf(const void* item) const +{ + size_t o; + _indexOrderOf(item, &o); + return o; +} + +ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = size()-1; + ssize_t mid; + const void* a = arrayImpl(); + const size_t s = itemSize(); + while (l <= h) { + mid = l + (h - l)/2; + const void* const curr = reinterpret_cast(a) + (mid*s); + const int c = do_compare(curr, item); + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t SortedVectorImpl::add(const void* item) +{ + size_t order; + ssize_t index = _indexOrderOf(item, &order); + if (index < 0) { + index = VectorImpl::insertAt(item, order, 1); + } else { + index = VectorImpl::replaceAt(item, index); + } + return index; +} + +ssize_t SortedVectorImpl::merge(const VectorImpl& vector) +{ + // naive merge... + if (!vector.isEmpty()) { + const void* buffer = vector.arrayImpl(); + const size_t is = itemSize(); + size_t s = vector.size(); + for (size_t i=0 ; i(buffer) + i*is ); + if (err<0) { + return err; + } + } + } + return NO_ERROR; +} + +ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector) +{ + // we've merging a sorted vector... nice! + ssize_t err = NO_ERROR; + if (!vector.isEmpty()) { + // first take care of the case where the vectors are sorted together + if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) { + err = VectorImpl::insertVectorAt(static_cast(vector), 0); + } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) { + err = VectorImpl::appendVector(static_cast(vector)); + } else { + // this could be made a little better + err = merge(static_cast(vector)); + } + } + return err; +} + +ssize_t SortedVectorImpl::remove(const void* item) +{ + ssize_t i = indexOf(item); + if (i>=0) { + VectorImpl::removeItemsAt(i, 1); + } + return i; +} + +void SortedVectorImpl::reservedSortedVectorImpl1() { }; +void SortedVectorImpl::reservedSortedVectorImpl2() { }; +void SortedVectorImpl::reservedSortedVectorImpl3() { }; +void SortedVectorImpl::reservedSortedVectorImpl4() { }; +void SortedVectorImpl::reservedSortedVectorImpl5() { }; +void SortedVectorImpl::reservedSortedVectorImpl6() { }; +void SortedVectorImpl::reservedSortedVectorImpl7() { }; +void SortedVectorImpl::reservedSortedVectorImpl8() { }; + + +/*****************************************************************************/ + +}; // namespace android + diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp new file mode 100644 index 000000000..fbc9e6784 --- /dev/null +++ b/libs/utils/ZipEntry.cpp @@ -0,0 +1,696 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access to entries in a Zip archive. +// + +#define LOG_TAG "zip" + +#include "utils/ZipEntry.h" +#include "utils/Log.h" + +#include +#include +#include + +using namespace android; + +/* + * Initialize a new ZipEntry structure from a FILE* positioned at a + * CentralDirectoryEntry. + * + * On exit, the file pointer will be at the start of the next CDE or + * at the EOCD. + */ +status_t ZipEntry::initFromCDE(FILE* fp) +{ + status_t result; + long posn; + bool hasDD; + + //LOGV("initFromCDE ---\n"); + + /* read the CDE */ + result = mCDE.read(fp); + if (result != NO_ERROR) { + LOGD("mCDE.read failed\n"); + return result; + } + + //mCDE.dump(); + + /* using the info in the CDE, go load up the LFH */ + posn = ftell(fp); + if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { + LOGD("local header seek failed (%ld)\n", + mCDE.mLocalHeaderRelOffset); + return UNKNOWN_ERROR; + } + + result = mLFH.read(fp); + if (result != NO_ERROR) { + LOGD("mLFH.read failed\n"); + return result; + } + + if (fseek(fp, posn, SEEK_SET) != 0) + return UNKNOWN_ERROR; + + //mLFH.dump(); + + /* + * We *might* need to read the Data Descriptor at this point and + * integrate it into the LFH. If this bit is set, the CRC-32, + * compressed size, and uncompressed size will be zero. In practice + * these seem to be rare. + */ + hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; + if (hasDD) { + // do something clever + //LOGD("+++ has data descriptor\n"); + } + + /* + * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" + * flag is set, because the LFH is incomplete. (Not a problem, since we + * prefer the CDE values.) + */ + if (!hasDD && !compareHeaders()) { + LOGW("WARNING: header mismatch\n"); + // keep going? + } + + /* + * If the mVersionToExtract is greater than 20, we may have an + * issue unpacking the record -- could be encrypted, compressed + * with something we don't support, or use Zip64 extensions. We + * can defer worrying about that to when we're extracting data. + */ + + return NO_ERROR; +} + +/* + * Initialize a new entry. Pass in the file name and an optional comment. + * + * Initializes the CDE and the LFH. + */ +void ZipEntry::initNew(const char* fileName, const char* comment) +{ + assert(fileName != NULL && *fileName != '\0'); // name required + + /* most fields are properly initialized by constructor */ + mCDE.mVersionMadeBy = kDefaultMadeBy; + mCDE.mVersionToExtract = kDefaultVersion; + mCDE.mCompressionMethod = kCompressStored; + mCDE.mFileNameLength = strlen(fileName); + if (comment != NULL) + mCDE.mFileCommentLength = strlen(comment); + mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does + + if (mCDE.mFileNameLength > 0) { + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; + strcpy((char*) mCDE.mFileName, fileName); + } + if (mCDE.mFileCommentLength > 0) { + /* TODO: stop assuming null-terminated ASCII here? */ + mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; + strcpy((char*) mCDE.mFileComment, comment); + } + + copyCDEtoLFH(); +} + +/* + * Initialize a new entry, starting with the ZipEntry from a different + * archive. + * + * Initializes the CDE and the LFH. + */ +status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, + const ZipEntry* pEntry) +{ + /* + * Copy everything in the CDE over, then fix up the hairy bits. + */ + memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); + + if (mCDE.mFileNameLength > 0) { + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; + if (mCDE.mFileName == NULL) + return NO_MEMORY; + strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); + } + if (mCDE.mFileCommentLength > 0) { + mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; + if (mCDE.mFileComment == NULL) + return NO_MEMORY; + strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); + } + if (mCDE.mExtraFieldLength > 0) { + /* we null-terminate this, though it may not be a string */ + mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; + if (mCDE.mExtraField == NULL) + return NO_MEMORY; + memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, + mCDE.mExtraFieldLength+1); + } + + /* construct the LFH from the CDE */ + copyCDEtoLFH(); + + /* + * The LFH "extra" field is independent of the CDE "extra", so we + * handle it here. + */ + assert(mLFH.mExtraField == NULL); + mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; + if (mLFH.mExtraFieldLength > 0) { + mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; + if (mLFH.mExtraField == NULL) + return NO_MEMORY; + memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, + mLFH.mExtraFieldLength+1); + } + + return NO_ERROR; +} + +/* + * Insert pad bytes in the LFH by tweaking the "extra" field. This will + * potentially confuse something that put "extra" data in here earlier, + * but I can't find an actual problem. + */ +status_t ZipEntry::addPadding(int padding) +{ + if (padding <= 0) + return INVALID_OPERATION; + + //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", + // padding, mLFH.mExtraFieldLength, mCDE.mFileName); + + if (mLFH.mExtraFieldLength > 0) { + /* extend existing field */ + unsigned char* newExtra; + + newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; + if (newExtra == NULL) + return NO_MEMORY; + memset(newExtra + mLFH.mExtraFieldLength, 0, padding); + memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); + + delete[] mLFH.mExtraField; + mLFH.mExtraField = newExtra; + mLFH.mExtraFieldLength += padding; + } else { + /* create new field */ + mLFH.mExtraField = new unsigned char[padding]; + memset(mLFH.mExtraField, 0, padding); + mLFH.mExtraFieldLength = padding; + } + + return NO_ERROR; +} + +/* + * Set the fields in the LFH equal to the corresponding fields in the CDE. + * + * This does not touch the LFH "extra" field. + */ +void ZipEntry::copyCDEtoLFH(void) +{ + mLFH.mVersionToExtract = mCDE.mVersionToExtract; + mLFH.mGPBitFlag = mCDE.mGPBitFlag; + mLFH.mCompressionMethod = mCDE.mCompressionMethod; + mLFH.mLastModFileTime = mCDE.mLastModFileTime; + mLFH.mLastModFileDate = mCDE.mLastModFileDate; + mLFH.mCRC32 = mCDE.mCRC32; + mLFH.mCompressedSize = mCDE.mCompressedSize; + mLFH.mUncompressedSize = mCDE.mUncompressedSize; + mLFH.mFileNameLength = mCDE.mFileNameLength; + // the "extra field" is independent + + delete[] mLFH.mFileName; + if (mLFH.mFileNameLength > 0) { + mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; + strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); + } else { + mLFH.mFileName = NULL; + } +} + +/* + * Set some information about a file after we add it. + */ +void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, + int compressionMethod) +{ + mCDE.mCompressionMethod = compressionMethod; + mCDE.mCRC32 = crc32; + mCDE.mCompressedSize = compLen; + mCDE.mUncompressedSize = uncompLen; + mCDE.mCompressionMethod = compressionMethod; + if (compressionMethod == kCompressDeflated) { + mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used + } + copyCDEtoLFH(); +} + +/* + * See if the data in mCDE and mLFH match up. This is mostly useful for + * debugging these classes, but it can be used to identify damaged + * archives. + * + * Returns "false" if they differ. + */ +bool ZipEntry::compareHeaders(void) const +{ + if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { + LOGV("cmp: VersionToExtract\n"); + return false; + } + if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { + LOGV("cmp: GPBitFlag\n"); + return false; + } + if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { + LOGV("cmp: CompressionMethod\n"); + return false; + } + if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { + LOGV("cmp: LastModFileTime\n"); + return false; + } + if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { + LOGV("cmp: LastModFileDate\n"); + return false; + } + if (mCDE.mCRC32 != mLFH.mCRC32) { + LOGV("cmp: CRC32\n"); + return false; + } + if (mCDE.mCompressedSize != mLFH.mCompressedSize) { + LOGV("cmp: CompressedSize\n"); + return false; + } + if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { + LOGV("cmp: UncompressedSize\n"); + return false; + } + if (mCDE.mFileNameLength != mLFH.mFileNameLength) { + LOGV("cmp: FileNameLength\n"); + return false; + } +#if 0 // this seems to be used for padding, not real data + if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { + LOGV("cmp: ExtraFieldLength\n"); + return false; + } +#endif + if (mCDE.mFileName != NULL) { + if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { + LOGV("cmp: FileName\n"); + return false; + } + } + + return true; +} + + +/* + * Convert the DOS date/time stamp into a UNIX time stamp. + */ +time_t ZipEntry::getModWhen(void) const +{ + struct tm parts; + + parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; + parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; + parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; + parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); + parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; + parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; + parts.tm_wday = parts.tm_yday = 0; + parts.tm_isdst = -1; // DST info "not available" + + return mktime(&parts); +} + +/* + * Set the CDE/LFH timestamp from UNIX time. + */ +void ZipEntry::setModWhen(time_t when) +{ +#ifdef HAVE_LOCALTIME_R + struct tm tmResult; +#endif + time_t even; + unsigned short zdate, ztime; + + struct tm* ptm; + + /* round up to an even number of seconds */ + even = (time_t)(((unsigned long)(when) + 1) & (~1)); + + /* expand */ +#ifdef HAVE_LOCALTIME_R + ptm = localtime_r(&even, &tmResult); +#else + ptm = localtime(&even); +#endif + + int year; + year = ptm->tm_year; + if (year < 80) + year = 80; + + zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; + ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; + + mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; + mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; +} + + +/* + * =========================================================================== + * ZipEntry::LocalFileHeader + * =========================================================================== + */ + +/* + * Read a local file header. + * + * On entry, "fp" points to the signature at the start of the header. + * On exit, "fp" points to the start of data. + */ +status_t ZipEntry::LocalFileHeader::read(FILE* fp) +{ + status_t result = NO_ERROR; + unsigned char buf[kLFHLen]; + + assert(mFileName == NULL); + assert(mExtraField == NULL); + + if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { + result = UNKNOWN_ERROR; + goto bail; + } + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOGD("whoops: didn't find expected signature\n"); + result = UNKNOWN_ERROR; + goto bail; + } + + mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); + mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); + mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); + mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); + mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); + mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); + mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); + mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); + mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); + mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); + + // TODO: validate sizes + + /* grab filename */ + if (mFileNameLength != 0) { + mFileName = new unsigned char[mFileNameLength+1]; + if (mFileName == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mFileName[mFileNameLength] = '\0'; + } + + /* grab extra field */ + if (mExtraFieldLength != 0) { + mExtraField = new unsigned char[mExtraFieldLength+1]; + if (mExtraField == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mExtraField[mExtraFieldLength] = '\0'; + } + +bail: + return result; +} + +/* + * Write a local file header. + */ +status_t ZipEntry::LocalFileHeader::write(FILE* fp) +{ + unsigned char buf[kLFHLen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); + ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); + ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); + ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); + ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); + ZipEntry::putLongLE(&buf[0x0e], mCRC32); + ZipEntry::putLongLE(&buf[0x12], mCompressedSize); + ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); + ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); + ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); + + if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) + return UNKNOWN_ERROR; + + /* write filename */ + if (mFileNameLength != 0) { + if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) + return UNKNOWN_ERROR; + } + + /* write "extra field" */ + if (mExtraFieldLength != 0) { + if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + +/* + * Dump the contents of a LocalFileHeader object. + */ +void ZipEntry::LocalFileHeader::dump(void) const +{ + LOGD(" LocalFileHeader contents:\n"); + LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", + mVersionToExtract, mGPBitFlag, mCompressionMethod); + LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", + mLastModFileTime, mLastModFileDate, mCRC32); + LOGD(" compressedSize=%lu uncompressedSize=%lu\n", + mCompressedSize, mUncompressedSize); + LOGD(" filenameLen=%u extraLen=%u\n", + mFileNameLength, mExtraFieldLength); + if (mFileName != NULL) + LOGD(" filename: '%s'\n", mFileName); +} + + +/* + * =========================================================================== + * ZipEntry::CentralDirEntry + * =========================================================================== + */ + +/* + * Read the central dir entry that appears next in the file. + * + * On entry, "fp" should be positioned on the signature bytes for the + * entry. On exit, "fp" will point at the signature word for the next + * entry or for the EOCD. + */ +status_t ZipEntry::CentralDirEntry::read(FILE* fp) +{ + status_t result = NO_ERROR; + unsigned char buf[kCDELen]; + + /* no re-use */ + assert(mFileName == NULL); + assert(mExtraField == NULL); + assert(mFileComment == NULL); + + if (fread(buf, 1, kCDELen, fp) != kCDELen) { + result = UNKNOWN_ERROR; + goto bail; + } + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOGD("Whoops: didn't find expected signature\n"); + result = UNKNOWN_ERROR; + goto bail; + } + + mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); + mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); + mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); + mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); + mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); + mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); + mCRC32 = ZipEntry::getLongLE(&buf[0x10]); + mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); + mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); + mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); + mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); + mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); + mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); + mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); + mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); + mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); + + // TODO: validate sizes and offsets + + /* grab filename */ + if (mFileNameLength != 0) { + mFileName = new unsigned char[mFileNameLength+1]; + if (mFileName == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mFileName[mFileNameLength] = '\0'; + } + + /* read "extra field" */ + if (mExtraFieldLength != 0) { + mExtraField = new unsigned char[mExtraFieldLength+1]; + if (mExtraField == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mExtraField[mExtraFieldLength] = '\0'; + } + + + /* grab comment, if any */ + if (mFileCommentLength != 0) { + mFileComment = new unsigned char[mFileCommentLength+1]; + if (mFileComment == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) + { + result = UNKNOWN_ERROR; + goto bail; + } + mFileComment[mFileCommentLength] = '\0'; + } + +bail: + return result; +} + +/* + * Write a central dir entry. + */ +status_t ZipEntry::CentralDirEntry::write(FILE* fp) +{ + unsigned char buf[kCDELen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); + ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); + ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); + ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); + ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); + ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); + ZipEntry::putLongLE(&buf[0x10], mCRC32); + ZipEntry::putLongLE(&buf[0x14], mCompressedSize); + ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); + ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); + ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); + ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); + ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); + ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); + ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); + ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); + + if (fwrite(buf, 1, kCDELen, fp) != kCDELen) + return UNKNOWN_ERROR; + + /* write filename */ + if (mFileNameLength != 0) { + if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) + return UNKNOWN_ERROR; + } + + /* write "extra field" */ + if (mExtraFieldLength != 0) { + if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) + return UNKNOWN_ERROR; + } + + /* write comment */ + if (mFileCommentLength != 0) { + if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +/* + * Dump the contents of a CentralDirEntry object. + */ +void ZipEntry::CentralDirEntry::dump(void) const +{ + LOGD(" CentralDirEntry contents:\n"); + LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", + mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); + LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", + mLastModFileTime, mLastModFileDate, mCRC32); + LOGD(" compressedSize=%lu uncompressedSize=%lu\n", + mCompressedSize, mUncompressedSize); + LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", + mFileNameLength, mExtraFieldLength, mFileCommentLength); + LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", + mDiskNumberStart, mInternalAttrs, mExternalAttrs, + mLocalHeaderRelOffset); + + if (mFileName != NULL) + LOGD(" filename: '%s'\n", mFileName); + if (mFileComment != NULL) + LOGD(" comment: '%s'\n", mFileComment); +} + diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp new file mode 100644 index 000000000..89aa874b4 --- /dev/null +++ b/libs/utils/ZipFile.cpp @@ -0,0 +1,1296 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access to Zip archives. +// + +#define LOG_TAG "zip" + +#include "utils/ZipFile.h" +#include "utils/ZipUtils.h" +#include "utils/Log.h" + +#include +#define DEF_MEM_LEVEL 8 // normally in zutil.h? + +#include +#include +#include +#include + +using namespace android; + +/* + * Some environments require the "b", some choke on it. + */ +#define FILE_OPEN_RO "rb" +#define FILE_OPEN_RW "r+b" +#define FILE_OPEN_RW_CREATE "w+b" + +/* should live somewhere else? */ +static status_t errnoToStatus(int err) +{ + if (err == ENOENT) + return NAME_NOT_FOUND; + else if (err == EACCES) + return PERMISSION_DENIED; + else + return UNKNOWN_ERROR; +} + +/* + * Open a file and parse its guts. + */ +status_t ZipFile::open(const char* zipFileName, int flags) +{ + bool newArchive = false; + + assert(mZipFp == NULL); // no reopen + + if ((flags & kOpenTruncate)) + flags |= kOpenCreate; // trunc implies create + + if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) + return INVALID_OPERATION; // not both + if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) + return INVALID_OPERATION; // not neither + if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) + return INVALID_OPERATION; // create requires write + + if (flags & kOpenTruncate) { + newArchive = true; + } else { + newArchive = (access(zipFileName, F_OK) != 0); + if (!(flags & kOpenCreate) && newArchive) { + /* not creating, must already exist */ + LOGD("File %s does not exist", zipFileName); + return NAME_NOT_FOUND; + } + } + + /* open the file */ + const char* openflags; + if (flags & kOpenReadWrite) { + if (newArchive) + openflags = FILE_OPEN_RW_CREATE; + else + openflags = FILE_OPEN_RW; + } else { + openflags = FILE_OPEN_RO; + } + mZipFp = fopen(zipFileName, openflags); + if (mZipFp == NULL) { + int err = errno; + LOGD("fopen failed: %d\n", err); + return errnoToStatus(err); + } + + status_t result; + if (!newArchive) { + /* + * Load the central directory. If that fails, then this probably + * isn't a Zip archive. + */ + result = readCentralDir(); + } else { + /* + * Newly-created. The EndOfCentralDir constructor actually + * sets everything to be the way we want it (all zeroes). We + * set mNeedCDRewrite so that we create *something* if the + * caller doesn't add any files. (We could also just unlink + * the file if it's brand new and nothing was added, but that's + * probably doing more than we really should -- the user might + * have a need for empty zip files.) + */ + mNeedCDRewrite = true; + result = NO_ERROR; + } + + if (flags & kOpenReadOnly) + mReadOnly = true; + else + assert(!mReadOnly); + + return result; +} + +/* + * Return the Nth entry in the archive. + */ +ZipEntry* ZipFile::getEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= (int) mEntries.size()) + return NULL; + + return mEntries[idx]; +} + +/* + * Find an entry by name. + */ +ZipEntry* ZipFile::getEntryByName(const char* fileName) const +{ + /* + * Do a stupid linear string-compare search. + * + * There are various ways to speed this up, especially since it's rare + * to intermingle changes to the archive with "get by name" calls. We + * don't want to sort the mEntries vector itself, however, because + * it's used to recreate the Central Directory. + * + * (Hash table works, parallel list of pointers in sorted order is good.) + */ + int idx; + + for (idx = mEntries.size()-1; idx >= 0; idx--) { + ZipEntry* pEntry = mEntries[idx]; + if (!pEntry->getDeleted() && + strcmp(fileName, pEntry->getFileName()) == 0) + { + return pEntry; + } + } + + return NULL; +} + +/* + * Empty the mEntries vector. + */ +void ZipFile::discardEntries(void) +{ + int count = mEntries.size(); + + while (--count >= 0) + delete mEntries[count]; + + mEntries.clear(); +} + + +/* + * Find the central directory and read the contents. + * + * The fun thing about ZIP archives is that they may or may not be + * readable from start to end. In some cases, notably for archives + * that were written to stdout, the only length information is in the + * central directory at the end of the file. + * + * Of course, the central directory can be followed by a variable-length + * comment field, so we have to scan through it backwards. The comment + * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff + * itself, plus apparently sometimes people throw random junk on the end + * just for the fun of it. + * + * This is all a little wobbly. If the wrong value ends up in the EOCD + * area, we're hosed. This appears to be the way that everbody handles + * it though, so we're in pretty good company if this fails. + */ +status_t ZipFile::readCentralDir(void) +{ + status_t result = NO_ERROR; + unsigned char* buf = NULL; + off_t fileLength, seekStart; + long readAmount; + int i; + + fseek(mZipFp, 0, SEEK_END); + fileLength = ftell(mZipFp); + rewind(mZipFp); + + /* too small to be a ZIP archive? */ + if (fileLength < EndOfCentralDir::kEOCDLen) { + LOGD("Length is %ld -- too small\n", (long)fileLength); + result = INVALID_OPERATION; + goto bail; + } + + buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; + if (buf == NULL) { + LOGD("Failure allocating %d bytes for EOCD search", + EndOfCentralDir::kMaxEOCDSearch); + result = NO_MEMORY; + goto bail; + } + + if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { + seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; + readAmount = EndOfCentralDir::kMaxEOCDSearch; + } else { + seekStart = 0; + readAmount = (long) fileLength; + } + if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { + LOGD("Failure seeking to end of zip at %ld", (long) seekStart); + result = UNKNOWN_ERROR; + goto bail; + } + + /* read the last part of the file into the buffer */ + if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { + LOGD("short file? wanted %ld\n", readAmount); + result = UNKNOWN_ERROR; + goto bail; + } + + /* find the end-of-central-dir magic */ + for (i = readAmount - 4; i >= 0; i--) { + if (buf[i] == 0x50 && + ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) + { + LOGV("+++ Found EOCD at buf+%d\n", i); + break; + } + } + if (i < 0) { + LOGD("EOCD not found, not Zip\n"); + result = INVALID_OPERATION; + goto bail; + } + + /* extract eocd values */ + result = mEOCD.readBuf(buf + i, readAmount - i); + if (result != NO_ERROR) { + LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); + goto bail; + } + //mEOCD.dump(); + + if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || + mEOCD.mNumEntries != mEOCD.mTotalNumEntries) + { + LOGD("Archive spanning not supported\n"); + result = INVALID_OPERATION; + goto bail; + } + + /* + * So far so good. "mCentralDirSize" is the size in bytes of the + * central directory, so we can just seek back that far to find it. + * We can also seek forward mCentralDirOffset bytes from the + * start of the file. + * + * We're not guaranteed to have the rest of the central dir in the + * buffer, nor are we guaranteed that the central dir will have any + * sort of convenient size. We need to skip to the start of it and + * read the header, then the other goodies. + * + * The only thing we really need right now is the file comment, which + * we're hoping to preserve. + */ + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + LOGD("Failure seeking to central dir offset %ld\n", + mEOCD.mCentralDirOffset); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * Loop through and read the central dir entries. + */ + LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries); + int entry; + for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { + ZipEntry* pEntry = new ZipEntry; + + result = pEntry->initFromCDE(mZipFp); + if (result != NO_ERROR) { + LOGD("initFromCDE failed\n"); + delete pEntry; + goto bail; + } + + mEntries.add(pEntry); + } + + + /* + * If all went well, we should now be back at the EOCD. + */ + { + unsigned char checkBuf[4]; + if (fread(checkBuf, 1, 4, mZipFp) != 4) { + LOGD("EOCD check read failed\n"); + result = INVALID_OPERATION; + goto bail; + } + if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { + LOGD("EOCD read check failed\n"); + result = UNKNOWN_ERROR; + goto bail; + } + LOGV("+++ EOCD read check passed\n"); + } + +bail: + delete[] buf; + return result; +} + + +/* + * Add a new file to the archive. + * + * This requires creating and populating a ZipEntry structure, and copying + * the data into the file at the appropriate position. The "appropriate + * position" is the current location of the central directory, which we + * casually overwrite (we can put it back later). + * + * If we were concerned about safety, we would want to make all changes + * in a temp file and then overwrite the original after everything was + * safely written. Not really a concern for us. + */ +status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, + const char* storageName, int sourceType, int compressionMethod, + ZipEntry** ppEntry) +{ + ZipEntry* pEntry = NULL; + status_t result = NO_ERROR; + long lfhPosn, startPosn, endPosn, uncompressedLen; + FILE* inputFp = NULL; + unsigned long crc; + time_t modWhen; + + if (mReadOnly) + return INVALID_OPERATION; + + assert(compressionMethod == ZipEntry::kCompressDeflated || + compressionMethod == ZipEntry::kCompressStored); + + /* make sure we're in a reasonable state */ + assert(mZipFp != NULL); + assert(mEntries.size() == mEOCD.mTotalNumEntries); + + /* make sure it doesn't already exist */ + if (getEntryByName(storageName) != NULL) + return ALREADY_EXISTS; + + if (!data) { + inputFp = fopen(fileName, FILE_OPEN_RO); + if (inputFp == NULL) + return errnoToStatus(errno); + } + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + + pEntry = new ZipEntry; + pEntry->initNew(storageName, NULL); + + /* + * From here on out, failures are more interesting. + */ + mNeedCDRewrite = true; + + /* + * Write the LFH, even though it's still mostly blank. We need it + * as a place-holder. In theory the LFH isn't necessary, but in + * practice some utilities demand it. + */ + lfhPosn = ftell(mZipFp); + pEntry->mLFH.write(mZipFp); + startPosn = ftell(mZipFp); + + /* + * Copy the data in, possibly compressing it as we go. + */ + if (sourceType == ZipEntry::kCompressStored) { + if (compressionMethod == ZipEntry::kCompressDeflated) { + bool failed = false; + result = compressFpToFp(mZipFp, inputFp, data, size, &crc); + if (result != NO_ERROR) { + LOGD("compression failed, storing\n"); + failed = true; + } else { + /* + * Make sure it has compressed "enough". This probably ought + * to be set through an API call, but I don't expect our + * criteria to change over time. + */ + long src = inputFp ? ftell(inputFp) : size; + long dst = ftell(mZipFp) - startPosn; + if (dst + (dst / 10) > src) { + LOGD("insufficient compression (src=%ld dst=%ld), storing\n", + src, dst); + failed = true; + } + } + + if (failed) { + compressionMethod = ZipEntry::kCompressStored; + if (inputFp) rewind(inputFp); + fseek(mZipFp, startPosn, SEEK_SET); + /* fall through to kCompressStored case */ + } + } + /* handle "no compression" request, or failed compression from above */ + if (compressionMethod == ZipEntry::kCompressStored) { + if (inputFp) { + result = copyFpToFp(mZipFp, inputFp, &crc); + } else { + result = copyDataToFp(mZipFp, data, size, &crc); + } + if (result != NO_ERROR) { + // don't need to truncate; happens in CDE rewrite + LOGD("failed copying data in\n"); + goto bail; + } + } + + // currently seeked to end of file + uncompressedLen = inputFp ? ftell(inputFp) : size; + } else if (sourceType == ZipEntry::kCompressDeflated) { + /* we should support uncompressed-from-compressed, but it's not + * important right now */ + assert(compressionMethod == ZipEntry::kCompressDeflated); + + bool scanResult; + int method; + long compressedLen; + + scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, + &compressedLen, &crc); + if (!scanResult || method != ZipEntry::kCompressDeflated) { + LOGD("this isn't a deflated gzip file?"); + result = UNKNOWN_ERROR; + goto bail; + } + + result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); + if (result != NO_ERROR) { + LOGD("failed copying gzip data in\n"); + goto bail; + } + } else { + assert(false); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * We could write the "Data Descriptor", but there doesn't seem to + * be any point since we're going to go back and write the LFH. + * + * Update file offsets. + */ + endPosn = ftell(mZipFp); // seeked to end of compressed data + + /* + * Success! Fill out new values. + */ + pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, + compressionMethod); + modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); + pEntry->setModWhen(modWhen); + pEntry->setLFHOffset(lfhPosn); + mEOCD.mNumEntries++; + mEOCD.mTotalNumEntries++; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + mEOCD.mCentralDirOffset = endPosn; + + /* + * Go back and write the LFH. + */ + if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + pEntry->mLFH.write(mZipFp); + + /* + * Add pEntry to the list. + */ + mEntries.add(pEntry); + if (ppEntry != NULL) + *ppEntry = pEntry; + pEntry = NULL; + +bail: + if (inputFp != NULL) + fclose(inputFp); + delete pEntry; + return result; +} + +/* + * Add an entry by copying it from another zip file. If "padding" is + * nonzero, the specified number of bytes will be added to the "extra" + * field in the header. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ +status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, + int padding, ZipEntry** ppEntry) +{ + ZipEntry* pEntry = NULL; + status_t result; + long lfhPosn, endPosn; + + if (mReadOnly) + return INVALID_OPERATION; + + /* make sure we're in a reasonable state */ + assert(mZipFp != NULL); + assert(mEntries.size() == mEOCD.mTotalNumEntries); + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + + pEntry = new ZipEntry; + if (pEntry == NULL) { + result = NO_MEMORY; + goto bail; + } + + result = pEntry->initFromExternal(pSourceZip, pSourceEntry); + if (result != NO_ERROR) + goto bail; + if (padding != 0) { + result = pEntry->addPadding(padding); + if (result != NO_ERROR) + goto bail; + } + + /* + * From here on out, failures are more interesting. + */ + mNeedCDRewrite = true; + + /* + * Write the LFH. Since we're not recompressing the data, we already + * have all of the fields filled out. + */ + lfhPosn = ftell(mZipFp); + pEntry->mLFH.write(mZipFp); + + /* + * Copy the data over. + * + * If the "has data descriptor" flag is set, we want to copy the DD + * fields as well. This is a fixed-size area immediately following + * the data. + */ + if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) + { + result = UNKNOWN_ERROR; + goto bail; + } + + off_t copyLen; + copyLen = pSourceEntry->getCompressedLen(); + if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) + copyLen += ZipEntry::kDataDescriptorLen; + + if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) + != NO_ERROR) + { + LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * Update file offsets. + */ + endPosn = ftell(mZipFp); + + /* + * Success! Fill out new values. + */ + pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset + mEOCD.mNumEntries++; + mEOCD.mTotalNumEntries++; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + mEOCD.mCentralDirOffset = endPosn; + + /* + * Add pEntry to the list. + */ + mEntries.add(pEntry); + if (ppEntry != NULL) + *ppEntry = pEntry; + pEntry = NULL; + + result = NO_ERROR; + +bail: + delete pEntry; + return result; +} + +/* + * Copy all of the bytes in "src" to "dst". + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the data. + */ +status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32) +{ + unsigned char tmpBuf[32768]; + size_t count; + + *pCRC32 = crc32(0L, Z_NULL, 0); + + while (1) { + count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); + if (ferror(srcFp) || ferror(dstFp)) + return errnoToStatus(errno); + if (count == 0) + break; + + *pCRC32 = crc32(*pCRC32, tmpBuf, count); + + if (fwrite(tmpBuf, 1, count, dstFp) != count) { + LOGD("fwrite %d bytes failed\n", (int) count); + return UNKNOWN_ERROR; + } + } + + return NO_ERROR; +} + +/* + * Copy all of the bytes in "src" to "dst". + * + * On exit, "dstFp" will be seeked immediately past the data. + */ +status_t ZipFile::copyDataToFp(FILE* dstFp, + const void* data, size_t size, unsigned long* pCRC32) +{ + size_t count; + + *pCRC32 = crc32(0L, Z_NULL, 0); + if (size > 0) { + *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); + if (fwrite(data, 1, size, dstFp) != size) { + LOGD("fwrite %d bytes failed\n", (int) size); + return UNKNOWN_ERROR; + } + } + + return NO_ERROR; +} + +/* + * Copy some of the bytes in "src" to "dst". + * + * If "pCRC32" is NULL, the CRC will not be computed. + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the data just written. + */ +status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, + unsigned long* pCRC32) +{ + unsigned char tmpBuf[32768]; + size_t count; + + if (pCRC32 != NULL) + *pCRC32 = crc32(0L, Z_NULL, 0); + + while (length) { + long readSize; + + readSize = sizeof(tmpBuf); + if (readSize > length) + readSize = length; + + count = fread(tmpBuf, 1, readSize, srcFp); + if ((long) count != readSize) { // error or unexpected EOF + LOGD("fread %d bytes failed\n", (int) readSize); + return UNKNOWN_ERROR; + } + + if (pCRC32 != NULL) + *pCRC32 = crc32(*pCRC32, tmpBuf, count); + + if (fwrite(tmpBuf, 1, count, dstFp) != count) { + LOGD("fwrite %d bytes failed\n", (int) count); + return UNKNOWN_ERROR; + } + + length -= readSize; + } + + return NO_ERROR; +} + +/* + * Compress all of the data in "srcFp" and write it to "dstFp". + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the compressed data. + */ +status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, + const void* data, size_t size, unsigned long* pCRC32) +{ + status_t result = NO_ERROR; + const size_t kBufSize = 32768; + unsigned char* inBuf = NULL; + unsigned char* outBuf = NULL; + z_stream zstream; + bool atEof = false; // no feof() aviailable yet + unsigned long crc; + int zerr; + + /* + * Create an input buffer and an output buffer. + */ + inBuf = new unsigned char[kBufSize]; + outBuf = new unsigned char[kBufSize]; + if (inBuf == NULL || outBuf == NULL) { + result = NO_MEMORY; + goto bail; + } + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + zstream.data_type = Z_UNKNOWN; + + zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (zerr != Z_OK) { + result = UNKNOWN_ERROR; + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + crc = crc32(0L, Z_NULL, 0); + + /* + * Loop while we have data. + */ + do { + size_t getSize; + int flush; + + /* only read if the input buffer is empty */ + if (zstream.avail_in == 0 && !atEof) { + LOGV("+++ reading %d bytes\n", (int)kBufSize); + if (data) { + getSize = size > kBufSize ? kBufSize : size; + memcpy(inBuf, data, getSize); + data = ((const char*)data) + getSize; + size -= getSize; + } else { + getSize = fread(inBuf, 1, kBufSize, srcFp); + if (ferror(srcFp)) { + LOGD("deflate read failed (errno=%d)\n", errno); + goto z_bail; + } + } + if (getSize < kBufSize) { + LOGV("+++ got %d bytes, EOF reached\n", + (int)getSize); + atEof = true; + } + + crc = crc32(crc, inBuf, getSize); + + zstream.next_in = inBuf; + zstream.avail_in = getSize; + } + + if (atEof) + flush = Z_FINISH; /* tell zlib that we're done */ + else + flush = Z_NO_FLUSH; /* more to come! */ + + zerr = deflate(&zstream, flush); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib deflate call failed (zerr=%d)\n", zerr); + result = UNKNOWN_ERROR; + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) + { + LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); + if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) != + (size_t)(zstream.next_out - outBuf)) + { + LOGD("write %d failed in deflate\n", + (int) (zstream.next_out - outBuf)); + goto z_bail; + } + + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + *pCRC32 = crc; + +z_bail: + deflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] inBuf; + delete[] outBuf; + + return result; +} + +/* + * Mark an entry as deleted. + * + * We will eventually need to crunch the file down, but if several files + * are being removed (perhaps as part of an "update" process) we can make + * things considerably faster by deferring the removal to "flush" time. + */ +status_t ZipFile::remove(ZipEntry* pEntry) +{ + /* + * Should verify that pEntry is actually part of this archive, and + * not some stray ZipEntry from a different file. + */ + + /* mark entry as deleted, and mark archive as dirty */ + pEntry->setDeleted(); + mNeedCDRewrite = true; + return NO_ERROR; +} + +/* + * Flush any pending writes. + * + * In particular, this will crunch out deleted entries, and write the + * Central Directory and EOCD if we have stomped on them. + */ +status_t ZipFile::flush(void) +{ + status_t result = NO_ERROR; + long eocdPosn; + int i, count; + + if (mReadOnly) + return INVALID_OPERATION; + if (!mNeedCDRewrite) + return NO_ERROR; + + assert(mZipFp != NULL); + + result = crunchArchive(); + if (result != NO_ERROR) + return result; + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) + return UNKNOWN_ERROR; + + count = mEntries.size(); + for (i = 0; i < count; i++) { + ZipEntry* pEntry = mEntries[i]; + pEntry->mCDE.write(mZipFp); + } + + eocdPosn = ftell(mZipFp); + mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; + + mEOCD.write(mZipFp); + + /* + * If we had some stuff bloat up during compression and get replaced + * with plain files, or if we deleted some entries, there's a lot + * of wasted space at the end of the file. Remove it now. + */ + if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { + LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); + // not fatal + } + + /* should we clear the "newly added" flag in all entries now? */ + + mNeedCDRewrite = false; + return NO_ERROR; +} + +/* + * Crunch deleted files out of an archive by shifting the later files down. + * + * Because we're not using a temp file, we do the operation inside the + * current file. + */ +status_t ZipFile::crunchArchive(void) +{ + status_t result = NO_ERROR; + int i, count; + long delCount, adjust; + +#if 0 + printf("CONTENTS:\n"); + for (i = 0; i < (int) mEntries.size(); i++) { + printf(" %d: lfhOff=%ld del=%d\n", + i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); + } + printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); +#endif + + /* + * Roll through the set of files, shifting them as appropriate. We + * could probably get a slight performance improvement by sliding + * multiple files down at once (because we could use larger reads + * when operating on batches of small files), but it's not that useful. + */ + count = mEntries.size(); + delCount = adjust = 0; + for (i = 0; i < count; i++) { + ZipEntry* pEntry = mEntries[i]; + long span; + + if (pEntry->getLFHOffset() != 0) { + long nextOffset; + + /* Get the length of this entry by finding the offset + * of the next entry. Directory entries don't have + * file offsets, so we need to find the next non-directory + * entry. + */ + nextOffset = 0; + for (int ii = i+1; nextOffset == 0 && ii < count; ii++) + nextOffset = mEntries[ii]->getLFHOffset(); + if (nextOffset == 0) + nextOffset = mEOCD.mCentralDirOffset; + span = nextOffset - pEntry->getLFHOffset(); + + assert(span >= ZipEntry::LocalFileHeader::kLFHLen); + } else { + /* This is a directory entry. It doesn't have + * any actual file contents, so there's no need to + * move anything. + */ + span = 0; + } + + //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", + // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); + + if (pEntry->getDeleted()) { + adjust += span; + delCount++; + + delete pEntry; + mEntries.removeAt(i); + + /* adjust loop control */ + count--; + i--; + } else if (span != 0 && adjust > 0) { + /* shuffle this entry back */ + //printf("+++ Shuffling '%s' back %ld\n", + // pEntry->getFileName(), adjust); + result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, + pEntry->getLFHOffset(), span); + if (result != NO_ERROR) { + /* this is why you use a temp file */ + LOGE("error during crunch - archive is toast\n"); + return result; + } + + pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); + } + } + + /* + * Fix EOCD info. We have to wait until the end to do some of this + * because we use mCentralDirOffset to determine "span" for the + * last entry. + */ + mEOCD.mCentralDirOffset -= adjust; + mEOCD.mNumEntries -= delCount; + mEOCD.mTotalNumEntries -= delCount; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + + assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); + assert(mEOCD.mNumEntries == count); + + return result; +} + +/* + * Works like memmove(), but on pieces of a file. + */ +status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) +{ + if (dst == src || n <= 0) + return NO_ERROR; + + unsigned char readBuf[32768]; + + if (dst < src) { + /* shift stuff toward start of file; must read from start */ + while (n != 0) { + size_t getSize = sizeof(readBuf); + if (getSize > n) + getSize = n; + + if (fseek(fp, (long) src, SEEK_SET) != 0) { + LOGD("filemove src seek %ld failed\n", (long) src); + return UNKNOWN_ERROR; + } + + if (fread(readBuf, 1, getSize, fp) != getSize) { + LOGD("filemove read %ld off=%ld failed\n", + (long) getSize, (long) src); + return UNKNOWN_ERROR; + } + + if (fseek(fp, (long) dst, SEEK_SET) != 0) { + LOGD("filemove dst seek %ld failed\n", (long) dst); + return UNKNOWN_ERROR; + } + + if (fwrite(readBuf, 1, getSize, fp) != getSize) { + LOGD("filemove write %ld off=%ld failed\n", + (long) getSize, (long) dst); + return UNKNOWN_ERROR; + } + + src += getSize; + dst += getSize; + n -= getSize; + } + } else { + /* shift stuff toward end of file; must read from end */ + assert(false); // write this someday, maybe + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + +/* + * Get the modification time from a file descriptor. + */ +time_t ZipFile::getModTime(int fd) +{ + struct stat sb; + + if (fstat(fd, &sb) < 0) { + LOGD("HEY: fstat on fd %d failed\n", fd); + return (time_t) -1; + } + + return sb.st_mtime; +} + + +#if 0 /* this is a bad idea */ +/* + * Get a copy of the Zip file descriptor. + * + * We don't allow this if the file was opened read-write because we tend + * to leave the file contents in an uncertain state between calls to + * flush(). The duplicated file descriptor should only be valid for reads. + */ +int ZipFile::getZipFd(void) const +{ + if (!mReadOnly) + return INVALID_OPERATION; + assert(mZipFp != NULL); + + int fd; + fd = dup(fileno(mZipFp)); + if (fd < 0) { + LOGD("didn't work, errno=%d\n", errno); + } + + return fd; +} +#endif + + +#if 0 +/* + * Expand data. + */ +bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const +{ + return false; +} +#endif + +// free the memory when you're done +void* ZipFile::uncompress(const ZipEntry* entry) +{ + size_t unlen = entry->getUncompressedLen(); + size_t clen = entry->getCompressedLen(); + + void* buf = malloc(unlen); + if (buf == NULL) { + return NULL; + } + + fseek(mZipFp, 0, SEEK_SET); + + off_t offset = entry->getFileOffset(); + if (fseek(mZipFp, offset, SEEK_SET) != 0) { + goto bail; + } + + switch (entry->getCompressionMethod()) + { + case ZipEntry::kCompressStored: { + ssize_t amt = fread(buf, 1, unlen, mZipFp); + if (amt != (ssize_t)unlen) { + goto bail; + } +#if 0 + printf("data...\n"); + const unsigned char* p = (unsigned char*)buf; + const unsigned char* end = p+unlen; + for (int i=0; i<32 && p < end; i++) { + printf("0x%08x ", (int)(offset+(i*0x10))); + for (int j=0; j<0x10 && p < end; j++) { + printf(" %02x", *p); + p++; + } + printf("\n"); + } +#endif + + } + break; + case ZipEntry::kCompressDeflated: { + if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { + goto bail; + } + } + break; + default: + goto bail; + } + return buf; + +bail: + free(buf); + return NULL; +} + + +/* + * =========================================================================== + * ZipFile::EndOfCentralDir + * =========================================================================== + */ + +/* + * Read the end-of-central-dir fields. + * + * "buf" should be positioned at the EOCD signature, and should contain + * the entire EOCD area including the comment. + */ +status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) +{ + /* don't allow re-use */ + assert(mComment == NULL); + + if (len < kEOCDLen) { + /* looks like ZIP file got truncated */ + LOGD(" Zip EOCD: expected >= %d bytes, found %d\n", + kEOCDLen, len); + return INVALID_OPERATION; + } + + /* this should probably be an assert() */ + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) + return UNKNOWN_ERROR; + + mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); + mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); + mNumEntries = ZipEntry::getShortLE(&buf[0x08]); + mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); + mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); + mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); + mCommentLen = ZipEntry::getShortLE(&buf[0x14]); + + // TODO: validate mCentralDirOffset + + if (mCommentLen > 0) { + if (kEOCDLen + mCommentLen > len) { + LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n", + kEOCDLen, mCommentLen, len); + return UNKNOWN_ERROR; + } + mComment = new unsigned char[mCommentLen]; + memcpy(mComment, buf + kEOCDLen, mCommentLen); + } + + return NO_ERROR; +} + +/* + * Write an end-of-central-directory section. + */ +status_t ZipFile::EndOfCentralDir::write(FILE* fp) +{ + unsigned char buf[kEOCDLen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mDiskNumber); + ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); + ZipEntry::putShortLE(&buf[0x08], mNumEntries); + ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); + ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); + ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); + ZipEntry::putShortLE(&buf[0x14], mCommentLen); + + if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) + return UNKNOWN_ERROR; + if (mCommentLen > 0) { + assert(mComment != NULL); + if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +/* + * Dump the contents of an EndOfCentralDir object. + */ +void ZipFile::EndOfCentralDir::dump(void) const +{ + LOGD(" EndOfCentralDir contents:\n"); + LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n", + mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); + LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n", + mCentralDirSize, mCentralDirOffset, mCommentLen); +} + diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp new file mode 100644 index 000000000..d312dafad --- /dev/null +++ b/libs/utils/ZipFileCRO.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 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 "utils/ZipFileCRO.h" +#include "utils/ZipFileRO.h" + +using namespace android; + +ZipFileCRO ZipFileXRO_open(const char* path) { + ZipFileRO* zip = new ZipFileRO(); + if (zip->open(path) == NO_ERROR) { + return (ZipFileCRO)zip; + } + return NULL; +} + +void ZipFileCRO_destroy(ZipFileCRO zipToken) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + delete zip; +} + +ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, + const char* fileName) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + return (ZipEntryCRO)zip->findEntryByName(fileName); +} + +bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, + int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + ZipEntryRO entry = (ZipEntryRO)entryToken; + return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, + pModWhen, pCrc32); +} + +bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + ZipEntryRO entry = (ZipEntryRO)entryToken; + return zip->uncompressEntry(entry, fd); +} diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp new file mode 100644 index 000000000..ae8c71972 --- /dev/null +++ b/libs/utils/ZipFileRO.cpp @@ -0,0 +1,724 @@ +/* + * Copyright (C) 2007 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. + */ + +// +// Read-only access to Zip archives, with minimal heap allocation. +// +#define LOG_TAG "zipro" +//#define LOG_NDEBUG 0 +#include "utils/ZipFileRO.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include + +#include +#include +#include +#include + +using namespace android; + +/* + * Zip file constants. + */ +#define kEOCDSignature 0x06054b50 +#define kEOCDLen 22 +#define kEOCDNumEntries 8 // offset to #of entries in file +#define kEOCDFileOffset 16 // offset to central directory + +#define kMaxCommentLen 65535 // longest possible in ushort +#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) + +#define kLFHSignature 0x04034b50 +#define kLFHLen 30 // excluding variable-len fields +#define kLFHNameLen 26 // offset to filename length +#define kLFHExtraLen 28 // offset to extra length + +#define kCDESignature 0x02014b50 +#define kCDELen 46 // excluding variable-len fields +#define kCDEMethod 10 // offset to compression method +#define kCDEModWhen 12 // offset to modification timestamp +#define kCDECRC 16 // offset to entry CRC +#define kCDECompLen 20 // offset to compressed length +#define kCDEUncompLen 24 // offset to uncompressed length +#define kCDENameLen 28 // offset to filename length +#define kCDEExtraLen 30 // offset to extra length +#define kCDECommentLen 32 // offset to comment length +#define kCDELocalOffset 42 // offset to local hdr + +/* + * The values we return for ZipEntryRO use 0 as an invalid value, so we + * want to adjust the hash table index by a fixed amount. Using a large + * value helps insure that people don't mix & match arguments, e.g. to + * findEntryByIndex(). + */ +#define kZipEntryAdj 10000 + +/* + * Convert a ZipEntryRO to a hash table index, verifying that it's in a + * valid range. + */ +int ZipFileRO::entryToIndex(const ZipEntryRO entry) const +{ + long ent = ((long) entry) - kZipEntryAdj; + if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { + LOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); + return -1; + } + return ent; +} + + +/* + * Open the specified file read-only. We memory-map the entire thing and + * close the file before returning. + */ +status_t ZipFileRO::open(const char* zipFileName) +{ + int fd = -1; + off_t length; + + assert(mFileMap == NULL); + + /* + * Open and map the specified file. + */ + fd = ::open(zipFileName, O_RDONLY); + if (fd < 0) { + LOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); + return NAME_NOT_FOUND; + } + + length = lseek(fd, 0, SEEK_END); + if (length < 0) { + close(fd); + return UNKNOWN_ERROR; + } + + mFileMap = new FileMap(); + if (mFileMap == NULL) { + close(fd); + return NO_MEMORY; + } + if (!mFileMap->create(zipFileName, fd, 0, length, true)) { + LOGW("Unable to map '%s': %s\n", zipFileName, strerror(errno)); + close(fd); + return UNKNOWN_ERROR; + } + + mFd = fd; + + /* + * Got it mapped, verify it and create data structures for fast access. + */ + if (!parseZipArchive()) { + mFileMap->release(); + mFileMap = NULL; + return UNKNOWN_ERROR; + } + + return OK; +} + +/* + * Parse the Zip archive, verifying its contents and initializing internal + * data structures. + */ +bool ZipFileRO::parseZipArchive(void) +{ +#define CHECK_OFFSET(_off) { \ + if ((unsigned int) (_off) >= maxOffset) { \ + LOGE("ERROR: bad offset %u (max %d): %s\n", \ + (unsigned int) (_off), maxOffset, #_off); \ + goto bail; \ + } \ + } + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + const unsigned char* ptr; + size_t length = mFileMap->getDataLength(); + bool result = false; + unsigned int i, numEntries, cdOffset; + unsigned int val; + + /* + * The first 4 bytes of the file will either be the local header + * signature for the first file (kLFHSignature) or, if the archive doesn't + * have any files in it, the end-of-central-directory signature + * (kEOCDSignature). + */ + val = get4LE(basePtr); + if (val == kEOCDSignature) { + LOGI("Found Zip archive, but it looks empty\n"); + goto bail; + } else if (val != kLFHSignature) { + LOGV("Not a Zip archive (found 0x%08x)\n", val); + goto bail; + } + + /* + * Find the EOCD. We'll find it immediately unless they have a file + * comment. + */ + ptr = basePtr + length - kEOCDLen; + + while (ptr >= basePtr) { + if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature) + break; + ptr--; + } + if (ptr < basePtr) { + LOGI("Could not find end-of-central-directory in Zip\n"); + goto bail; + } + + /* + * There are two interesting items in the EOCD block: the number of + * entries in the file, and the file offset of the start of the + * central directory. + * + * (There's actually a count of the #of entries in this file, and for + * all files which comprise a spanned archive, but for our purposes + * we're only interested in the current file. Besides, we expect the + * two to be equivalent for our stuff.) + */ + numEntries = get2LE(ptr + kEOCDNumEntries); + cdOffset = get4LE(ptr + kEOCDFileOffset); + + /* valid offsets are [0,EOCD] */ + unsigned int maxOffset; + maxOffset = (ptr - basePtr) +1; + + LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset); + if (numEntries == 0 || cdOffset >= length) { + LOGW("Invalid entries=%d offset=%d (len=%zd)\n", + numEntries, cdOffset, length); + goto bail; + } + + /* + * Create hash table. We have a minimum 75% load factor, possibly as + * low as 50% after we round off to a power of 2. + */ + mNumEntries = numEntries; + mHashTableSize = roundUpPower2(1 + ((numEntries * 4) / 3)); + mHashTable = (HashEntry*) calloc(1, sizeof(HashEntry) * mHashTableSize); + + /* + * Walk through the central directory, adding entries to the hash + * table. + */ + ptr = basePtr + cdOffset; + for (i = 0; i < numEntries; i++) { + unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; + const unsigned char* localHdr; + unsigned int hash; + + if (get4LE(ptr) != kCDESignature) { + LOGW("Missed a central dir sig (at %d)\n", i); + goto bail; + } + if (ptr + kCDELen > basePtr + length) { + LOGW("Ran off the end (at %d)\n", i); + goto bail; + } + + localHdrOffset = get4LE(ptr + kCDELocalOffset); + CHECK_OFFSET(localHdrOffset); + fileNameLen = get2LE(ptr + kCDENameLen); + extraLen = get2LE(ptr + kCDEExtraLen); + commentLen = get2LE(ptr + kCDECommentLen); + + //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n", + // i, localHdrOffset, fileNameLen, extraLen, commentLen); + //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen); + + /* add the CDE filename to the hash table */ + hash = computeHash((const char*)ptr + kCDELen, fileNameLen); + addToHash((const char*)ptr + kCDELen, fileNameLen, hash); + + localHdr = basePtr + localHdrOffset; + if (get4LE(localHdr) != kLFHSignature) { + LOGW("Bad offset to local header: %d (at %d)\n", + localHdrOffset, i); + goto bail; + } + + ptr += kCDELen + fileNameLen + extraLen + commentLen; + CHECK_OFFSET(ptr - basePtr); + } + + result = true; + +bail: + return result; +#undef CHECK_OFFSET +} + + +/* + * Simple string hash function for non-null-terminated strings. + */ +/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) +{ + unsigned int hash = 0; + + while (len--) + hash = hash * 31 + *str++; + + return hash; +} + +/* + * Add a new entry to the hash table. + */ +void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) +{ + int ent = hash & (mHashTableSize-1); + + /* + * We over-allocate the table, so we're guaranteed to find an empty slot. + */ + while (mHashTable[ent].name != NULL) + ent = (ent + 1) & (mHashTableSize-1); + + mHashTable[ent].name = str; + mHashTable[ent].nameLen = strLen; +} + +/* + * Find a matching entry. + * + * Returns 0 if not found. + */ +ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const +{ + int nameLen = strlen(fileName); + unsigned int hash = computeHash(fileName, nameLen); + int ent = hash & (mHashTableSize-1); + + while (mHashTable[ent].name != NULL) { + if (mHashTable[ent].nameLen == nameLen && + memcmp(mHashTable[ent].name, fileName, nameLen) == 0) + { + /* match */ + return (ZipEntryRO) (ent + kZipEntryAdj); + } + + ent = (ent + 1) & (mHashTableSize-1); + } + + return NULL; +} + +/* + * Find the Nth entry. + * + * This currently involves walking through the sparse hash table, counting + * non-empty entries. If we need to speed this up we can either allocate + * a parallel lookup table or (perhaps better) provide an iterator interface. + */ +ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= mNumEntries) { + LOGW("Invalid index %d\n", idx); + return NULL; + } + + for (int ent = 0; ent < mHashTableSize; ent++) { + if (mHashTable[ent].name != NULL) { + if (idx-- == 0) + return (ZipEntryRO) (ent + kZipEntryAdj); + } + } + + return NULL; +} + +/* + * Get the useful fields from the zip entry. + * + * Returns "false" if the offsets to the fields or the contents of the fields + * appear to be bogus. + */ +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const +{ + int ent = entryToIndex(entry); + if (ent < 0) + return false; + + /* + * Recover the start of the central directory entry from the filename + * pointer. + */ + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + const unsigned char* ptr = (const unsigned char*) mHashTable[ent].name; + size_t zipLength = mFileMap->getDataLength(); + + ptr -= kCDELen; + + int method = get2LE(ptr + kCDEMethod); + if (pMethod != NULL) + *pMethod = method; + + if (pModWhen != NULL) + *pModWhen = get4LE(ptr + kCDEModWhen); + if (pCrc32 != NULL) + *pCrc32 = get4LE(ptr + kCDECRC); + + /* + * We need to make sure that the lengths are not so large that somebody + * trying to map the compressed or uncompressed data runs off the end + * of the mapped region. + */ + unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset); + if (localHdrOffset + kLFHLen >= zipLength) { + LOGE("ERROR: bad local hdr offset in zip\n"); + return false; + } + const unsigned char* localHdr = basePtr + localHdrOffset; + off_t dataOffset = localHdrOffset + kLFHLen + + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen); + if ((unsigned long) dataOffset >= zipLength) { + LOGE("ERROR: bad data offset in zip\n"); + return false; + } + + if (pCompLen != NULL) { + *pCompLen = get4LE(ptr + kCDECompLen); + if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) { + LOGE("ERROR: bad compressed length in zip\n"); + return false; + } + } + if (pUncompLen != NULL) { + *pUncompLen = get4LE(ptr + kCDEUncompLen); + if (*pUncompLen < 0) { + LOGE("ERROR: negative uncompressed length in zip\n"); + return false; + } + if (method == kCompressStored && + (size_t)(dataOffset + *pUncompLen) >= zipLength) + { + LOGE("ERROR: bad uncompressed length in zip\n"); + return false; + } + } + + if (pOffset != NULL) { + *pOffset = dataOffset; + } + return true; +} + +/* + * Copy the entry's filename to the buffer. + */ +int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) + const +{ + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + int nameLen = mHashTable[ent].nameLen; + if (bufLen < nameLen+1) + return nameLen+1; + + memcpy(buffer, mHashTable[ent].name, nameLen); + buffer[nameLen] = '\0'; + return 0; +} + +/* + * Create a new FileMap object that spans the data in "entry". + */ +FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const +{ + /* + * TODO: the efficient way to do this is to modify FileMap to allow + * sub-regions of a file to be mapped. A reference-counting scheme + * can manage the base memory mapping. For now, we just create a brand + * new mapping off of the Zip archive file descriptor. + */ + + FileMap* newMap; + long compLen; + off_t offset; + + if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) + return NULL; + + newMap = new FileMap(); + if (!newMap->create(mFileMap->getFileName(), mFd, offset, compLen, true)) { + newMap->release(); + return NULL; + } + + return newMap; +} + +/* + * Uncompress an entry, in its entirety, into the provided output buffer. + * + * This doesn't verify the data's CRC, which might be useful for + * uncompressed data. The caller should be able to manage it. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const +{ + const int kSequentialMin = 32768; + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + int method; + long uncompLen, compLen; + off_t offset; + + getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + + /* + * Experiment with madvise hint. When we want to uncompress a file, + * we pull some stuff out of the central dir entry and then hit a + * bunch of compressed or uncompressed data sequentially. The CDE + * visit will cause a limited amount of read-ahead because it's at + * the end of the file. We could end up doing lots of extra disk + * access if the file we're prying open is small. Bottom line is we + * probably don't want to turn MADV_SEQUENTIAL on and leave it on. + * + * So, if the compressed size of the file is above a certain minimum + * size, temporarily boost the read-ahead in the hope that the extra + * pair of system calls are negated by a reduction in page faults. + */ + if (compLen > kSequentialMin) + mFileMap->advise(FileMap::SEQUENTIAL); + + if (method == kCompressStored) { + memcpy(buffer, basePtr + offset, uncompLen); + } else { + if (!inflateBuffer(buffer, basePtr + offset, uncompLen, compLen)) + goto bail; + } + + if (compLen > kSequentialMin) + mFileMap->advise(FileMap::NORMAL); + + result = true; + +bail: + return result; +} + +/* + * Uncompress an entry, in its entirety, to an open file descriptor. + * + * This doesn't verify the data's CRC, but probably should. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const +{ + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + int method; + long uncompLen, compLen; + off_t offset; + + getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + + if (method == kCompressStored) { + ssize_t actual; + + actual = write(fd, basePtr + offset, uncompLen); + if (actual < 0) { + LOGE("Write failed: %s\n", strerror(errno)); + goto bail; + } else if (actual != uncompLen) { + LOGE("Partial write during uncompress (%d of %ld)\n", + (int)actual, uncompLen); + goto bail; + } else { + LOGI("+++ successful write\n"); + } + } else { + if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen)) + goto bail; + } + + result = true; + +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to another. + */ +/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, + long uncompLen, long compLen) +{ + bool result = false; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) outBuf; + zstream.avail_out = uncompLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_FINISH); + if (zerr != Z_STREAM_END) { + LOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* paranoia */ + if ((long) zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to an open file descriptor. + */ +/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, + long uncompLen, long compLen) +{ + bool result = false; + const int kWriteBufSize = 32768; + unsigned char writeBuf[kWriteBufSize]; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) writeBuf; + zstream.avail_out = sizeof(writeBuf); + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have more to do. + */ + do { + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) + { + long writeSize = zstream.next_out - writeBuf; + int cc = write(fd, writeBuf, writeSize); + if (cc != (int) writeSize) { + LOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); + goto z_bail; + } + + zstream.next_out = writeBuf; + zstream.avail_out = sizeof(writeBuf); + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + /* paranoia */ + if ((long) zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp new file mode 100644 index 000000000..bfbacfecd --- /dev/null +++ b/libs/utils/ZipUtils.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2007 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. + */ + +// +// Misc zip/gzip utility functions. +// + +#define LOG_TAG "ziputil" + +#include "utils/ZipUtils.h" +#include "utils/ZipFileRO.h" +#include "utils/Log.h" + +#include +#include +#include + +#include + +using namespace android; + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * "fd" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + LOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = read(fd, readBuf, getSize); + if (cc != (int) getSize) { + LOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * (This is a clone of the previous function, but it takes a FILE* instead + * of an fd. We could pass fileno(fd) to the above, but we can run into + * trouble when "fp" has a different notion of what fd's file position is.) + * + * "fp" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + LOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = fread(readBuf, getSize, 1, fp); + if (cc != (int) getSize) { + LOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Look at the contents of a gzip archive. We want to know where the + * data starts, and how long it will be after it is uncompressed. + * + * We expect to find the CRC and length as the last 8 bytes on the file. + * This is a pretty reasonable thing to expect for locally-compressed + * files, but there's a small chance that some extra padding got thrown + * on (the man page talks about compressed data written to tape). We + * don't currently deal with that here. If "gzip -l" whines, we're going + * to fail too. + * + * On exit, "fp" is pointing at the start of the compressed data. + */ +/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32) +{ + enum { // flags + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + }; + int ic; + int method, flags; + int i; + + ic = getc(fp); + if (ic != 0x1f || getc(fp) != 0x8b) + return false; // not gzip + method = getc(fp); + flags = getc(fp); + + /* quick sanity checks */ + if (method == EOF || flags == EOF) + return false; + if (method != ZipFileRO::kCompressDeflated) + return false; + + /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */ + for (i = 0; i < 6; i++) + (void) getc(fp); + /* consume "extra" field, if present */ + if ((flags & FEXTRA) != 0) { + int len; + + len = getc(fp); + len |= getc(fp) << 8; + while (len-- && getc(fp) != EOF) + ; + } + /* consume filename, if present */ + if ((flags & FNAME) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume comment, if present */ + if ((flags & FCOMMENT) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume 16-bit header CRC, if present */ + if ((flags & FHCRC) != 0) { + (void) getc(fp); + (void) getc(fp); + } + + if (feof(fp) || ferror(fp)) + return false; + + /* seek to the end; CRC and length are in the last 8 bytes */ + long curPosn = ftell(fp); + unsigned char buf[8]; + fseek(fp, -8, SEEK_END); + *pCompressedLen = ftell(fp) - curPosn; + + if (fread(buf, 1, 8, fp) != 8) + return false; + /* seek back to start of compressed data */ + fseek(fp, curPosn, SEEK_SET); + + *pCompressionMethod = method; + *pCRC32 = ZipFileRO::get4LE(&buf[0]); + *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); + + return true; +} + diff --git a/libs/utils/characterData.h b/libs/utils/characterData.h new file mode 100644 index 000000000..e931d995e --- /dev/null +++ b/libs/utils/characterData.h @@ -0,0 +1,730 @@ +/* + * Copyright (C) 2008 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. + */ + +// Automatically generated on 07-11-2006 by make-CharacterDataC +// DO NOT EDIT DIRECTLY +namespace CharacterData { + + // Structure containing an array of ranges + struct Range { + int length; + const uint32_t* array; + }; + + // For Latin1 characters just index into this array to get the index and decomposition + static const uint16_t LATIN1_DATA[] = { + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0002, 0x0003, 0x0002, 0x0004, 0x0003, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0003, 0x0003, 0x0002, + 0x0005, 0x0006, 0x0006, 0x0007, 0x0008, 0x0007, 0x0006, 0x0006, + 0x0009, 0x000A, 0x0006, 0x000B, 0x000C, 0x000D, 0x000C, 0x000C, + 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, + 0x0016, 0x0017, 0x000C, 0x0006, 0x0018, 0x0019, 0x001A, 0x0006, + 0x0006, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, + 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, + 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0006, 0x0036, 0x0037, 0x0038, + 0x0037, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0035, 0x0019, 0x0036, 0x0019, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x5853, 0x0006, 0x0008, 0x0008, 0x0008, 0x0008, 0x0054, 0x0054, + 0x1037, 0x0054, 0x7855, 0x0056, 0x0019, 0x0057, 0x0054, 0x1037, + 0x0058, 0x0059, 0x785A, 0x785B, 0x1037, 0x105C, 0x0054, 0x0006, + 0x1037, 0x785D, 0x7855, 0x005E, 0x305F, 0x305F, 0x305F, 0x0006, + 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0860, + 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, + 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0019, + 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0055, + 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0861, + 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, + 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0019, + 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0862 + }; + + // Each of these arrays is stripped into ranges. In order to build the arrays, each + // codepoint was bit-shifted so that even and odd characters were separated into different + // arrays. The identifier of each array is the top byte after bit-shifting. + // The numbers stored in the array are the bit-shifted codepoint, the decomposition, and an + // index into another array of all possible packed data values. The top 16 bits are the + // codepoint and the bottom 16 are the decomposition and index. The top 5 bits for the decomposition + // and the rest for the index. + static const uint32_t a0[] = { + 0x00800863, 0x00880063, 0x00890863, 0x00930063, 0x00940863, 0x00980864, 0x00991063, 0x009A0863, + 0x009C0055, 0x009D0865, 0x00A01065, 0x00A10065, 0x00A20865, 0x00A50063, 0x00A60863, 0x00A90063, + 0x00AA0863, 0x00B30063, 0x00B40863, 0x00BC0866, 0x00BD0865, 0x00C00055, 0x00C10063, 0x00C30067, + 0x00C40065, 0x00C50068, 0x00C60065, 0x00C70069, 0x00C8006A, 0x00C90065, 0x00CA006B, 0x00CB006C, + 0x00CC0063, 0x00CD006D, 0x00CE006C, 0x00CF006E, 0x00D00863, 0x00D10063, 0x00D3006F, 0x00D40065, + 0x00D50055, 0x00D60063, 0x00D7006F, 0x00D80865, 0x00D90070, 0x00DA0065, 0x00DC0063, 0x00DD0055, + 0x00DE0063, 0x00DF0055, 0x00E00071, 0x00E21072, 0x00E31073, 0x00E41074, 0x00E51072, 0x00E61073, + 0x00E70865, 0x00EF0863, 0x00F20063, 0x00F30863, 0x00F80855, 0x00F91074, 0x00FA0863, 0x00FB0075, + 0x00FC0863, 0x010E0063, 0x010F0863, 0x01100076, 0x01110063, 0x01130863, 0x011A0055, 0x011D0077, + 0x011E0065, 0x011F0077, 0x01200055, 0x01210078, 0x01280055, 0x012A0079, 0x012B007A, 0x012C0055, + 0x0130007A, 0x01310055, 0x0134007B, 0x01350055, 0x0139007C, 0x013A0055, 0x0140007D, 0x01410055, + 0x0144007D, 0x0145007E, 0x01460055, 0x0149007F, 0x014A0080, 0x014B0055, 0x01587881, 0x015D0082, + 0x015E0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, 0x01707881, + 0x01730037, 0x01770081, 0x01780037, 0x01800083, 0x01A00883, 0x01A10083, 0x01A20883, 0x01A30083, + 0x01B80078, 0x01BA0837, 0x01BB0078, 0x01BD1081, 0x01BE0078, 0x01BF0806, 0x01C00078, 0x01C21037, + 0x01C30884, 0x01C40885, 0x01C60886, 0x01C70887, 0x01C80855, 0x01C90060, 0x01D10078, 0x01D20060, + 0x01D50860, 0x01D60888, 0x01D70889, 0x01D80855, 0x01D90061, 0x01E1008A, 0x01E20061, 0x01E50861, + 0x01E6088B, 0x01E7088C, 0x01E8108D, 0x01E91077, 0x01EA0877, 0x01EB108E, 0x01EC0063, 0x01F8108F, + 0x01F91090, 0x01FA1091, 0x01FB0019, 0x01FC0065, 0x01FD0063, 0x01FE0055, 0x01FF0077, 0x02000892, + 0x02010092, 0x02060892, 0x02080060, 0x02180061, 0x02280893, 0x02290093, 0x022E0893, 0x02300063, + 0x023B0863, 0x023C0063, 0x02410094, 0x02420083, 0x02440095, 0x02450063, 0x02600077, 0x02610865, + 0x02620065, 0x02680863, 0x026A0063, 0x026B0863, 0x026C0063, 0x026D0863, 0x02700063, 0x02710863, + 0x02740063, 0x02750863, 0x027B0063, 0x027C0863, 0x027D0078, 0x02800063, 0x02880078, 0x02990096, + 0x02AC0078, 0x02AD0097, 0x02B00078, 0x02B10098, 0x02C40078, 0x02C50099, 0x02C60078, 0x02C90083, + 0x02DD0078, 0x02DE0083, 0x02DF009A, 0x02E10083, 0x02E3009A, 0x02E40078, 0x02E8009B, 0x02F60078, + 0x02F8009B, 0x02FA009A, 0x02FB0078, 0x0300009C, 0x03020078, 0x0306000C, 0x03070054, 0x03080083, + 0x030B0078, 0x030F009D, 0x03100078, 0x0311089E, 0x0314009E, 0x031E0078, 0x0320009F, 0x0321009E, + 0x03260083, 0x033000A0, 0x033100A1, 0x033200A2, 0x033300A3, 0x033400A4, 0x03350007, 0x033600A5, + 0x0337009E, 0x03380083, 0x0339009E, 0x033B109E, 0x033D009E, 0x0360089E, 0x0362009E, 0x036A009D, + 0x036B0083, 0x036F0095, 0x03700083, 0x0373009F, 0x03740083, 0x0377009E, 0x0378000E, 0x03790010, + 0x037A0012, 0x037B0014, 0x037C0016, 0x037D009E, 0x037F00A6, 0x0380009D, 0x03870078, 0x0388009E, + 0x03980083, 0x03A60078, 0x03A7009E, 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D90078, 0x04810083, + 0x04820071, 0x049A0871, 0x049B0071, 0x049D0078, 0x049E0083, 0x049F00A7, 0x04A10083, 0x04A500A7, + 0x04A70078, 0x04A80071, 0x04A90083, 0x04AB0078, 0x04AC0871, 0x04B00071, 0x04B10083, 0x04B20097, + 0x04B300A8, 0x04B400A9, 0x04B500AA, 0x04B600AB, 0x04B700AC, 0x04B80097, 0x04B90078, 0x04C100A7, + 0x04C20078, 0x04C30071, 0x04C70078, 0x04C80071, 0x04C90078, 0x04CA0071, 0x04DA0078, 0x04DB0071, + 0x04DD0078, 0x04DE0083, 0x04DF00A7, 0x04E10083, 0x04E30078, 0x04E400A7, 0x04E50078, 0x04E608A7, + 0x04E70071, 0x04E80078, 0x04EE0871, 0x04EF0078, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F300A8, + 0x04F400A9, 0x04F500AA, 0x04F600AB, 0x04F700AC, 0x04F80071, 0x04F90008, 0x04FA00AD, 0x04FB00AE, + 0x04FC00AF, 0x04FD0094, 0x04FE0078, 0x05010083, 0x05020078, 0x05030071, 0x05060078, 0x05080071, + 0x05090078, 0x050A0071, 0x051A0078, 0x051B0871, 0x051C0071, 0x051D0078, 0x051E0083, 0x051F00A7, + 0x05210083, 0x05220078, 0x05240083, 0x05250078, 0x05260083, 0x05270078, 0x052D0871, 0x052E0071, + 0x052F0871, 0x05300078, 0x053300A8, 0x053400A9, 0x053500AA, 0x053600AB, 0x053700AC, 0x05380083, + 0x05390071, 0x053B0078, 0x05410083, 0x05420078, 0x05430071, 0x05470078, 0x05480071, 0x05490078, + 0x054A0071, 0x055A0078, 0x055B0071, 0x055D0078, 0x055E0083, 0x055F00A7, 0x05610083, 0x05630078, + 0x05640083, 0x05650078, 0x056600A7, 0x05670078, 0x05680071, 0x05690078, 0x05700071, 0x05710083, + 0x05720078, 0x057300A8, 0x057400A9, 0x057500AA, 0x057600AB, 0x057700AC, 0x05780078, 0x058100A7, + 0x05820078, 0x05830071, 0x05870078, 0x05880071, 0x05890078, 0x058A0071, 0x059A0078, 0x059B0071, + 0x059D0078, 0x059E0083, 0x059F00A7, 0x05A10083, 0x05A20078, 0x05A408A7, 0x05A50078, 0x05A608A7, + 0x05A70078, 0x05AB0083, 0x05AC0078, 0x05AE0871, 0x05AF0078, 0x05B00071, 0x05B10078, 0x05B300A8, + 0x05B400A9, 0x05B500AA, 0x05B600AB, 0x05B700AC, 0x05B80094, 0x05B90078, 0x05C10083, 0x05C20078, + 0x05C30071, 0x05C60078, 0x05C70071, 0x05CA0871, 0x05CB0078, 0x05CD0071, 0x05D00078, 0x05D20071, + 0x05D30078, 0x05D40071, 0x05D60078, 0x05D70071, 0x05DD0078, 0x05DF00A7, 0x05E00083, 0x05E100A7, + 0x05E20078, 0x05E300A7, 0x05E508A7, 0x05E70078, 0x05F300A8, 0x05F400A9, 0x05F500AA, 0x05F600AB, + 0x05F700AC, 0x05F800B0, 0x05F900B1, 0x05FA0054, 0x05FE0078, 0x060100A7, 0x06020078, 0x06030071, + 0x061A0078, 0x061B0071, 0x061D0078, 0x061F0083, 0x062100A7, 0x06230083, 0x06240883, 0x06250083, + 0x06270078, 0x062B0083, 0x062C0078, 0x06300071, 0x06310078, 0x063300A8, 0x063400A9, 0x063500AA, + 0x063600AB, 0x063700AC, 0x06380078, 0x064100A7, 0x06420078, 0x06430071, 0x065A0078, 0x065B0071, + 0x065D0078, 0x065E0083, 0x065F00A7, 0x066008A7, 0x066100A7, 0x066300B2, 0x066408A7, 0x06660083, + 0x06670078, 0x066B00A7, 0x066C0078, 0x066F0071, 0x06710078, 0x067300A8, 0x067400A9, 0x067500AA, + 0x067600AB, 0x067700AC, 0x06780078, 0x068100A7, 0x06820078, 0x06830071, 0x069D0078, 0x069F00A7, + 0x06A10083, 0x06A20078, 0x06A300A7, 0x06A508A7, 0x06A70078, 0x06B00071, 0x06B10078, 0x06B300A8, + 0x06B400A9, 0x06B500AA, 0x06B600AB, 0x06B700AC, 0x06B80078, 0x06C100A7, 0x06C20078, 0x06C30071, + 0x06CC0078, 0x06CD0071, 0x06D90078, 0x06DA0071, 0x06DE0078, 0x06E00071, 0x06E40078, 0x06E50083, + 0x06E60078, 0x06E800A7, 0x06E90083, 0x06EC00A7, 0x06ED08A7, 0x06F00078, 0x06F900A7, 0x06FA0097, + 0x06FB0078, 0x07010071, 0x071A0083, 0x071E0078, 0x07200071, 0x07230081, 0x07240083, 0x072800A8, + 0x072900A9, 0x072A00AA, 0x072B00AB, 0x072C00AC, 0x072D0097, 0x072E0078, 0x07410071, 0x07430078, + 0x07440071, 0x07460078, 0x074A0071, 0x074C0078, 0x074D0071, 0x07500078, 0x07510071, 0x07520078, + 0x07550071, 0x07560078, 0x07570071, 0x075A0083, 0x075D0078, 0x075E0083, 0x075F0078, 0x07600071, + 0x07630081, 0x07640083, 0x07670078, 0x076800A8, 0x076900A9, 0x076A00AA, 0x076B00AB, 0x076C00AC, + 0x076D0078, 0x076E1071, 0x076F0078, 0x07800071, 0x07810094, 0x07820097, 0x07865897, 0x07870097, + 0x078A0094, 0x078C0083, 0x078D0094, 0x079000A8, 0x079100A9, 0x079200AA, 0x079300AB, 0x079400AC, + 0x079500B3, 0x079A0094, 0x079D00B4, 0x079F00A7, 0x07A00071, 0x07A40078, 0x07A50071, 0x07A90871, + 0x07AA0071, 0x07AE0871, 0x07AF0071, 0x07B60078, 0x07B90083, 0x07BB0883, 0x07BD0083, 0x07C40071, + 0x07C60078, 0x07C80083, 0x07CC0078, 0x07CD0083, 0x07D10883, 0x07D20083, 0x07D60883, 0x07D70083, + 0x07DF0094, 0x07E30083, 0x07E40094, 0x07E70078, 0x07E80097, 0x07E90078, 0x08000071, 0x08110078, + 0x08120071, 0x08130871, 0x08140078, 0x08150071, 0x081600A7, 0x08170083, 0x081A0078, 0x081B0083, + 0x081C00A7, 0x081D0078, 0x082000A8, 0x082100A9, 0x082200AA, 0x082300AB, 0x082400AC, 0x08250097, + 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, 0x08680071, 0x087E7881, + 0x087F0078, 0x08800071, 0x08AD0078, 0x08B00071, 0x08D20078, 0x08D40071, 0x08FD0078, 0x09000071, + 0x09270078, 0x09280071, 0x092F0078, 0x09300071, 0x09470078, 0x09480071, 0x095B0078, 0x095C0071, + 0x09630078, 0x09640071, 0x098B0078, 0x098C0071, 0x09AE0078, 0x09B00094, 0x09B10097, 0x09B500B6, + 0x09B600B7, 0x09B700B8, 0x09B800B9, 0x09B900B0, 0x09BA00BA, 0x09BB00BB, 0x09BC00BC, 0x09BD00BD, + 0x09BE00BE, 0x09BF0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FB0078, 0x0A010071, + 0x0B370097, 0x0B380071, 0x0B3C0078, 0x0B400005, 0x0B410071, 0x0B4E00BF, 0x0B4F0078, 0x0B500071, + 0x0B760097, 0x0B7700C0, 0x0B7800C1, 0x0B790078, 0x0B800071, 0x0B890083, 0x0B8B0078, 0x0B900071, + 0x0B990083, 0x0B9B0097, 0x0B9C0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB90083, + 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB00A7, 0x0BDC0083, 0x0BDF00A7, 0x0BE30083, 0x0BE400A7, + 0x0BE50083, 0x0BEA0097, 0x0BEE0071, 0x0BEF0078, 0x0BF000A8, 0x0BF100A9, 0x0BF200AA, 0x0BF300AB, + 0x0BF400AC, 0x0BF50078, 0x0BF800C3, 0x0BF900C4, 0x0BFA00C5, 0x0BFB00C6, 0x0BFC00C7, 0x0BFD0078, + 0x0C000006, 0x0C030099, 0x0C040006, 0x0C060083, 0x0C070005, 0x0C0800A8, 0x0C0900A9, 0x0C0A00AA, + 0x0C0B00AB, 0x0C0C00AC, 0x0C0D0078, 0x0C100071, 0x0C3C0078, 0x0C400071, 0x0C550078, 0x0C800071, + 0x0C8F0078, 0x0C900083, 0x0C9200A7, 0x0C940083, 0x0C9500C8, 0x0C960078, 0x0C9800A7, 0x0C990083, + 0x0C9A00A7, 0x0C9D0083, 0x0C9E0078, 0x0CA00054, 0x0CA10078, 0x0CA20006, 0x0CA300A8, 0x0CA400A9, + 0x0CA500AA, 0x0CA600AB, 0x0CA700AC, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBB0078, 0x0CC00071, + 0x0CD50078, 0x0CD800A7, 0x0CE10071, 0x0CE400A7, 0x0CE50078, 0x0CE800A8, 0x0CE900A9, 0x0CEA00AA, + 0x0CEB00AB, 0x0CEC00AC, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0C0083, 0x0D0D00A7, + 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0EA70081, 0x0EA87881, 0x0EB17055, + 0x0EB60055, 0x0EBC7881, 0x0EBD0055, 0x0ECE7881, 0x0EE00083, 0x0EE20078, 0x0F000863, 0x0F4B0855, + 0x0F4D1055, 0x0F4E0078, 0x0F500863, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, + 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, + 0x0FA408CA, 0x0FA70078, 0x0FA80855, 0x0FAC0078, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, + 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, + 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD90855, 0x0FDC08CA, 0x0FDD08D2, 0x0FDE08D3, + 0x0FDF08D4, 0x0FE01037, 0x0FE10855, 0x0FE408D5, 0x0FE608D3, 0x0FE70837, 0x0FE808C9, 0x0FE90855, + 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0078, 0x0FEF0837, 0x0FF008C9, 0x0FF10855, + 0x0FF408CA, 0x0FF508D7, 0x0FF608D8, 0x0FF70837, 0x0FF80078, 0x0FF90855, 0x0FFC08D9, 0x0FFD08DA, + 0x0FFE08D3, 0x0FFF1037, 0x10000805, 0x10011005, 0x10060057, 0x100700C2, 0x10080099, 0x100B0006, + 0x100C00DB, 0x100D00B4, 0x100E00DB, 0x100F00B4, 0x10100006, 0x10121006, 0x101400DC, 0x101500DD, + 0x101600DE, 0x101700DF, 0x10180007, 0x101A1007, 0x101B1006, 0x101C0006, 0x101D00E0, 0x101E1006, + 0x10200038, 0x10210006, 0x102200E1, 0x1023000A, 0x10241006, 0x10250006, 0x10290019, 0x102A0038, + 0x102B0006, 0x10300057, 0x10320078, 0x10350057, 0x103878E2, 0x10390078, 0x103A78E3, 0x103B78E4, + 0x103C78E5, 0x103D780B, 0x103E7819, 0x103F780A, 0x104070E2, 0x1041705A, 0x104270E3, 0x104370E4, + 0x104470E5, 0x1045700B, 0x10467019, 0x1047700A, 0x10487081, 0x104B0078, 0x10500008, 0x10541008, + 0x10550008, 0x105B0078, 0x10680083, 0x106F0095, 0x10730083, 0x10760078, 0x10801054, 0x10812877, + 0x10820054, 0x10831054, 0x10840054, 0x10852855, 0x10862877, 0x10872855, 0x10882877, 0x108A0054, + 0x108B1054, 0x108C0054, 0x108D2877, 0x108F0054, 0x10907854, 0x10922877, 0x109308E6, 0x10942877, + 0x109508E7, 0x10962877, 0x10970058, 0x10982877, 0x10990054, 0x109A2855, 0x109B1071, 0x109D0054, + 0x109E2855, 0x109F2877, 0x10A028E8, 0x10A10019, 0x10A32855, 0x10A50054, 0x10A70078, 0x10AA305F, + 0x10B010E9, 0x10B110EA, 0x10B210EB, 0x10B310EC, 0x10B410ED, 0x10B510EE, 0x10B610EF, 0x10B710F0, + 0x10B810F1, 0x10B910F2, 0x10BA10F3, 0x10BB10F4, 0x10BC10F5, 0x10BD10F6, 0x10BE10F7, 0x10BF10F8, + 0x10C000F9, 0x10C100FA, 0x10C20078, 0x10C80019, 0x10CB0054, 0x10CD0819, 0x10CE0054, 0x10D00019, + 0x10D10054, 0x10D30019, 0x10D40054, 0x10D70819, 0x10D80054, 0x10E70819, 0x10E80054, 0x10E90019, + 0x10EB0054, 0x10FA0019, 0x110100E8, 0x110208E8, 0x11030019, 0x110400FB, 0x110608FC, 0x11070019, + 0x1109000B, 0x110A0019, 0x110B00E8, 0x110C0019, 0x110D00E8, 0x110F0019, 0x111000E8, 0x111208E8, + 0x11140019, 0x111610E8, 0x111700E8, 0x111810E8, 0x111900E8, 0x111A0019, 0x111E00FD, 0x111F00E8, + 0x112208E8, 0x112300E8, 0x11270019, 0x112900FD, 0x112B0019, 0x113008E8, 0x113200FD, 0x11360019, + 0x113708FD, 0x113900FD, 0x113A08FD, 0x113B00FD, 0x113C08FD, 0x113D00FD, 0x114008FD, 0x114100FD, + 0x114208FD, 0x114300FD, 0x114408FD, 0x114500FD, 0x114600E8, 0x11470019, 0x114800FE, 0x114A0019, + 0x114C00FF, 0x114D0019, 0x115100FD, 0x11520019, 0x11530100, 0x11540101, 0x115500E8, 0x115608E8, + 0x115800FD, 0x115C00E8, 0x115D0019, 0x115F00E8, 0x11600019, 0x116500FE, 0x11670019, 0x116800FD, + 0x11690019, 0x116B00FD, 0x117008FD, 0x117200FD, 0x117508FD, 0x11770019, 0x117800FD, 0x11790102, + 0x117B0103, 0x117C00E8, 0x117D0104, 0x117F0105, 0x11800054, 0x118400FD, 0x11860054, 0x119000E8, + 0x11910054, 0x1195080A, 0x11960054, 0x119B0094, 0x11BE0019, 0x11BF0054, 0x11CE0019, 0x11DA00B4, + 0x11DB0006, 0x11DC0054, 0x11EE0078, 0x12000054, 0x12140078, 0x12200054, 0x12260078, 0x12301906, + 0x12311907, 0x12321908, 0x12331909, 0x1234190A, 0x1235190B, 0x1236190C, 0x1237190D, 0x1238190E, + 0x1239190F, 0x123A1106, 0x123B1107, 0x123C1108, 0x123D1109, 0x123E110A, 0x123F110B, 0x1240110C, + 0x1241110D, 0x1242110E, 0x1243110F, 0x1244105D, 0x1245105B, 0x12461110, 0x12471111, 0x12481112, + 0x12491113, 0x124A1114, 0x124B1115, 0x124C1116, 0x124D1117, 0x124E1094, 0x125B1918, 0x12681919, + 0x127518C3, 0x1276011A, 0x1277011B, 0x1278011C, 0x1279011D, 0x127A011E, 0x127B00C4, 0x127C00C5, + 0x127D00C6, 0x127E00C7, 0x127F011F, 0x12800054, 0x12FC0019, 0x13000054, 0x134F0078, 0x13500054, + 0x13560094, 0x13570054, 0x13590078, 0x13810054, 0x13850078, 0x13860054, 0x13940078, 0x13950054, + 0x13A60078, 0x13A80054, 0x13AA0078, 0x13AB0054, 0x13B00078, 0x13B10054, 0x13B40009, 0x13BB0106, + 0x13BC0107, 0x13BD0108, 0x13BE0109, 0x13BF010A, 0x13C00106, 0x13C10107, 0x13C20108, 0x13C30109, + 0x13C4010A, 0x13C50106, 0x13C60107, 0x13C70108, 0x13C80109, 0x13C9010A, 0x13CA0054, 0x13CB0078, + 0x13CC0054, 0x13D80078, 0x13D90054, 0x13E000E8, 0x13E10019, 0x13E200FE, 0x13E3000A, 0x13E40078, + 0x13E80019, 0x13EA00E8, 0x13EB00FE, 0x13EC0019, 0x13EE00E8, 0x13EF00FE, 0x13F00019, 0x13F100FD, + 0x13F30009, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C2000A, 0x14C70120, 0x14C80121, + 0x14C9000A, 0x14CD0019, 0x14CE00E8, 0x14D80019, 0x14DC0122, 0x14DD0019, 0x14E000FD, 0x14E100E8, + 0x14E200FD, 0x14E30019, 0x14E700E8, 0x14E800FE, 0x14EA00FD, 0x14EB0019, 0x14EC0009, 0x14EE00E8, + 0x14EF0019, 0x14F200E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA00E8, 0x14FC00FD, 0x14FD0019, + 0x14FE0009, 0x14FF0019, 0x150500E8, 0x150610E8, 0x150700E8, 0x15110019, 0x151200E8, 0x15140019, + 0x151600FE, 0x15180019, 0x151A00FD, 0x151B0019, 0x151E00FD, 0x151F00E8, 0x15200019, 0x152C00E8, + 0x152D0019, 0x153200FD, 0x15330019, 0x153500E8, 0x15370019, 0x153800E8, 0x15390019, 0x153A10E8, + 0x153B1019, 0x153C0019, 0x153D00FE, 0x153E00E8, 0x153F00FE, 0x154300E8, 0x154600FE, 0x154700E8, + 0x154900FE, 0x154F00E8, 0x155100FE, 0x15520019, 0x155300FD, 0x15570019, 0x155800FE, 0x155900E8, + 0x155A00FE, 0x155B00E8, 0x155E00FE, 0x156400E8, 0x156700FE, 0x156C0019, 0x156E08E8, 0x156F0123, + 0x15700019, 0x157100E8, 0x15720124, 0x157300E8, 0x15740019, 0x157600FD, 0x157700E8, 0x15780019, + 0x157C00FE, 0x157E0019, 0x15800054, 0x158A0078, 0x16000096, 0x16180098, 0x16300078, 0x16400063, + 0x16720055, 0x16730054, 0x16760078, 0x167D0006, 0x16800125, 0x16930078, 0x16980071, 0x16B30078, + 0x16C00071, 0x16CC0078, 0x16D00071, 0x16F00078, 0x17000006, 0x17010126, 0x17030006, 0x170500E0, + 0x17060126, 0x17070006, 0x170C0078, 0x170E0126, 0x170F0078, 0x17400054, 0x174D0078, 0x174E0054, + 0x177A0078, 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18008805, 0x18010006, 0x18020054, + 0x18030071, 0x18040009, 0x18090054, 0x180A0009, 0x180E0099, 0x180F00BF, 0x18100054, 0x18110127, + 0x18120128, 0x18130129, 0x1814012A, 0x18150083, 0x18180099, 0x18190081, 0x181B1054, 0x181C112B, + 0x181D112C, 0x181E0071, 0x181F0054, 0x18200078, 0x18210071, 0x18260871, 0x18320071, 0x18380871, + 0x18390071, 0x183A0871, 0x183C0071, 0x183D0871, 0x183F0071, 0x184A0871, 0x184B0071, 0x184C0078, + 0x184D0083, 0x184E1037, 0x184F0881, 0x18500099, 0x18510071, 0x18560871, 0x18620071, 0x18680871, + 0x18690071, 0x186A0871, 0x186C0071, 0x186D0871, 0x186F0071, 0x187A0871, 0x187B0071, 0x187C0871, + 0x187E0081, 0x187F0881, 0x18800078, 0x18830071, 0x18970078, 0x18991071, 0x18C80094, 0x18C978AD, + 0x18CA78AE, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, 0x18F80071, 0x19001094, + 0x190F1054, 0x191010AD, 0x191110AE, 0x1912112D, 0x1913112E, 0x1914112F, 0x19151094, 0x19220078, + 0x19286854, 0x19291930, 0x192A1931, 0x192B1932, 0x192C1933, 0x192D1934, 0x192E1935, 0x192F1936, + 0x19301894, 0x193E1854, 0x194018AD, 0x194118AE, 0x1942192D, 0x1943192E, 0x1944192F, 0x19451894, + 0x19591937, 0x195A1938, 0x195B1939, 0x195C193A, 0x195D193B, 0x195E193C, 0x195F193D, 0x19601094, + 0x19666854, 0x19681894, 0x19806894, 0x19AC1094, 0x19B96894, 0x19BC6854, 0x19BE6894, 0x19EF6854, + 0x19F01094, 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x52470078, + 0x52480054, 0x52640078, 0x53800037, 0x538C0078, 0x54000071, 0x540100C8, 0x54020071, 0x54030083, + 0x54040071, 0x541200A7, 0x54130083, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, + 0x7000013F, 0x7C800871, 0x7D070071, 0x7D080871, 0x7D0A0071, 0x7D0B0871, 0x7D120071, 0x7D130871, + 0x7D140071, 0x7D150871, 0x7D170078, 0x7D180871, 0x7D360078, 0x7D380871, 0x7D6D0078, 0x7D801055, + 0x7D840078, 0x7D8A1055, 0x7D8C0078, 0x7D8F0083, 0x7D90289B, 0x7D95089B, 0x7DA10078, 0x7DA2089B, + 0x7DA8409E, 0x7DAA389E, 0x7DAB409E, 0x7DAC389E, 0x7DAD409E, 0x7DAE389E, 0x7DAF409E, 0x7DB0389E, + 0x7DB1409E, 0x7DB2389E, 0x7DB3409E, 0x7DB4389E, 0x7DB5409E, 0x7DB6389E, 0x7DB7409E, 0x7DB8389E, + 0x7DB9409E, 0x7DBA389E, 0x7DBB409E, 0x7DBC389E, 0x7DBD409E, 0x7DBE389E, 0x7DBF409E, 0x7DC0389E, + 0x7DC1409E, 0x7DC8389E, 0x7DC9409E, 0x7DCA389E, 0x7DCB409E, 0x7DCC389E, 0x7DCD409E, 0x7DCE389E, + 0x7DCF409E, 0x7DD1389E, 0x7DD2409E, 0x7DD4389E, 0x7DD5409E, 0x7DD6389E, 0x7DD7409E, 0x7DD90078, + 0x7DEA209E, 0x7DEB489E, 0x7DEC209E, 0x7DEF409E, 0x7DF3389E, 0x7DF5409E, 0x7DFC389E, 0x7DFD209E, + 0x7DFE409E, 0x7DFF389E, 0x7E00409E, 0x7E32209E, 0x7E4C389E, 0x7E70489E, 0x7E7B409E, 0x7E89209E, + 0x7E97389E, 0x7E9A489E, 0x7E9E209E, 0x7E9F00B4, 0x7EA00078, 0x7EA8389E, 0x7EAC209E, 0x7EAE389E, + 0x7EAF209E, 0x7EB0389E, 0x7EB1209E, 0x7EB4389E, 0x7EB5209E, 0x7EB8389E, 0x7EBA209E, 0x7EC3389E, + 0x7EC80078, 0x7EC9389E, 0x7ECB209E, 0x7ECC389E, 0x7ECD209E, 0x7EDA389E, 0x7EDB209E, 0x7EDC389E, + 0x7EDE209E, 0x7EE2389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, 0x7EFE4140, 0x7EFF0078, 0x7F000083, + 0x7F088006, 0x7F0C80BF, 0x7F0D0078, 0x7F100083, 0x7F120078, 0x7F188006, 0x7F198099, 0x7F1A8038, + 0x7F1B80BF, 0x7F230006, 0x7F2480BF, 0x7F251006, 0x7F271038, 0x7F28600C, 0x7F2A6006, 0x7F2C6099, + 0x7F2D60BF, 0x7F306006, 0x7F31600B, 0x7F326019, 0x7F346006, 0x7F356007, 0x7F360078, 0x7F38409E, + 0x7F41209E, 0x7F46489E, 0x7F47209E, 0x7F49489E, 0x7F4A209E, 0x7F4C489E, 0x7F4D209E, 0x7F4E489E, + 0x7F4F209E, 0x7F50489E, 0x7F51209E, 0x7F52489E, 0x7F53209E, 0x7F54489E, 0x7F55209E, 0x7F5A489E, + 0x7F5B209E, 0x7F5C489E, 0x7F5D209E, 0x7F5E489E, 0x7F5F209E, 0x7F60489E, 0x7F61209E, 0x7F62489E, + 0x7F63209E, 0x7F64489E, 0x7F65209E, 0x7F66489E, 0x7F67209E, 0x7F68489E, 0x7F69209E, 0x7F6A489E, + 0x7F6B209E, 0x7F6C489E, 0x7F6D209E, 0x7F6E489E, 0x7F6F209E, 0x7F70489E, 0x7F71209E, 0x7F72489E, + 0x7F73209E, 0x7F74489E, 0x7F75209E, 0x7F76489E, 0x7F77209E, 0x7F7A489E, 0x7F7B209E, 0x7F7F0078, + 0x7F818806, 0x7F828808, 0x7F838806, 0x7F848809, 0x7F858806, 0x7F86880C, 0x7F88880E, 0x7F898810, + 0x7F8A8812, 0x7F8B8814, 0x7F8C8816, 0x7F8D880C, 0x7F8E8818, 0x7F8F881A, 0x7F908806, 0x7F918860, + 0x7F9E8806, 0x7F9F8837, 0x7FA18861, 0x7FAE8819, 0x7FB0880A, 0x7FB15009, 0x7FB25006, 0x7FB35071, + 0x7FB85081, 0x7FB95071, 0x7FCF5081, 0x7FD05071, 0x7FE00078, 0x7FE15071, 0x7FE40078, 0x7FE55071, + 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEF0078, 0x7FF08808, 0x7FF18819, 0x7FF28854, + 0x7FF38808, 0x7FF45054, 0x7FF55019, 0x7FF75054, 0x7FF80078, 0x7FFD0141, 0x7FFE0054, 0x7FFF0078, + 0x80000071, 0x80060078, 0x80070071, 0x801F0078, 0x80200071, 0x80270078, 0x80280071, 0x802F0078, + 0x80400071, 0x807E0078, 0x80800097, 0x80810094, 0x80820078, 0x808400B6, 0x808500B7, 0x808600B8, + 0x808700B9, 0x808800B0, 0x808900BA, 0x808A00BB, 0x808B00BC, 0x808C00BD, 0x808D0142, 0x808E0143, + 0x808F0144, 0x80900145, 0x809100B1, 0x80920146, 0x80930147, 0x80940148, 0x80950149, 0x8096014A, + 0x8097014B, 0x8098014C, 0x8099014D, 0x809A0078, 0x809C0094, 0x80A0014E, 0x80A1014F, 0x80A20150, + 0x80A30151, 0x80A40152, 0x80A50150, 0x80A60153, 0x80A70151, 0x80A80154, 0x80A90155, 0x80AA0156, + 0x80AB0157, 0x80AC014F, 0x80AE0158, 0x80B00154, 0x80B30150, 0x80B50155, 0x80B60153, 0x80B90151, + 0x80BA0150, 0x80BB005F, 0x80BD0054, 0x80C500C3, 0x80C60078, 0x81800071, 0x819000AD, 0x819100B0, + 0x81920078, 0x81980071, 0x81A50159, 0x81A60078, 0x81C00071, 0x81CF0078, 0x81D00071, 0x81E20078, + 0x81E40071, 0x81E80094, 0x81E90158, 0x81EA015A, 0x81EB0078, 0x8200015B, 0x8214015C, 0x82280071, + 0x824F0078, 0x825000A8, 0x825100A9, 0x825200AA, 0x825300AB, 0x825400AC, 0x82550078, 0x8400009B, + 0x84030078, 0x8404009B, 0x841B0078, 0x841C009B, 0x841D0078, 0x841E009B, 0x841F0078, 0x8500009B, + 0x85010083, 0x85020078, 0x85030083, 0x85040078, 0x85060083, 0x8508009B, 0x850A0078, 0x850B009B, + 0x850C0078, 0x850D009B, 0x851A0078, 0x851C0083, 0x851E0078, 0x8520015D, 0x8521015E, 0x8522015F, + 0x85230160, 0x85240078, 0x8528009A, 0x852D0078, 0xE8000094, 0xE87B0078, 0xE8800094, 0xE8940078, + 0xE8950094, 0xE8AF0894, 0xE8B300A7, 0xE8B40083, 0xE8B50094, 0xE8B700A7, 0xE8BA0057, 0xE8BE0083, + 0xE8C20094, 0xE8C30083, 0xE8C60094, 0xE8D50083, 0xE8D70094, 0xE8DE0894, 0xE8E10094, 0xE8EF0078, + 0xE9000054, 0xE9210083, 0xE9230078, 0xE9800054, 0xE9AC0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, + 0xEA272855, 0xEA342877, 0xEA412855, 0xEA4E2877, 0xEA500078, 0xEA512877, 0xEA520078, 0xEA532877, + 0xEA540078, 0xEA552877, 0xEA5B2855, 0xEA5D0078, 0xEA5F2855, 0xEA620078, 0xEA632855, 0xEA682877, + 0xEA752855, 0xEA822877, 0xEA830078, 0xEA842877, 0xEA860078, 0xEA872877, 0xEA8F2855, 0xEA9C2877, + 0xEA9D0078, 0xEA9E2877, 0xEAA40078, 0xEAA52877, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, + 0xEADD2855, 0xEAEA2877, 0xEAF72855, 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, + 0xEB452855, 0xEB530078, 0xEB542877, 0xEB612855, 0xEB712877, 0xEB7E2855, 0xEB8E2877, 0xEB9B2855, + 0xEBAB2877, 0xEBB82855, 0xEBC82877, 0xEBD52855, 0xEBE50078, 0xEBE7280E, 0xEBE82810, 0xEBE92812, + 0xEBEA2814, 0xEBEB2816, 0xEBEC280E, 0xEBED2810, 0xEBEE2812, 0xEBEF2814, 0xEBF02816, 0xEBF1280E, + 0xEBF22810, 0xEBF32812, 0xEBF42814, 0xEBF52816, 0xEBF6280E, 0xEBF72810, 0xEBF82812, 0xEBF92814, + 0xEBFA2816, 0xEBFB280E, 0xEBFC2810, 0xEBFD2812, 0xEBFE2814, 0xEBFF2816, 0xEC000078 + }; + + static const uint32_t a1[] = { + 0x00000071, 0x536C0078, 0x7C000871, 0x7D0F0078 + }; + + static const uint32_t a7[] = { + 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 + }; + + static const uint32_t a8[] = { + 0x0000013F, 0x7FFF0078 + }; + + static const uint32_t a16[] = { + 0x00800865, 0x00880065, 0x00890865, 0x00930065, 0x00940865, 0x00980161, 0x00991065, 0x009A0865, + 0x009C0863, 0x009F1063, 0x00A00063, 0x00A10863, 0x00A41055, 0x00A50065, 0x00A60865, 0x00A90065, + 0x00AA0865, 0x00B30065, 0x00B40865, 0x00BC0863, 0x00BF1162, 0x00C00163, 0x00C10065, 0x00C30063, + 0x00C40068, 0x00C50063, 0x00C60055, 0x00C70164, 0x00C80063, 0x00C90068, 0x00CA0165, 0x00CB0166, + 0x00CC0065, 0x00CD0055, 0x00CE0167, 0x00CF0168, 0x00D00865, 0x00D10065, 0x00D30063, 0x00D4006F, + 0x00D50055, 0x00D60065, 0x00D70863, 0x00D80070, 0x00D90063, 0x00DB0169, 0x00DC0065, 0x00DD0071, + 0x00DE0065, 0x00DF016A, 0x00E00071, 0x00E21074, 0x00E31072, 0x00E41073, 0x00E51074, 0x00E60863, + 0x00EE016B, 0x00EF0865, 0x00F20065, 0x00F30865, 0x00F81072, 0x00F91073, 0x00FA0865, 0x00FB016C, + 0x00FC0865, 0x010E0065, 0x010F0865, 0x01100055, 0x01110065, 0x01130865, 0x011A0055, 0x011D0063, + 0x011E016D, 0x011F0055, 0x0120016E, 0x01210078, 0x01280055, 0x0129016F, 0x012A0055, 0x012B007A, + 0x012C0170, 0x012D0171, 0x012E0055, 0x01310172, 0x01320055, 0x01340173, 0x01350055, 0x01370173, + 0x01380055, 0x013A0174, 0x013B0055, 0x0141007D, 0x01420055, 0x0145007E, 0x01460055, 0x01587881, + 0x015C0082, 0x015D0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, + 0x01707881, 0x01720037, 0x01800083, 0x01A00883, 0x01A20175, 0x01A30083, 0x01B80078, 0x01BA0037, + 0x01BB0078, 0x01C20837, 0x01C30806, 0x01C40885, 0x01C50078, 0x01C70887, 0x01C80060, 0x01D50860, + 0x01D60889, 0x01D80061, 0x01E50861, 0x01E6088C, 0x01E70078, 0x01E81176, 0x01E90877, 0x01EA1177, + 0x01EB0055, 0x01EC0065, 0x01F81093, 0x01F90055, 0x01FA1178, 0x01FB0063, 0x01FC10D8, 0x01FD0065, + 0x01FE0077, 0x02000892, 0x02020092, 0x02030892, 0x02040092, 0x02060892, 0x02070092, 0x02080060, + 0x020C0860, 0x020D0060, 0x02180061, 0x021C0861, 0x021D0061, 0x02280893, 0x022A0093, 0x022B0893, + 0x022C0093, 0x022E0893, 0x022F0093, 0x02300065, 0x023B0865, 0x023C0065, 0x02410083, 0x02430078, + 0x02440095, 0x02450065, 0x02600863, 0x02610063, 0x02670078, 0x02680865, 0x026A0065, 0x026B0865, + 0x026C0065, 0x026D0865, 0x02700065, 0x02710865, 0x02740065, 0x02750865, 0x027B0065, 0x027C0865, + 0x027D0078, 0x02800065, 0x02880078, 0x02980096, 0x02AB0078, 0x02AC0081, 0x02AD0097, 0x02B00098, + 0x02C31055, 0x02C40097, 0x02C50078, 0x02C80083, 0x02E1009A, 0x02E20083, 0x02E40078, 0x02E8009B, + 0x02F50078, 0x02F8009B, 0x02F9009A, 0x02FA0078, 0x0300009C, 0x03020078, 0x03050140, 0x0306009D, + 0x03070054, 0x03080083, 0x030B0078, 0x030D009D, 0x030E0078, 0x030F009D, 0x0310009E, 0x0311089E, + 0x0313009E, 0x031D0078, 0x0320009E, 0x03250083, 0x032F0078, 0x03300179, 0x0331017A, 0x0332017B, + 0x0333017C, 0x0334017D, 0x033500A5, 0x0336009D, 0x0337009E, 0x033A109E, 0x033C009E, 0x0369089E, + 0x036A009E, 0x036B0083, 0x036E009C, 0x036F0083, 0x0372009F, 0x03730083, 0x03740054, 0x03750083, + 0x0377009E, 0x0378000F, 0x03790011, 0x037A0013, 0x037B0015, 0x037C0017, 0x037D009E, 0x037E00A6, + 0x037F009E, 0x0380009D, 0x03870057, 0x03880083, 0x0389009E, 0x03980083, 0x03A50078, 0x03A6009E, + 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D8009E, 0x03D90078, 0x04800083, 0x048100A7, 0x04820071, + 0x04940871, 0x04950071, 0x04980871, 0x04990071, 0x049D0078, 0x049E0071, 0x049F00A7, 0x04A00083, + 0x04A400A7, 0x04A60083, 0x04A70078, 0x04A80083, 0x04AA0078, 0x04AC0871, 0x04B00071, 0x04B10083, + 0x04B20097, 0x04B3017E, 0x04B4017F, 0x04B50180, 0x04B60181, 0x04B70182, 0x04B80078, 0x04BE0071, + 0x04BF0078, 0x04C00083, 0x04C100A7, 0x04C20071, 0x04C60078, 0x04C70071, 0x04C80078, 0x04C90071, + 0x04D40078, 0x04D50071, 0x04D80078, 0x04DB0071, 0x04DD0078, 0x04DE0071, 0x04DF00A7, 0x04E00083, + 0x04E20078, 0x04E300A7, 0x04E40078, 0x04E508A7, 0x04E60083, 0x04E70078, 0x04EB00A7, 0x04EC0078, + 0x04EE0871, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F3017E, 0x04F4017F, 0x04F50180, 0x04F60181, + 0x04F70182, 0x04F80071, 0x04F90008, 0x04FA00B6, 0x04FB00B7, 0x04FC0183, 0x04FD0078, 0x05000083, + 0x050100A7, 0x05020071, 0x05050078, 0x05070071, 0x05080078, 0x05090071, 0x05140078, 0x05150071, + 0x05180078, 0x05190871, 0x051A0071, 0x051B0078, 0x051C0071, 0x051D0078, 0x051F00A7, 0x05200083, + 0x05210078, 0x05230083, 0x05240078, 0x05250083, 0x05270078, 0x052C0871, 0x052E0078, 0x0533017E, + 0x0534017F, 0x05350180, 0x05360181, 0x05370182, 0x05380083, 0x05390071, 0x053A0078, 0x05400083, + 0x054100A7, 0x05420071, 0x05540078, 0x05550071, 0x05580078, 0x05590071, 0x055D0078, 0x055E0071, + 0x055F00A7, 0x05600083, 0x056400A7, 0x05660083, 0x05670078, 0x05700071, 0x05710083, 0x05720078, + 0x0573017E, 0x0574017F, 0x05750180, 0x05760181, 0x05770182, 0x05780008, 0x05790078, 0x05800083, + 0x058100A7, 0x05820071, 0x05860078, 0x05870071, 0x05880078, 0x05890071, 0x05940078, 0x05950071, + 0x05980078, 0x05990071, 0x059D0078, 0x059E0071, 0x059F0083, 0x05A20078, 0x05A300A7, 0x05A40078, + 0x05A508A7, 0x05A60083, 0x05A70078, 0x05AB00A7, 0x05AC0078, 0x05AE0871, 0x05AF0071, 0x05B10078, + 0x05B3017E, 0x05B4017F, 0x05B50180, 0x05B60181, 0x05B70182, 0x05B80071, 0x05B90078, 0x05C10071, + 0x05C50078, 0x05C70071, 0x05C80078, 0x05C90071, 0x05CB0078, 0x05CC0071, 0x05CD0078, 0x05CF0071, + 0x05D00078, 0x05D10071, 0x05D20078, 0x05D40071, 0x05D50078, 0x05D70071, 0x05DD0078, 0x05DF00A7, + 0x05E10078, 0x05E300A7, 0x05E40078, 0x05E508A7, 0x05E60083, 0x05E70078, 0x05EB00A7, 0x05EC0078, + 0x05F3017E, 0x05F4017F, 0x05F50180, 0x05F60181, 0x05F70182, 0x05F80184, 0x05F90054, 0x05FC0008, + 0x05FD0078, 0x060000A7, 0x06020071, 0x06060078, 0x06070071, 0x06080078, 0x06090071, 0x06140078, + 0x06150071, 0x061D0078, 0x061F0083, 0x062000A7, 0x06220078, 0x06230083, 0x06240078, 0x06250083, + 0x06270078, 0x062A0083, 0x062B0078, 0x06300071, 0x06310078, 0x0633017E, 0x0634017F, 0x06350180, + 0x06360181, 0x06370182, 0x06380078, 0x064100A7, 0x06420071, 0x06460078, 0x06470071, 0x06480078, + 0x06490071, 0x06540078, 0x06550071, 0x065D0078, 0x065E0071, 0x065F00B2, 0x066000A7, 0x06620078, + 0x066308A7, 0x06640078, 0x066508A7, 0x06660083, 0x06670078, 0x066A00A7, 0x066B0078, 0x06700071, + 0x06710078, 0x0673017E, 0x0674017F, 0x06750180, 0x06760181, 0x06770182, 0x06780078, 0x068100A7, + 0x06820071, 0x06860078, 0x06870071, 0x06880078, 0x06890071, 0x06940078, 0x06950071, 0x069D0078, + 0x069F00A7, 0x06A00083, 0x06A20078, 0x06A300A7, 0x06A40078, 0x06A508A7, 0x06A60083, 0x06A70078, + 0x06AB00A7, 0x06AC0078, 0x06B00071, 0x06B10078, 0x06B3017E, 0x06B4017F, 0x06B50180, 0x06B60181, + 0x06B70182, 0x06B80078, 0x06C100A7, 0x06C20071, 0x06CB0078, 0x06CD0071, 0x06DF0078, 0x06E00071, + 0x06E30078, 0x06E700A7, 0x06E90083, 0x06EA0078, 0x06EC00A7, 0x06EE08A7, 0x06EF00A7, 0x06F00078, + 0x06F900A7, 0x06FA0078, 0x07000071, 0x07180083, 0x07191071, 0x071A0083, 0x071D0078, 0x071F0008, + 0x07200071, 0x07230083, 0x07270097, 0x0728017E, 0x0729017F, 0x072A0180, 0x072B0181, 0x072C0182, + 0x072D0097, 0x072E0078, 0x07400071, 0x07410078, 0x07430071, 0x07440078, 0x07460071, 0x07470078, + 0x074A0071, 0x07540078, 0x07550071, 0x07580083, 0x07591071, 0x075A0083, 0x075E0071, 0x075F0078, + 0x07600071, 0x07620078, 0x07640083, 0x07670078, 0x0768017E, 0x0769017F, 0x076A0180, 0x076B0181, + 0x076C0182, 0x076D0078, 0x076E1071, 0x076F0078, 0x07800094, 0x07820097, 0x07890094, 0x078C0083, + 0x078D0094, 0x0790017E, 0x0791017F, 0x07920180, 0x07930181, 0x07940182, 0x079500B3, 0x079A0083, + 0x079D00BF, 0x079F00A7, 0x07A00071, 0x07A10871, 0x07A20071, 0x07A60871, 0x07A70071, 0x07AB0871, + 0x07AC0071, 0x07B40871, 0x07B50078, 0x07B80083, 0x07B90883, 0x07BB1083, 0x07BD0083, 0x07BF00A7, + 0x07C00883, 0x07C10083, 0x07C20097, 0x07C30083, 0x07C40071, 0x07C60078, 0x07C80083, 0x07C90883, + 0x07CA0083, 0x07CE0883, 0x07CF0083, 0x07D30883, 0x07D40083, 0x07DC0883, 0x07DD0083, 0x07DE0078, + 0x07DF0094, 0x07E60078, 0x07E70094, 0x07E80097, 0x07E90078, 0x08000071, 0x08150078, 0x08160083, + 0x081800A7, 0x08190078, 0x081B0083, 0x081D0078, 0x0820017E, 0x0821017F, 0x08220180, 0x08230181, + 0x08240182, 0x08250097, 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, + 0x08680071, 0x087D0097, 0x087E0078, 0x08800071, 0x08AD0078, 0x08AF0071, 0x08D10078, 0x08D40071, + 0x08FD0078, 0x09000071, 0x09240078, 0x09250071, 0x09270078, 0x09280071, 0x092B0078, 0x092D0071, + 0x092F0078, 0x09300071, 0x09440078, 0x09450071, 0x09470078, 0x09480071, 0x09580078, 0x09590071, + 0x095B0078, 0x095C0071, 0x095F0078, 0x09610071, 0x09630078, 0x09640071, 0x096B0078, 0x096C0071, + 0x09880078, 0x09890071, 0x098B0078, 0x098C0071, 0x09AD0078, 0x09AF0083, 0x09B00097, 0x09B400AD, + 0x09B500AE, 0x09B6012D, 0x09B7012E, 0x09B8012F, 0x09B90185, 0x09BA0186, 0x09BB0187, 0x09BC0188, + 0x09BD0184, 0x09BE0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FA0078, 0x0A000071, + 0x0B360097, 0x0B370071, 0x0B3B0078, 0x0B400071, 0x0B4D00B4, 0x0B4E0078, 0x0B500071, 0x0B750097, + 0x0B770189, 0x0B780078, 0x0B800071, 0x0B860078, 0x0B870071, 0x0B890083, 0x0B8A0078, 0x0B900071, + 0x0B990083, 0x0B9A0097, 0x0B9B0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB60078, + 0x0BB70071, 0x0BB80078, 0x0BB90083, 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB0083, 0x0BDF00A7, + 0x0BE40083, 0x0BEA0097, 0x0BEB0081, 0x0BEC0097, 0x0BED0008, 0x0BEE0083, 0x0BEF0078, 0x0BF0017E, + 0x0BF1017F, 0x0BF20180, 0x0BF30181, 0x0BF40182, 0x0BF50078, 0x0BF80106, 0x0BF90107, 0x0BFA0108, + 0x0BFB0109, 0x0BFC010A, 0x0BFD0078, 0x0C000006, 0x0C050083, 0x0C070078, 0x0C08017E, 0x0C09017F, + 0x0C0A0180, 0x0C0B0181, 0x0C0C0182, 0x0C0D0078, 0x0C100071, 0x0C210081, 0x0C220071, 0x0C3C0078, + 0x0C400071, 0x0C540083, 0x0C550078, 0x0C800071, 0x0C8E0078, 0x0C900083, 0x0C9100A7, 0x0C930083, + 0x0C9400C8, 0x0C960078, 0x0C9800A7, 0x0C9C0083, 0x0C9E0078, 0x0CA20006, 0x0CA3017E, 0x0CA4017F, + 0x0CA50180, 0x0CA60181, 0x0CA70182, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBA0078, 0x0CC00071, + 0x0CD50078, 0x0CD800A7, 0x0CE00071, 0x0CE400A7, 0x0CE50078, 0x0CE8017E, 0x0CE9017F, 0x0CEA0180, + 0x0CEB0181, 0x0CEC0182, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0B0083, 0x0D0C00A7, + 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0E970081, 0x0E987881, 0x0E9D0081, + 0x0E9E7881, 0x0EB17055, 0x0EB50055, 0x0ECD7881, 0x0EE00083, 0x0EE20078, 0x0F000865, 0x0F4B0855, + 0x0F4D098A, 0x0F4E0078, 0x0F500865, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, + 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, + 0x0FA408CA, 0x0FA70078, 0x0FA808C9, 0x0FAC08CA, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, + 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, + 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD9098B, 0x0FDA0078, 0x0FDB0855, 0x0FDC08CA, + 0x0FDD08D2, 0x0FDE1037, 0x0FE00837, 0x0FE1098B, 0x0FE20078, 0x0FE30855, 0x0FE408D5, 0x0FE60837, + 0x0FE808C9, 0x0FE90855, 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0837, 0x0FF008C9, + 0x0FF10855, 0x0FF20890, 0x0FF30855, 0x0FF408CA, 0x0FF508D7, 0x0FF60837, 0x0FF80078, 0x0FF9098B, + 0x0FFA0078, 0x0FFB0855, 0x0FFC08D9, 0x0FFD08DA, 0x0FFE0837, 0x0FFF0078, 0x10000805, 0x10011005, + 0x10035805, 0x10041005, 0x10050057, 0x1007018C, 0x10085899, 0x10090099, 0x100B1006, 0x100C018D, + 0x100D00DB, 0x100E018D, 0x100F00DB, 0x10100006, 0x10121006, 0x10130006, 0x1014018E, 0x1015018F, + 0x10160190, 0x10175853, 0x10180007, 0x10191007, 0x101A0006, 0x101B1006, 0x101C0126, 0x101D0006, + 0x101F0038, 0x10200006, 0x10220009, 0x10231006, 0x10250006, 0x102B1006, 0x102C0006, 0x102F1005, + 0x10300057, 0x10320078, 0x10350057, 0x10387855, 0x10390078, 0x103A7910, 0x103B7911, 0x103C7912, + 0x103D780B, 0x103E7809, 0x103F7855, 0x1040705D, 0x1041705B, 0x10427110, 0x10437111, 0x10447112, + 0x1045700B, 0x10467009, 0x10470078, 0x10487081, 0x104A0078, 0x10500008, 0x105B0078, 0x10680083, + 0x106E0095, 0x10700083, 0x10710095, 0x10720083, 0x10760078, 0x10801054, 0x10831077, 0x10841054, + 0x10852877, 0x10872855, 0x10882877, 0x10892855, 0x108A2877, 0x108B0054, 0x108C2877, 0x108F0054, + 0x10901054, 0x10910054, 0x10950991, 0x10962877, 0x10972855, 0x10982877, 0x109A1071, 0x109C2855, + 0x109D1054, 0x109E2855, 0x109F2877, 0x10A00019, 0x10A22877, 0x10A32855, 0x10A50019, 0x10A60078, + 0x10A9305F, 0x10AF3106, 0x10B01192, 0x10B11193, 0x10B21194, 0x10B31195, 0x10B41196, 0x10B51197, + 0x10B61198, 0x10B71199, 0x10B8119A, 0x10B9119B, 0x10BA119C, 0x10BB119D, 0x10BC119E, 0x10BD119F, + 0x10BE11A0, 0x10BF11A1, 0x10C001A2, 0x10C101A3, 0x10C20078, 0x10C80019, 0x10CA0054, 0x10CD0819, + 0x10CE0054, 0x10D10019, 0x10D20054, 0x10E60854, 0x10E70819, 0x10E80054, 0x10FA0019, 0x110000E8, + 0x11020019, 0x110408FB, 0x110500FC, 0x11070019, 0x110800E8, 0x11090059, 0x110A01A4, 0x110B0019, + 0x110D00E8, 0x11110019, 0x111500E8, 0x111610E8, 0x111800E8, 0x111A0019, 0x111C00E8, 0x111E00FE, + 0x111F00E8, 0x112008E8, 0x112101A5, 0x112200E8, 0x112308E8, 0x112500E8, 0x11260019, 0x112900FE, + 0x112B0019, 0x112F00E8, 0x11300019, 0x113200FE, 0x11360819, 0x113708FE, 0x113900FE, 0x113A08FE, + 0x113B00FE, 0x113C08FE, 0x113D00FE, 0x114008FE, 0x114100FE, 0x114208FE, 0x114300FE, 0x114408FE, + 0x114500FE, 0x11460019, 0x114700FD, 0x11490019, 0x115100FE, 0x11520019, 0x115300E8, 0x115401A6, + 0x115608E8, 0x115800FE, 0x115C0019, 0x115F00E8, 0x11600019, 0x116400FD, 0x116601A7, 0x11670019, + 0x116800FE, 0x11690019, 0x116B00FE, 0x117008FE, 0x117200FE, 0x117508FE, 0x11770019, 0x117800FE, + 0x11790102, 0x117A00E8, 0x117B0103, 0x117C00E8, 0x117D0104, 0x117E0105, 0x117F00E8, 0x11800054, + 0x118400FE, 0x11860054, 0x119000E8, 0x11910054, 0x11940809, 0x11950054, 0x119B0094, 0x11BD0054, + 0x11CA0094, 0x11CB0054, 0x11CD0019, 0x11DA00BF, 0x11DB0054, 0x11EE0078, 0x12000054, 0x12130078, + 0x12200054, 0x12250078, 0x123018C4, 0x123118C5, 0x123218C6, 0x123318C7, 0x1234191F, 0x1235191A, + 0x1236191B, 0x1237191C, 0x1238191D, 0x1239191E, 0x123A10C4, 0x123B10C5, 0x123C10C6, 0x123D10C7, + 0x123E111F, 0x123F111A, 0x1240111B, 0x1241111C, 0x1242111D, 0x1243111E, 0x1244105A, 0x124510E3, + 0x124610E4, 0x124710E5, 0x124811A8, 0x124911A9, 0x124A11AA, 0x124B11AB, 0x124C11AC, 0x124D11AD, + 0x124E1094, 0x125B1918, 0x12681919, 0x1275010B, 0x1276010C, 0x1277010D, 0x1278010E, 0x1279010F, + 0x127A0106, 0x127B0107, 0x127C0108, 0x127D0109, 0x127E010A, 0x127F00C3, 0x12800054, 0x12DB0019, + 0x12DC0054, 0x12E00019, 0x12E10054, 0x12FC0019, 0x13000054, 0x13370019, 0x13380054, 0x134E0078, + 0x13500054, 0x13590078, 0x13800054, 0x13820078, 0x13830054, 0x13850078, 0x13860054, 0x13A90078, + 0x13AC0054, 0x13AF0078, 0x13B00054, 0x13B4000A, 0x13BB00C4, 0x13BC00C5, 0x13BD00C6, 0x13BE00C7, + 0x13BF011F, 0x13C000C4, 0x13C100C5, 0x13C200C6, 0x13C300C7, 0x13C4011F, 0x13C500C4, 0x13C600C5, + 0x13C700C6, 0x13C800C7, 0x13C9011F, 0x13CA0078, 0x13CC0054, 0x13DF0078, 0x13E00019, 0x13E100FD, + 0x13E20009, 0x13E30078, 0x13E80019, 0x13E900E8, 0x13EA00FD, 0x13EB0019, 0x13EE00FD, 0x13EF0019, + 0x13F100FE, 0x13F3000A, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C10009, 0x14C601AE, + 0x14C701AF, 0x14C80009, 0x14CC0019, 0x14CD00E8, 0x14D80019, 0x14E000FE, 0x14E100E8, 0x14E200FE, + 0x14E30019, 0x14E400E8, 0x14E50019, 0x14E700FD, 0x14E90019, 0x14EA00FE, 0x14EB0019, 0x14EC000A, + 0x14EE0019, 0x14F000E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA01B0, 0x14FB00E8, 0x14FC00FE, + 0x14FD0019, 0x14FE000A, 0x14FF0019, 0x150500E8, 0x150E0019, 0x150F00E8, 0x15110019, 0x151400E8, + 0x151500FD, 0x15170019, 0x151A00FE, 0x151B0019, 0x151E00FE, 0x151F0019, 0x152B00E8, 0x152C0019, + 0x153200FE, 0x15330019, 0x153500E8, 0x15380019, 0x153900E8, 0x153A1019, 0x153B0019, 0x153C00FD, + 0x153D00E8, 0x153E00FD, 0x154200E8, 0x154500FD, 0x154600E8, 0x154800FD, 0x154E00E8, 0x155000FD, + 0x155100E8, 0x15520019, 0x155300FE, 0x155700FD, 0x155800E8, 0x155900FD, 0x155A00E8, 0x155D00FD, + 0x156300E8, 0x156600FD, 0x156B0019, 0x157101B1, 0x15730019, 0x157600FE, 0x15770019, 0x157900E8, + 0x157A0019, 0x157B00FD, 0x157D00E8, 0x157F0019, 0x15800054, 0x158A0078, 0x16000096, 0x16170078, + 0x16180098, 0x162F0078, 0x16400065, 0x16720054, 0x16750078, 0x167C0006, 0x167E005F, 0x167F0006, + 0x16800125, 0x16930078, 0x16980071, 0x16B30078, 0x16B77881, 0x16B80078, 0x16C00071, 0x16CB0078, + 0x16D00071, 0x16D30078, 0x16D40071, 0x16D70078, 0x16D80071, 0x16DB0078, 0x16DC0071, 0x16DF0078, + 0x16E00071, 0x16E30078, 0x16E40071, 0x16E70078, 0x16E80071, 0x16EB0078, 0x16EC0071, 0x16EF0078, + 0x17000006, 0x170100E0, 0x17030006, 0x17040126, 0x17050006, 0x170600E0, 0x17070006, 0x170B0099, + 0x170C0078, 0x170E00E0, 0x170F0078, 0x17400054, 0x174F1054, 0x17500054, 0x17791054, 0x177A0078, + 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18000006, 0x18020081, 0x180301B2, 0x1804000A, + 0x18090054, 0x180A000A, 0x180E00B4, 0x180F00BF, 0x181001B3, 0x181101B4, 0x181201B5, 0x181301B6, + 0x181401B7, 0x18150083, 0x18180081, 0x181B0054, 0x181C11B8, 0x181D0081, 0x181E0006, 0x181F0054, + 0x18200071, 0x18320871, 0x18350071, 0x18380871, 0x183A0071, 0x183B0871, 0x183D0071, 0x183E0871, + 0x183F0071, 0x184B0078, 0x184C0083, 0x184D1037, 0x184E0081, 0x184F8071, 0x18500071, 0x18620871, + 0x18650071, 0x18680871, 0x186A0071, 0x186B0871, 0x186D0071, 0x186E0871, 0x186F0071, 0x187B0871, + 0x187D0006, 0x187E0081, 0x187F8071, 0x18800078, 0x18820071, 0x18960078, 0x18981071, 0x18C70078, + 0x18C80094, 0x18C978B6, 0x18CA78B7, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, + 0x18F80071, 0x19001094, 0x190E1054, 0x190F0078, 0x191010B6, 0x191110B7, 0x191210B8, 0x191310B9, + 0x191410B0, 0x19151094, 0x19220078, 0x192819B9, 0x192919BA, 0x192A19BB, 0x192B19BC, 0x192C19BD, + 0x192D19BE, 0x192E19BF, 0x192F19C0, 0x19301894, 0x193E1854, 0x193F0094, 0x194018B6, 0x194118B7, + 0x194218B8, 0x194318B9, 0x194418B0, 0x19451894, 0x195819C1, 0x195919C2, 0x195A19C3, 0x195B19C4, + 0x195C19C5, 0x195D19C6, 0x195E19C7, 0x195F19C8, 0x19601094, 0x19666854, 0x19681894, 0x197F0078, + 0x19806894, 0x19AC1094, 0x19B86894, 0x19BB6854, 0x19BD6894, 0x19EF6854, 0x19F01094, 0x19FF6854, + 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x500A0081, 0x500B0071, + 0x52460078, 0x52480054, 0x52630078, 0x53800037, 0x538B0078, 0x54000071, 0x54050083, 0x54060071, + 0x541100A7, 0x54120083, 0x541300A7, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, + 0x7000013F, 0x7C800871, 0x7D070071, 0x7D0A0871, 0x7D0F0071, 0x7D120871, 0x7D130071, 0x7D150871, + 0x7D170078, 0x7D180871, 0x7D350078, 0x7D380871, 0x7D6D0078, 0x7D801055, 0x7D830078, 0x7D891055, + 0x7D8C0078, 0x7D8E089B, 0x7D90289B, 0x7D94280B, 0x7D95089B, 0x7D9B0078, 0x7D9C089B, 0x7D9E0078, + 0x7DA0089B, 0x7DA20078, 0x7DA3089B, 0x7DA7109B, 0x7DA8209E, 0x7DAA489E, 0x7DAB209E, 0x7DAC489E, + 0x7DAD209E, 0x7DAE489E, 0x7DAF209E, 0x7DB0489E, 0x7DB1209E, 0x7DB2489E, 0x7DB3209E, 0x7DB4489E, + 0x7DB5209E, 0x7DB6489E, 0x7DB7209E, 0x7DB8489E, 0x7DB9209E, 0x7DBA489E, 0x7DBB209E, 0x7DBC489E, + 0x7DBD209E, 0x7DBE489E, 0x7DBF209E, 0x7DC0489E, 0x7DC1209E, 0x7DC8489E, 0x7DC9209E, 0x7DCA489E, + 0x7DCB209E, 0x7DCC489E, 0x7DCD209E, 0x7DCE489E, 0x7DCF209E, 0x7DD1489E, 0x7DD2209E, 0x7DD4489E, + 0x7DD5209E, 0x7DD6489E, 0x7DD7209E, 0x7DD90078, 0x7DE9409E, 0x7DEA389E, 0x7DEB409E, 0x7DEF209E, + 0x7DF3489E, 0x7DF5209E, 0x7DFC409E, 0x7DFD389E, 0x7DFE209E, 0x7DFF489E, 0x7E00409E, 0x7E32209E, + 0x7E4B389E, 0x7E6F489E, 0x7E7A409E, 0x7E88209E, 0x7E96389E, 0x7E9A489E, 0x7E9E409E, 0x7E9F00BF, + 0x7EA00078, 0x7EA8209E, 0x7EA9389E, 0x7EAD209E, 0x7EAE389E, 0x7EAF209E, 0x7EB0389E, 0x7EB3209E, + 0x7EB5389E, 0x7EB7209E, 0x7EB9389E, 0x7EBA209E, 0x7EBB389E, 0x7EBC209E, 0x7EBE389E, 0x7EBF209E, + 0x7EC1389E, 0x7EC2209E, 0x7EC4389E, 0x7EC5209E, 0x7EC6389E, 0x7EC80078, 0x7EC9389E, 0x7ECB209E, + 0x7ECE389E, 0x7ECF209E, 0x7EDA389E, 0x7EDB209E, 0x7EE1389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, + 0x7EFE0054, 0x7EFF0078, 0x7F000083, 0x7F088006, 0x7F0B80B4, 0x7F0C8006, 0x7F0D0078, 0x7F100083, + 0x7F120078, 0x7F188099, 0x7F198038, 0x7F1A80B4, 0x7F220006, 0x7F2380B4, 0x7F241006, 0x7F261038, + 0x7F286006, 0x7F290078, 0x7F2A600C, 0x7F2B6006, 0x7F2C60B4, 0x7F2F6007, 0x7F306006, 0x7F31600D, + 0x7F326019, 0x7F330078, 0x7F346008, 0x7F356006, 0x7F360078, 0x7F38489E, 0x7F39009E, 0x7F3A0078, + 0x7F3B489E, 0x7F40409E, 0x7F45389E, 0x7F46409E, 0x7F48389E, 0x7F49409E, 0x7F4B389E, 0x7F4C409E, + 0x7F4D389E, 0x7F4E409E, 0x7F4F389E, 0x7F50409E, 0x7F51389E, 0x7F52409E, 0x7F53389E, 0x7F54409E, + 0x7F59389E, 0x7F5A409E, 0x7F5B389E, 0x7F5C409E, 0x7F5D389E, 0x7F5E409E, 0x7F5F389E, 0x7F60409E, + 0x7F61389E, 0x7F62409E, 0x7F63389E, 0x7F64409E, 0x7F65389E, 0x7F66409E, 0x7F67389E, 0x7F68409E, + 0x7F69389E, 0x7F6A409E, 0x7F6B389E, 0x7F6C409E, 0x7F6D389E, 0x7F6E409E, 0x7F6F389E, 0x7F70409E, + 0x7F71389E, 0x7F72409E, 0x7F73389E, 0x7F74409E, 0x7F75389E, 0x7F76409E, 0x7F79389E, 0x7F7A409E, + 0x7F7E0078, 0x7F7F0057, 0x7F808806, 0x7F818807, 0x7F838806, 0x7F84880A, 0x7F85880B, 0x7F86880D, + 0x7F87880C, 0x7F88880F, 0x7F898811, 0x7F8A8813, 0x7F8B8815, 0x7F8C8817, 0x7F8D8806, 0x7F8E8819, + 0x7F8F8806, 0x7F908860, 0x7F9D8835, 0x7F9E8836, 0x7F9F8838, 0x7FA08861, 0x7FAD8835, 0x7FAE8836, + 0x7FAF8809, 0x7FB05006, 0x7FB1500A, 0x7FB25006, 0x7FB35071, 0x7FCF5081, 0x7FD05071, 0x7FDF0078, + 0x7FE15071, 0x7FE40078, 0x7FE55071, 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEE0078, + 0x7FF08808, 0x7FF18837, 0x7FF28808, 0x7FF30078, 0x7FF45019, 0x7FF65054, 0x7FF70078, 0x7FFC0141, + 0x7FFE0054, 0x7FFF0078, 0x80000071, 0x80130078, 0x80140071, 0x801D0078, 0x801E0071, 0x80270078, + 0x80280071, 0x802F0078, 0x80400071, 0x807D0078, 0x80800006, 0x80810078, 0x808300AD, 0x808400AE, + 0x8085012D, 0x8086012E, 0x8087012F, 0x80880185, 0x80890186, 0x808A0187, 0x808B0188, 0x808C0184, + 0x808D01C9, 0x808E01CA, 0x808F01CB, 0x809001CC, 0x809101CD, 0x809201CE, 0x809301CF, 0x809401D0, + 0x809500BE, 0x809601D1, 0x809701D2, 0x809801D3, 0x809901D4, 0x809A0078, 0x809B0094, 0x80A0014E, + 0x80A10152, 0x80A20153, 0x80A30157, 0x80A40154, 0x80A50155, 0x80A60156, 0x80A70152, 0x80A80150, + 0x80A90153, 0x80AA01D5, 0x80AB0154, 0x80AC014F, 0x80AD0158, 0x80AF0152, 0x80B00154, 0x80B201D6, + 0x80B30150, 0x80B501D7, 0x80B60153, 0x80B80156, 0x80B90152, 0x80BA005F, 0x80BC0054, 0x80C50078, + 0x81800071, 0x818F0078, 0x8190012D, 0x819100BB, 0x81920078, 0x81980071, 0x81A50078, 0x81C00071, + 0x81CF0097, 0x81D00071, 0x81E20078, 0x81E40071, 0x81E8014F, 0x81E90154, 0x81EA0155, 0x81EB0078, + 0x8200015B, 0x8214015C, 0x82280071, 0x824F0078, 0x8250017E, 0x8251017F, 0x82520180, 0x82530181, + 0x82540182, 0x82550078, 0x8400009B, 0x84030078, 0x8405009B, 0x841C0078, 0x841F009B, 0x84200078, + 0x85000083, 0x85030078, 0x85060083, 0x8508009B, 0x851A0078, 0x851C0083, 0x851D0078, 0x851F0083, + 0x852001D8, 0x852101D9, 0x852201DA, 0x852301DB, 0x85240078, 0x8528009A, 0x852C0078, 0xE8000094, + 0xE87B0078, 0xE8800094, 0xE8930078, 0xE8950094, 0xE8AF0894, 0xE8B200A7, 0xE8B30083, 0xE8B50094, + 0xE8B600A7, 0xE8B90057, 0xE8BD0083, 0xE8C10094, 0xE8C20083, 0xE8C60094, 0xE8D50083, 0xE8D70094, + 0xE8DD0894, 0xE8E00094, 0xE8EF0078, 0xE9000054, 0xE9210083, 0xE9220054, 0xE9230078, 0xE9800054, + 0xE9AB0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, 0xEA272855, 0xEA2A0078, 0xEA2B2855, 0xEA342877, + 0xEA412855, 0xEA4E0078, 0xEA4F2877, 0xEA500078, 0xEA522877, 0xEA530078, 0xEA542877, 0xEA560078, + 0xEA572877, 0xEA5B2855, 0xEA682877, 0xEA752855, 0xEA822877, 0xEA850078, 0xEA862877, 0xEA8A0078, + 0xEA8B2877, 0xEA8E0078, 0xEA8F2855, 0xEA9C2877, 0xEA9F0078, 0xEAA02877, 0xEAA20078, 0xEAA52877, + 0xEAA80078, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, 0xEADD2855, 0xEAEA2877, 0xEAF72855, + 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, 0xEB452855, 0xEB530078, 0xEB542877, + 0xEB6029DC, 0xEB612855, 0xEB6D29DC, 0xEB6E2855, 0xEB712877, 0xEB7D29DC, 0xEB7E2855, 0xEB8A29DC, + 0xEB8B2855, 0xEB8E2877, 0xEB9A29DC, 0xEB9B2855, 0xEBA729DC, 0xEBA82855, 0xEBAB2877, 0xEBB729DC, + 0xEBB82855, 0xEBC429DC, 0xEBC52855, 0xEBC82877, 0xEBD429DC, 0xEBD52855, 0xEBE129DC, 0xEBE22855, + 0xEBE50078, 0xEBE7280F, 0xEBE82811, 0xEBE92813, 0xEBEA2815, 0xEBEB2817, 0xEBEC280F, 0xEBED2811, + 0xEBEE2813, 0xEBEF2815, 0xEBF02817, 0xEBF1280F, 0xEBF22811, 0xEBF32813, 0xEBF42815, 0xEBF52817, + 0xEBF6280F, 0xEBF72811, 0xEBF82813, 0xEBF92815, 0xEBFA2817, 0xEBFB280F, 0xEBFC2811, 0xEBFD2813, + 0xEBFE2815, 0xEBFF2817, 0xEC000078 + }; + + static const uint32_t a17[] = { + 0x00000071, 0x536B0078, 0x7C000871, 0x7D0F0078 + }; + + static const uint32_t a23[] = { + 0x00000057, 0x00010078, 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 + }; + + static const uint32_t a24[] = { + 0x0000013F, 0x7FFF0078 + }; + + + // The full set of all arrays to be searched. + static const Range FULL_DATA[] = { + {sizeof(a0)/sizeof(uint32_t), a0}, + {sizeof(a1)/sizeof(uint32_t), a1}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a7)/sizeof(uint32_t), a7}, + {sizeof(a8)/sizeof(uint32_t), a8}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a16)/sizeof(uint32_t), a16}, + {sizeof(a17)/sizeof(uint32_t), a17}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a23)/sizeof(uint32_t), a23}, + {sizeof(a24)/sizeof(uint32_t), a24}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} + }; + + // Array of uppercase differences + static const short UCDIFF[] = { + 0, -32, 743, 121, -1, -232, -300, 97, + 163, 130, 56, -2, -79, -210, -206, -205, + -202, -203, -207, -209, -211, -213, -214, -218, + -217, -219, -83, 84, -38, -37, -31, -64, + -63, -62, -57, -47, -54, -86, -80, 7, + -96, -48, -59, 8, 74, 86, 100, 128, + 112, 126, 9, -7205, -16, -26, -7264, -40 + }; + + // Array of lowercase differences + static const short LCDIFF[] = { + 0, 32, 1, -199, -121, 210, 206, 205, + 79, 202, 203, 207, 211, 209, 213, 214, + 218, 217, 219, 2, -97, -56, -130, -163, + 83, 38, 37, 64, 63, -60, -7, 80, + 48, 7264, -8, -74, -9, -86, -100, -112, + -128, -126, -7517, -8383, -8262, 16, 26, 40 + }; + + // Array of titlecase differences + static const short TCDIFF[] = { + 3, 1, 0, -1 + }; + + // Array of mirrored character differences + static const short MIRROR_DIFF[] = { + 0, 1, -1, 2, -2, 16, -16, 3, + -3, 2016, 138, 1824, 2104, 2108, 2106, -138, + 8, 7, -8, -7, -1824, -2016, -2104, -2106, + -2108 + }; + + // Array of all possible numeric values + static const int NUMERICS[] = { + -1, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, -2, 100, 1000, + 40, 50, 60, 70, 80, 90, 10000, 500, + 5000, 36, 37, 38, 39, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 200, 300, + 400, 600, 700, 800, 900, 2000, 3000, 4000, + 6000, 7000, 8000, 9000, 20000, 30000, 40000, 50000, + 60000, 70000, 80000, 90000 + }; + + // All possible packed data values, no duplicates + static const uint32_t PACKED_DATA[] = { + 0x00000000, 0x0000012F, 0x0000016F, 0x0000014F, 0x0000018F, 0x0000018C, 0x000001B8, 0x000000B8, + 0x000000BA, 0x020005B5, 0x040005B6, 0x00000099, 0x000000F8, 0x00000094, 0x02000069, 0x04000069, + 0x06000069, 0x08000069, 0x0A000069, 0x0C000069, 0x0E000069, 0x10000069, 0x12000069, 0x14000069, + 0x060005B9, 0x000001B9, 0x080005B9, 0x16020001, 0x18020001, 0x1A020001, 0x1C020001, 0x1E020001, + 0x20020001, 0x22020001, 0x24020001, 0x26020001, 0x28020001, 0x2A020001, 0x2C020001, 0x2E020001, + 0x30020001, 0x32020001, 0x34020001, 0x36020001, 0x38020001, 0x3A020001, 0x3C020001, 0x3E020001, + 0x40020001, 0x42020001, 0x44020001, 0x46020001, 0x48020001, 0x060005B5, 0x080005B6, 0x000001BB, + 0x000001B7, 0x16000802, 0x18000802, 0x1A000802, 0x1C000802, 0x1E000802, 0x20000802, 0x22000802, + 0x24000802, 0x26000802, 0x28000802, 0x2A000802, 0x2C000802, 0x2E000802, 0x30000802, 0x32000802, + 0x34000802, 0x36000802, 0x38000802, 0x3A000802, 0x3C000802, 0x3E000802, 0x40000802, 0x42000802, + 0x44000802, 0x46000802, 0x48000802, 0x000000EC, 0x000001BC, 0x00000002, 0x0A0005BD, 0x00000130, + 0x000000BC, 0x000000B9, 0x0600006B, 0x0800006B, 0x00001002, 0x0400006B, 0x0C0005BE, 0x4A0001AB, + 0x00020001, 0x00000802, 0x00001802, 0x00040001, 0x00060001, 0x00002002, 0x00080001, 0x000C0001, + 0x000E0001, 0x00100001, 0x00140001, 0x00160001, 0x00180001, 0x00004002, 0x00004802, 0x00200001, + 0x00220001, 0x00000005, 0x00A60001, 0x01805802, 0x01042003, 0x00280001, 0x002C0001, 0x00000001, + 0x00000000, 0x00007002, 0x00007802, 0x00009802, 0x0000A802, 0x0000B802, 0x0000C002, 0x0000C802, + 0x0000D002, 0x00000004, 0x000001A4, 0x00000106, 0x00320001, 0x00340001, 0x00360001, 0x00380001, + 0x0000E002, 0x0000E802, 0x0000F002, 0x0000F802, 0x00010002, 0x00010802, 0x00012002, 0x00012802, + 0x00013802, 0x003A0001, 0x003E0001, 0x00013002, 0x0000001C, 0x00000107, 0x00400001, 0x00000018, + 0x00014802, 0x000001B4, 0x00000038, 0x00000025, 0x00000050, 0x00000058, 0x00000045, 0x00000044, + 0x020000C9, 0x060000C9, 0x0A0000C9, 0x0E0000C9, 0x120000C9, 0x000000D8, 0x0000005C, 0x00000008, + 0x02000009, 0x06000009, 0x0A000009, 0x0E000009, 0x12000009, 0x0400000B, 0x0800000B, 0x0000000B, + 0x1600000B, 0x4E00000B, 0x00000006, 0x4A00000B, 0x000001B5, 0x00420001, 0x0600000B, 0x0A00000B, + 0x0E00000B, 0x1200000B, 0x3E00000B, 0x5200000B, 0x5600000B, 0x5A00000B, 0x5C00000B, 0x000001B6, + 0x2400000A, 0x2800000A, 0x00000010, 0x020001AB, 0x060001AB, 0x0A0001AB, 0x0E0001AB, 0x120001AB, + 0x00000108, 0x00015802, 0x00440001, 0x00016002, 0x00016802, 0x00017002, 0x00017802, 0x00018002, + 0x00018802, 0x00440003, 0x00460001, 0x00480003, 0x00019802, 0x004A0001, 0x004C0001, 0x004E0001, + 0x003C0001, 0x00500001, 0x00520001, 0x000001BD, 0x0000018D, 0x000001D0, 0x00000250, 0x00000230, + 0x040005BE, 0x000000F9, 0x0200006B, 0x0A00006B, 0x0E00006B, 0x1200006B, 0x00540001, 0x00560001, + 0x000005B9, 0x045A000A, 0x085A000A, 0x0C5A000A, 0x105A000A, 0x145A000A, 0x185A000A, 0x525A000A, + 0x5E5A000A, 0x0401A00A, 0x0801A00A, 0x0C01A00A, 0x1001A00A, 0x1401A00A, 0x1801A00A, 0x5201A00A, + 0x5E01A00A, 0x4E00000A, 0x5C00000A, 0x0E0005B9, 0x100005B9, 0x020005B9, 0x040005B9, 0x160005B9, + 0x180005B9, 0x1A0005B9, 0x200005B9, 0x220005B9, 0x240005B9, 0x260005B9, 0x040001AB, 0x080001AB, + 0x0C0001AB, 0x100001AB, 0x140001AB, 0x180001AB, 0x1C0001AB, 0x200001AB, 0x240001AB, 0x280001AB, + 0x0C00006B, 0x1000006B, 0x1400006B, 0x1800006B, 0x1C00006B, 0x2000006B, 0x2400006B, 0x2800006B, + 0x005C001C, 0x0001A81C, 0x1A0001AB, 0x1E0001AB, 0x220001AB, 0x260001AB, 0x2A0001AB, 0x160001AB, + 0x020005B6, 0x100005B6, 0x280005B9, 0x2C0005B9, 0x300005B9, 0x0001B002, 0x020005BD, 0x0600000A, + 0x0A00000A, 0x0E00000A, 0x1200000A, 0x1600000A, 0x3E00000A, 0x0C00000B, 0x1000000B, 0x1400000B, + 0x2E0001AB, 0x320001AB, 0x360001AB, 0x3A0001AB, 0x3E0001AB, 0x420001AB, 0x460001AB, 0x640001AB, + 0x680001AB, 0x6A0001AB, 0x6E0001AB, 0x720001AB, 0x760001AB, 0x7A0001AB, 0x00000013, 0x00000012, + 0x0000005A, 0x000001B0, 0x7C00000B, 0x8000000B, 0x8200000B, 0x8600000B, 0x8C00000B, 0x6000000B, + 0x9200000B, 0x9600000B, 0x9800000B, 0x9C00000B, 0xA000000B, 0xA400000B, 0x4A0001AA, 0x040001AA, + 0x520001AA, 0x600001AA, 0x0C0001AA, 0x5E0001AA, 0x160001AA, 0x4C0001AA, 0x4E0001AA, 0x9E0001AA, + 0x060001AA, 0x8800000A, 0x2A0001AA, 0x005E0001, 0x0001B802, 0x0400002B, 0x0800002B, 0x1600002B, + 0x4C00002B, 0x00002802, 0x00003002, 0x000A0001, 0x00120001, 0x00003802, 0x001A0001, 0x001C0001, + 0x001E0001, 0x00240001, 0x00005002, 0x00006002, 0x002A0001, 0x002E0001, 0x00300001, 0x00006802, + 0x00008002, 0x00008802, 0x00009002, 0x0000A002, 0x0000B002, 0x0000D906, 0x00011002, 0x00011802, + 0x00014002, 0x040000C9, 0x080000C9, 0x0C0000C9, 0x100000C9, 0x140000C9, 0x04000009, 0x08000009, + 0x0C000009, 0x10000009, 0x14000009, 0x2200000B, 0x4C00000B, 0x2A00000B, 0x5000000B, 0x5400000B, + 0x5800000B, 0x2600000A, 0x00015002, 0x00019002, 0x00000030, 0x000001BE, 0x0000014E, 0x00000210, + 0x000001F0, 0x00580001, 0x065A000A, 0x0A5A000A, 0x0E5A000A, 0x125A000A, 0x165A000A, 0x1A5A000A, + 0x4C5A000A, 0x4E5A000A, 0x0601A00A, 0x0A01A00A, 0x0E01A00A, 0x1201A00A, 0x1601A00A, 0x1A01A00A, + 0x4C01A00A, 0x4E01A00A, 0x6000000A, 0x0000000A, 0x120005B9, 0x140005B9, 0x1C0005B9, 0x1E0005B9, + 0x1600006B, 0x1A00006B, 0x1E00006B, 0x2200006B, 0x2600006B, 0x2A00006B, 0x0E0005B5, 0x040005B5, + 0x2A0005B9, 0x2E0005B9, 0x0200000A, 0x0400000A, 0x0800000A, 0x0C00000A, 0x1000000A, 0x1400000A, + 0x2A00000A, 0x2C0001AB, 0x300001AB, 0x340001AB, 0x380001AB, 0x3C0001AB, 0x400001AB, 0x440001AB, + 0x480001AB, 0x620001AB, 0x660001AB, 0x500001AB, 0x6C0001AB, 0x700001AB, 0x740001AB, 0x780001AB, + 0x520001AB, 0x7E00000B, 0x5E00000B, 0x8400000B, 0x8800000B, 0x8A00000B, 0x8E00000B, 0x9000000B, + 0x9400000B, 0x9A00000B, 0x9E00000B, 0xA200000B, 0xA600000B, 0x5C0001AA, 0x3E0001AA, 0x7E0001AA, + 0x0600002B, 0x0A00002B, 0x2A00002B, 0x4E00002B, 0x00000019 + }; +} diff --git a/libs/utils/executablepath_darwin.cpp b/libs/utils/executablepath_darwin.cpp new file mode 100644 index 000000000..2e3c3a01f --- /dev/null +++ b/libs/utils/executablepath_darwin.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008 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 +#import +#include + +void executablepath(char s[PATH_MAX]) +{ + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + CFDictionaryRef dict; + dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); + CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, + CFSTR("CFBundleExecutable")); + CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8); +} + diff --git a/libs/utils/executablepath_linux.cpp b/libs/utils/executablepath_linux.cpp new file mode 100644 index 000000000..b8d2a3d6f --- /dev/null +++ b/libs/utils/executablepath_linux.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 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 + +void executablepath(char exe[PATH_MAX]) +{ + char proc[100]; + sprintf(proc, "/proc/%d/exe", getpid()); + + int err = readlink(proc, exe, PATH_MAX); +} + diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c new file mode 100644 index 000000000..ba1952033 --- /dev/null +++ b/libs/utils/futex_synchro.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2008 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 + +#include + + +// This futex glue code is need on desktop linux, but is already part of bionic. +#if !defined(HAVE_FUTEX_WRAPPERS) + +#include +typedef unsigned int u32; +#define asmlinkage +#define __user +#include +#include + + +int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) +{ + int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); + return err == 0 ? 0 : -errno; +} + +int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout) +{ + return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0); +} + +int __futex_wake(volatile void *ftx, int count) +{ + return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0); +} + +int __atomic_cmpxchg(int old, int _new, volatile int *ptr) +{ + return android_atomic_cmpxchg(old, _new, ptr); +} + +int __atomic_swap(int _new, volatile int *ptr) +{ + return android_atomic_swap(_new, ptr); +} + +int __atomic_dec(volatile int *ptr) +{ + return android_atomic_dec(ptr); +} + +#else // !defined(__arm__) + +int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout); +int __futex_wake(volatile void *ftx, int count); + +int __atomic_cmpxchg(int old, int _new, volatile int *ptr); +int __atomic_swap(int _new, volatile int *ptr); +int __atomic_dec(volatile int *ptr); + +#endif // !defined(HAVE_FUTEX_WRAPPERS) + + +// lock states +// +// 0: unlocked +// 1: locked, no waiters +// 2: locked, maybe waiters + +void futex_mutex_init(futex_mutex_t *m) +{ + m->value = 0; +} + +int futex_mutex_lock(futex_mutex_t *m, unsigned msec) +{ + if(__atomic_cmpxchg(0, 1, &m->value) == 0) { + return 0; + } + if(msec == FUTEX_WAIT_INFINITE) { + while(__atomic_swap(2, &m->value) != 0) { + __futex_wait(&m->value, 2, 0); + } + } else { + struct timespec ts; + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + while(__atomic_swap(2, &m->value) != 0) { + if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) { + return -1; + } + } + } + return 0; +} + +int futex_mutex_trylock(futex_mutex_t *m) +{ + if(__atomic_cmpxchg(0, 1, &m->value) == 0) { + return 0; + } + return -1; +} + +void futex_mutex_unlock(futex_mutex_t *m) +{ + if(__atomic_dec(&m->value) != 1) { + m->value = 0; + __futex_wake(&m->value, 1); + } +} + +/* XXX *technically* there is a race condition that could allow + * XXX a signal to be missed. If thread A is preempted in _wait() + * XXX after unlocking the mutex and before waiting, and if other + * XXX threads call signal or broadcast UINT_MAX times (exactly), + * XXX before thread A is scheduled again and calls futex_wait(), + * XXX then the signal will be lost. + */ + +void futex_cond_init(futex_cond_t *c) +{ + c->value = 0; +} + +int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec) +{ + if(msec == FUTEX_WAIT_INFINITE){ + int oldvalue = c->value; + futex_mutex_unlock(m); + __futex_wait(&c->value, oldvalue, 0); + futex_mutex_lock(m, FUTEX_WAIT_INFINITE); + return 0; + } else { + int oldvalue = c->value; + struct timespec ts; + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + futex_mutex_unlock(m); + const int err = __futex_wait(&c->value, oldvalue, &ts); + futex_mutex_lock(m, FUTEX_WAIT_INFINITE); + return err; + } +} + +void futex_cond_signal(futex_cond_t *c) +{ + __atomic_dec(&c->value); + __futex_wake(&c->value, 1); +} + +void futex_cond_broadcast(futex_cond_t *c) +{ + __atomic_dec(&c->value); + __futex_wake(&c->value, INT_MAX); +} + diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp new file mode 100644 index 000000000..dc89d15b6 --- /dev/null +++ b/libs/utils/misc.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Miscellaneous utility functions. +// +#include + +#include +#include +#include +#include +#include + +using namespace android; + +namespace android { + +/* + * Like strdup(), but uses C++ "new" operator instead of malloc. + */ +char* strdupNew(const char* str) +{ + char* newStr; + int len; + + if (str == NULL) + return NULL; + + len = strlen(str); + newStr = new char[len+1]; + memcpy(newStr, str, len+1); + + return newStr; +} + +/* + * Concatenate an argument vector. + */ +char* concatArgv(int argc, const char* const argv[]) +{ + char* newStr = NULL; + int len, totalLen, posn, idx; + + /* + * First, figure out the total length. + */ + totalLen = idx = 0; + while (1) { + if (idx == argc || argv[idx] == NULL) + break; + if (idx) + totalLen++; // leave a space between args + totalLen += strlen(argv[idx]); + idx++; + } + + /* + * Alloc the string. + */ + newStr = new char[totalLen +1]; + if (newStr == NULL) + return NULL; + + /* + * Finally, allocate the string and copy data over. + */ + idx = posn = 0; + while (1) { + if (idx == argc || argv[idx] == NULL) + break; + if (idx) + newStr[posn++] = ' '; + + len = strlen(argv[idx]); + memcpy(&newStr[posn], argv[idx], len); + posn += len; + + idx++; + } + + assert(posn == totalLen); + newStr[posn] = '\0'; + + return newStr; +} + +/* + * Count the #of args in an argument vector. Don't count the final NULL. + */ +int countArgv(const char* const argv[]) +{ + int count = 0; + + while (argv[count] != NULL) + count++; + + return count; +} + + +#include +/* + * Get a file's type. + */ +FileType getFileType(const char* fileName) +{ + struct stat sb; + + if (stat(fileName, &sb) < 0) { + if (errno == ENOENT || errno == ENOTDIR) + return kFileTypeNonexistent; + else { + fprintf(stderr, "getFileType got errno=%d on '%s'\n", + errno, fileName); + return kFileTypeUnknown; + } + } else { + if (S_ISREG(sb.st_mode)) + return kFileTypeRegular; + else if (S_ISDIR(sb.st_mode)) + return kFileTypeDirectory; + else if (S_ISCHR(sb.st_mode)) + return kFileTypeCharDev; + else if (S_ISBLK(sb.st_mode)) + return kFileTypeBlockDev; + else if (S_ISFIFO(sb.st_mode)) + return kFileTypeFifo; +#ifdef HAVE_SYMLINKS + else if (S_ISLNK(sb.st_mode)) + return kFileTypeSymlink; + else if (S_ISSOCK(sb.st_mode)) + return kFileTypeSocket; +#endif + else + return kFileTypeUnknown; + } +} + +/* + * Get a file's modification date. + */ +time_t getFileModDate(const char* fileName) +{ + struct stat sb; + + if (stat(fileName, &sb) < 0) + return (time_t) -1; + + return sb.st_mtime; +} + +/* + * Round up to the next highest power of 2. + * + * Found on http://graphics.stanford.edu/~seander/bithacks.html. + */ +unsigned int roundUpPower2(unsigned int val) +{ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + + return val; +} + +}; // namespace android + diff --git a/libs/utils/ported.cpp b/libs/utils/ported.cpp new file mode 100644 index 000000000..656e46f09 --- /dev/null +++ b/libs/utils/ported.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Ports of standard functions that don't exist on a specific platform. +// +// Note these are NOT in the "android" namespace. +// +#include + +#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP) +# include +# include +#endif + + +#if defined(NEED_GETTIMEOFDAY) +/* + * Replacement gettimeofday() for Windows environments (primarily MinGW). + * + * Ignores "tz". + */ +int gettimeofday(struct timeval* ptv, struct timezone* tz) +{ + long long nsTime; // time in 100ns units since Jan 1 1601 + FILETIME ft; + + if (tz != NULL) { + // oh well + } + + ::GetSystemTimeAsFileTime(&ft); + nsTime = (long long) ft.dwHighDateTime << 32 | + (long long) ft.dwLowDateTime; + // convert to time in usec since Jan 1 1970 + ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL); + ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL); + + return 0; +} +#endif + +#if defined(NEED_USLEEP) +// +// Replacement usleep for Windows environments (primarily MinGW). +// +void usleep(unsigned long usec) +{ + // Win32 API function Sleep() takes milliseconds + ::Sleep((usec + 500) / 1000); +} +#endif + +#if 0 //defined(NEED_PIPE) +// +// Replacement pipe() command for MinGW +// +// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the +// SecurityAttributes argument to CreatePipe(). This means the handles +// aren't inherited when a new process is created. The examples I've seen +// use it, possibly because there's a lot of junk going on behind the +// scenes. (I'm assuming "process" and "thread" are different here, so +// we should be okay spinning up a thread.) The recommended practice is +// to dup() the descriptor you want the child to have. +// +// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O. +// You can't use select() either, since that only works on sockets. The +// Windows API calls that are useful here all operate on a HANDLE, not +// an integer file descriptor, and I don't think you can get there from +// here. The "named pipe" stuff is insane. +// +int pipe(int filedes[2]) +{ + return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT); +} +#endif + +#if defined(NEED_SETENV) +/* + * MinGW lacks these. For now, just stub them out so the code compiles. + */ +int setenv(const char* name, const char* value, int overwrite) +{ + return 0; +} +void unsetenv(const char* name) +{ +} +char* getenv(const char* name) +{ + return NULL; +} +#endif diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h new file mode 100644 index 000000000..c269976f4 --- /dev/null +++ b/opengl/include/EGL/egl.h @@ -0,0 +1,330 @@ +/* -*- mode: c; tab-width: 8; -*- */ +/* vi: set sw=4 ts=8: */ +/* Reference version of egl.h for EGL 1.4. + * $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ + */ + +/* +** Copyright (c) 2007-2009 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#ifndef __egl_h_ +#define __egl_h_ + +/* All platform-dependent types and macro boilerplate (such as EGLAPI + * and EGLAPIENTRY) should go in eglplatform.h. + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* EGL Types */ +/* EGLint is defined in eglplatform.h */ +typedef unsigned int EGLBoolean; +typedef unsigned int EGLenum; +typedef void *EGLConfig; +typedef void *EGLContext; +typedef void *EGLDisplay; +typedef void *EGLSurface; +typedef void *EGLClientBuffer; + +/* EGL Versioning */ +#define EGL_VERSION_1_0 1 +#define EGL_VERSION_1_1 1 +#define EGL_VERSION_1_2 1 +#define EGL_VERSION_1_3 1 +#define EGL_VERSION_1_4 1 + +/* EGL Enumerants. Bitmasks and other exceptional cases aside, most + * enums are assigned unique values starting at 0x3000. + */ + +/* EGL aliases */ +#define EGL_FALSE 0 +#define EGL_TRUE 1 + +/* Out-of-band handle values */ +#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0) +#define EGL_NO_CONTEXT ((EGLContext)0) +#define EGL_NO_DISPLAY ((EGLDisplay)0) +#define EGL_NO_SURFACE ((EGLSurface)0) + +/* Out-of-band attribute value */ +#define EGL_DONT_CARE ((EGLint)-1) + +/* Errors / GetError return values */ +#define EGL_SUCCESS 0x3000 +#define EGL_NOT_INITIALIZED 0x3001 +#define EGL_BAD_ACCESS 0x3002 +#define EGL_BAD_ALLOC 0x3003 +#define EGL_BAD_ATTRIBUTE 0x3004 +#define EGL_BAD_CONFIG 0x3005 +#define EGL_BAD_CONTEXT 0x3006 +#define EGL_BAD_CURRENT_SURFACE 0x3007 +#define EGL_BAD_DISPLAY 0x3008 +#define EGL_BAD_MATCH 0x3009 +#define EGL_BAD_NATIVE_PIXMAP 0x300A +#define EGL_BAD_NATIVE_WINDOW 0x300B +#define EGL_BAD_PARAMETER 0x300C +#define EGL_BAD_SURFACE 0x300D +#define EGL_CONTEXT_LOST 0x300E /* EGL 1.1 - IMG_power_management */ + +/* Reserved 0x300F-0x301F for additional errors */ + +/* Config attributes */ +#define EGL_BUFFER_SIZE 0x3020 +#define EGL_ALPHA_SIZE 0x3021 +#define EGL_BLUE_SIZE 0x3022 +#define EGL_GREEN_SIZE 0x3023 +#define EGL_RED_SIZE 0x3024 +#define EGL_DEPTH_SIZE 0x3025 +#define EGL_STENCIL_SIZE 0x3026 +#define EGL_CONFIG_CAVEAT 0x3027 +#define EGL_CONFIG_ID 0x3028 +#define EGL_LEVEL 0x3029 +#define EGL_MAX_PBUFFER_HEIGHT 0x302A +#define EGL_MAX_PBUFFER_PIXELS 0x302B +#define EGL_MAX_PBUFFER_WIDTH 0x302C +#define EGL_NATIVE_RENDERABLE 0x302D +#define EGL_NATIVE_VISUAL_ID 0x302E +#define EGL_NATIVE_VISUAL_TYPE 0x302F +#define EGL_PRESERVED_RESOURCES 0x3030 +#define EGL_SAMPLES 0x3031 +#define EGL_SAMPLE_BUFFERS 0x3032 +#define EGL_SURFACE_TYPE 0x3033 +#define EGL_TRANSPARENT_TYPE 0x3034 +#define EGL_TRANSPARENT_BLUE_VALUE 0x3035 +#define EGL_TRANSPARENT_GREEN_VALUE 0x3036 +#define EGL_TRANSPARENT_RED_VALUE 0x3037 +#define EGL_NONE 0x3038 /* Attrib list terminator */ +#define EGL_BIND_TO_TEXTURE_RGB 0x3039 +#define EGL_BIND_TO_TEXTURE_RGBA 0x303A +#define EGL_MIN_SWAP_INTERVAL 0x303B +#define EGL_MAX_SWAP_INTERVAL 0x303C +#define EGL_LUMINANCE_SIZE 0x303D +#define EGL_ALPHA_MASK_SIZE 0x303E +#define EGL_COLOR_BUFFER_TYPE 0x303F +#define EGL_RENDERABLE_TYPE 0x3040 +#define EGL_MATCH_NATIVE_PIXMAP 0x3041 /* Pseudo-attribute (not queryable) */ +#define EGL_CONFORMANT 0x3042 + +/* Reserved 0x3041-0x304F for additional config attributes */ + +/* Config attribute values */ +#define EGL_SLOW_CONFIG 0x3050 /* EGL_CONFIG_CAVEAT value */ +#define EGL_NON_CONFORMANT_CONFIG 0x3051 /* EGL_CONFIG_CAVEAT value */ +#define EGL_TRANSPARENT_RGB 0x3052 /* EGL_TRANSPARENT_TYPE value */ +#define EGL_RGB_BUFFER 0x308E /* EGL_COLOR_BUFFER_TYPE value */ +#define EGL_LUMINANCE_BUFFER 0x308F /* EGL_COLOR_BUFFER_TYPE value */ + +/* More config attribute values, for EGL_TEXTURE_FORMAT */ +#define EGL_NO_TEXTURE 0x305C +#define EGL_TEXTURE_RGB 0x305D +#define EGL_TEXTURE_RGBA 0x305E +#define EGL_TEXTURE_2D 0x305F + +/* Config attribute mask bits */ +#define EGL_PBUFFER_BIT 0x0001 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_PIXMAP_BIT 0x0002 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_WINDOW_BIT 0x0004 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_VG_COLORSPACE_LINEAR_BIT 0x0020 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_VG_ALPHA_FORMAT_PRE_BIT 0x0040 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_MULTISAMPLE_RESOLVE_BOX_BIT 0x0200 /* EGL_SURFACE_TYPE mask bits */ +#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400 /* EGL_SURFACE_TYPE mask bits */ + +#define EGL_OPENGL_ES_BIT 0x0001 /* EGL_RENDERABLE_TYPE mask bits */ +#define EGL_OPENVG_BIT 0x0002 /* EGL_RENDERABLE_TYPE mask bits */ +#define EGL_OPENGL_ES2_BIT 0x0004 /* EGL_RENDERABLE_TYPE mask bits */ +#define EGL_OPENGL_BIT 0x0008 /* EGL_RENDERABLE_TYPE mask bits */ + +/* QueryString targets */ +#define EGL_VENDOR 0x3053 +#define EGL_VERSION 0x3054 +#define EGL_EXTENSIONS 0x3055 +#define EGL_CLIENT_APIS 0x308D + +/* QuerySurface / SurfaceAttrib / CreatePbufferSurface targets */ +#define EGL_HEIGHT 0x3056 +#define EGL_WIDTH 0x3057 +#define EGL_LARGEST_PBUFFER 0x3058 +#define EGL_TEXTURE_FORMAT 0x3080 +#define EGL_TEXTURE_TARGET 0x3081 +#define EGL_MIPMAP_TEXTURE 0x3082 +#define EGL_MIPMAP_LEVEL 0x3083 +#define EGL_RENDER_BUFFER 0x3086 +#define EGL_VG_COLORSPACE 0x3087 +#define EGL_VG_ALPHA_FORMAT 0x3088 +#define EGL_HORIZONTAL_RESOLUTION 0x3090 +#define EGL_VERTICAL_RESOLUTION 0x3091 +#define EGL_PIXEL_ASPECT_RATIO 0x3092 +#define EGL_SWAP_BEHAVIOR 0x3093 +#define EGL_MULTISAMPLE_RESOLVE 0x3099 + +/* EGL_RENDER_BUFFER values / BindTexImage / ReleaseTexImage buffer targets */ +#define EGL_BACK_BUFFER 0x3084 +#define EGL_SINGLE_BUFFER 0x3085 + +/* OpenVG color spaces */ +#define EGL_VG_COLORSPACE_sRGB 0x3089 /* EGL_VG_COLORSPACE value */ +#define EGL_VG_COLORSPACE_LINEAR 0x308A /* EGL_VG_COLORSPACE value */ + +/* OpenVG alpha formats */ +#define EGL_VG_ALPHA_FORMAT_NONPRE 0x308B /* EGL_ALPHA_FORMAT value */ +#define EGL_VG_ALPHA_FORMAT_PRE 0x308C /* EGL_ALPHA_FORMAT value */ + +/* Constant scale factor by which fractional display resolutions & + * aspect ratio are scaled when queried as integer values. + */ +#define EGL_DISPLAY_SCALING 10000 + +/* Unknown display resolution/aspect ratio */ +#define EGL_UNKNOWN ((EGLint)-1) + +/* Back buffer swap behaviors */ +#define EGL_BUFFER_PRESERVED 0x3094 /* EGL_SWAP_BEHAVIOR value */ +#define EGL_BUFFER_DESTROYED 0x3095 /* EGL_SWAP_BEHAVIOR value */ + +/* CreatePbufferFromClientBuffer buffer types */ +#define EGL_OPENVG_IMAGE 0x3096 + +/* QueryContext targets */ +#define EGL_CONTEXT_CLIENT_TYPE 0x3097 + +/* CreateContext attributes */ +#define EGL_CONTEXT_CLIENT_VERSION 0x3098 + +/* Multisample resolution behaviors */ +#define EGL_MULTISAMPLE_RESOLVE_DEFAULT 0x309A /* EGL_MULTISAMPLE_RESOLVE value */ +#define EGL_MULTISAMPLE_RESOLVE_BOX 0x309B /* EGL_MULTISAMPLE_RESOLVE value */ + +/* BindAPI/QueryAPI targets */ +#define EGL_OPENGL_ES_API 0x30A0 +#define EGL_OPENVG_API 0x30A1 +#define EGL_OPENGL_API 0x30A2 + +/* GetCurrentSurface targets */ +#define EGL_DRAW 0x3059 +#define EGL_READ 0x305A + +/* WaitNative engines */ +#define EGL_CORE_NATIVE_ENGINE 0x305B + +/* EGL 1.2 tokens renamed for consistency in EGL 1.3 */ +#define EGL_COLORSPACE EGL_VG_COLORSPACE +#define EGL_ALPHA_FORMAT EGL_VG_ALPHA_FORMAT +#define EGL_COLORSPACE_sRGB EGL_VG_COLORSPACE_sRGB +#define EGL_COLORSPACE_LINEAR EGL_VG_COLORSPACE_LINEAR +#define EGL_ALPHA_FORMAT_NONPRE EGL_VG_ALPHA_FORMAT_NONPRE +#define EGL_ALPHA_FORMAT_PRE EGL_VG_ALPHA_FORMAT_PRE + +/* EGL extensions must request enum blocks from the Khronos + * API Registrar, who maintains the enumerant registry. Submit + * a bug in Khronos Bugzilla against task "Registry". + */ + + + +/* EGL Functions */ + +EGLAPI EGLint EGLAPIENTRY eglGetError(void); + +EGLAPI EGLDisplay EGLAPIENTRY eglGetDisplay(EGLNativeDisplayType display_id); +EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor); +EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy); + +EGLAPI const char * EGLAPIENTRY eglQueryString(EGLDisplay dpy, EGLint name); + +EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, + EGLint config_size, EGLint *num_config); +EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, + EGLConfig *configs, EGLint config_size, + EGLint *num_config); +EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, + EGLint attribute, EGLint *value); + +EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, + EGLNativeWindowType win, + const EGLint *attrib_list); +EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, + const EGLint *attrib_list); +EGLAPI EGLSurface EGLAPIENTRY eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, + EGLNativePixmapType pixmap, + const EGLint *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay dpy, EGLSurface surface); +EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface(EGLDisplay dpy, EGLSurface surface, + EGLint attribute, EGLint *value); + +EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api); +EGLAPI EGLenum EGLAPIENTRY eglQueryAPI(void); + +EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient(void); + +EGLAPI EGLBoolean EGLAPIENTRY eglReleaseThread(void); + +EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferFromClientBuffer( + EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, + EGLConfig config, const EGLint *attrib_list); + +EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, + EGLint attribute, EGLint value); +EGLAPI EGLBoolean EGLAPIENTRY eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); +EGLAPI EGLBoolean EGLAPIENTRY eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); + + +EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval(EGLDisplay dpy, EGLint interval); + + +EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, + EGLContext share_context, + const EGLint *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay dpy, EGLContext ctx); +EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, + EGLSurface read, EGLContext ctx); + +EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext(void); +EGLAPI EGLSurface EGLAPIENTRY eglGetCurrentSurface(EGLint readdraw); +EGLAPI EGLDisplay EGLAPIENTRY eglGetCurrentDisplay(void); +EGLAPI EGLBoolean EGLAPIENTRY eglQueryContext(EGLDisplay dpy, EGLContext ctx, + EGLint attribute, EGLint *value); + +EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL(void); +EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative(EGLint engine); +EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers(EGLDisplay dpy, EGLSurface surface); +EGLAPI EGLBoolean EGLAPIENTRY eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, + EGLNativePixmapType target); + +/* This is a generic function pointer type, whose name indicates it must + * be cast to the proper type *and calling convention* before use. + */ +typedef void (*__eglMustCastToProperFunctionPointerType)(void); + +/* Now, define eglGetProcAddress using the generic function ptr. type */ +EGLAPI __eglMustCastToProperFunctionPointerType EGLAPIENTRY + eglGetProcAddress(const char *procname); + +#ifdef __cplusplus +} +#endif + +#endif /* __egl_h_ */ diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h new file mode 100644 index 000000000..25cfcb832 --- /dev/null +++ b/opengl/include/EGL/eglext.h @@ -0,0 +1,138 @@ +#ifndef __eglext_h_ +#define __eglext_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2007-2009 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#include + +/*************************************************************/ + +/* Header file version number */ +/* Current version at http://www.khronos.org/registry/egl/ */ +/* $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ */ +#define EGL_EGLEXT_VERSION 3 + +#ifndef EGL_KHR_config_attribs +#define EGL_KHR_config_attribs 1 +#define EGL_CONFORMANT_KHR 0x3042 /* EGLConfig attribute */ +#define EGL_VG_COLORSPACE_LINEAR_BIT_KHR 0x0020 /* EGL_SURFACE_TYPE bitfield */ +#define EGL_VG_ALPHA_FORMAT_PRE_BIT_KHR 0x0040 /* EGL_SURFACE_TYPE bitfield */ +#endif + +#ifndef EGL_KHR_lock_surface +#define EGL_KHR_lock_surface 1 +#define EGL_READ_SURFACE_BIT_KHR 0x0001 /* EGL_LOCK_USAGE_HINT_KHR bitfield */ +#define EGL_WRITE_SURFACE_BIT_KHR 0x0002 /* EGL_LOCK_USAGE_HINT_KHR bitfield */ +#define EGL_LOCK_SURFACE_BIT_KHR 0x0080 /* EGL_SURFACE_TYPE bitfield */ +#define EGL_OPTIMAL_FORMAT_BIT_KHR 0x0100 /* EGL_SURFACE_TYPE bitfield */ +#define EGL_MATCH_FORMAT_KHR 0x3043 /* EGLConfig attribute */ +#define EGL_FORMAT_RGB_565_EXACT_KHR 0x30C0 /* EGL_MATCH_FORMAT_KHR value */ +#define EGL_FORMAT_RGB_565_KHR 0x30C1 /* EGL_MATCH_FORMAT_KHR value */ +#define EGL_FORMAT_RGBA_8888_EXACT_KHR 0x30C2 /* EGL_MATCH_FORMAT_KHR value */ +#define EGL_FORMAT_RGBA_8888_KHR 0x30C3 /* EGL_MATCH_FORMAT_KHR value */ +#define EGL_MAP_PRESERVE_PIXELS_KHR 0x30C4 /* eglLockSurfaceKHR attribute */ +#define EGL_LOCK_USAGE_HINT_KHR 0x30C5 /* eglLockSurfaceKHR attribute */ +#define EGL_BITMAP_POINTER_KHR 0x30C6 /* eglQuerySurface attribute */ +#define EGL_BITMAP_PITCH_KHR 0x30C7 /* eglQuerySurface attribute */ +#define EGL_BITMAP_ORIGIN_KHR 0x30C8 /* eglQuerySurface attribute */ +#define EGL_BITMAP_PIXEL_RED_OFFSET_KHR 0x30C9 /* eglQuerySurface attribute */ +#define EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR 0x30CA /* eglQuerySurface attribute */ +#define EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR 0x30CB /* eglQuerySurface attribute */ +#define EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR 0x30CC /* eglQuerySurface attribute */ +#define EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR 0x30CD /* eglQuerySurface attribute */ +#define EGL_LOWER_LEFT_KHR 0x30CE /* EGL_BITMAP_ORIGIN_KHR value */ +#define EGL_UPPER_LEFT_KHR 0x30CF /* EGL_BITMAP_ORIGIN_KHR value */ +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglLockSurfaceKHR (EGLDisplay display, EGLSurface surface, const EGLint *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglUnlockSurfaceKHR (EGLDisplay display, EGLSurface surface); +#endif /* EGL_EGLEXT_PROTOTYPES */ +typedef EGLBoolean (EGLAPIENTRYP PFNEGLLOCKSURFACEKHRPROC) (EGLDisplay display, EGLSurface surface, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNLOCKSURFACEKHRPROC) (EGLDisplay display, EGLSurface surface); +#endif + +#ifndef EGL_KHR_image +#define EGL_KHR_image 1 +#define EGL_NATIVE_PIXMAP_KHR 0x30B0 /* eglCreateImageKHR target */ +typedef void *EGLImageKHR; +#define EGL_NO_IMAGE_KHR ((EGLImageKHR)0) +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImageKHR (EGLDisplay dpy, EGLImageKHR image); +#endif /* EGL_EGLEXT_PROTOTYPES */ +typedef EGLImageKHR (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImageKHR image); +#endif + +#ifndef EGL_KHR_vg_parent_image +#define EGL_KHR_vg_parent_image 1 +#define EGL_VG_PARENT_IMAGE_KHR 0x30BA /* eglCreateImageKHR target */ +#endif + +#ifndef EGL_KHR_gl_texture_2D_image +#define EGL_KHR_gl_texture_2D_image 1 +#define EGL_GL_TEXTURE_2D_KHR 0x30B1 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_LEVEL_KHR 0x30BC /* eglCreateImageKHR attribute */ +#endif + +#ifndef EGL_KHR_gl_texture_cubemap_image +#define EGL_KHR_gl_texture_cubemap_image 1 +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR 0x30B3 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR 0x30B4 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR 0x30B5 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR 0x30B6 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR 0x30B7 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR 0x30B8 /* eglCreateImageKHR target */ +#endif + +#ifndef EGL_KHR_gl_texture_3D_image +#define EGL_KHR_gl_texture_3D_image 1 +#define EGL_GL_TEXTURE_3D_KHR 0x30B2 /* eglCreateImageKHR target */ +#define EGL_GL_TEXTURE_ZOFFSET_KHR 0x30BD /* eglCreateImageKHR attribute */ +#endif + +#ifndef EGL_KHR_gl_renderbuffer_image +#define EGL_KHR_gl_renderbuffer_image 1 +#define EGL_GL_RENDERBUFFER_KHR 0x30B9 /* eglCreateImageKHR target */ +#endif + +#ifndef EGL_KHR_image_base +#define EGL_KHR_image_base 1 +/* Most interfaces defined by EGL_KHR_image_pixmap above */ +#define EGL_IMAGE_PRESERVED_KHR 0x30D2 /* eglCreateImageKHR attribute */ +#endif + +#ifndef EGL_KHR_image_pixmap +#define EGL_KHR_image_pixmap 1 +/* Interfaces defined by EGL_KHR_image above */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/opengl/include/EGL/eglnatives.h b/opengl/include/EGL/eglnatives.h new file mode 100644 index 000000000..21622dc5a --- /dev/null +++ b/opengl/include/EGL/eglnatives.h @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_EGLNATIVES_H +#define ANDROID_EGLNATIVES_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +/* flags returned from swapBuffer */ +#define EGL_NATIVES_FLAG_SIZE_CHANGED 0x00000001 + +/* surface flags */ +#define EGL_NATIVES_FLAG_DESTROY_BACKBUFFER 0x00000001 + +enum native_pixel_format_t +{ + NATIVE_PIXEL_FORMAT_RGBA_8888 = 1, + NATIVE_PIXEL_FORMAT_RGB_565 = 4, + NATIVE_PIXEL_FORMAT_BGRA_8888 = 5, + NATIVE_PIXEL_FORMAT_RGBA_5551 = 6, + NATIVE_PIXEL_FORMAT_RGBA_4444 = 7, + NATIVE_PIXEL_FORMAT_YCbCr_422_SP= 0x10, + NATIVE_PIXEL_FORMAT_YCbCr_420_SP= 0x11, +}; + +enum native_memory_type_t +{ + NATIVE_MEMORY_TYPE_PMEM = 0, + NATIVE_MEMORY_TYPE_GPU = 1, + NATIVE_MEMORY_TYPE_FB = 2, + NATIVE_MEMORY_TYPE_HEAP = 128 +}; + + +struct egl_native_window_t +{ + /* + * magic must be set to 0x600913 + */ + uint32_t magic; + + /* + * must be sizeof(egl_native_window_t) + */ + uint32_t version; + + /* + * ident is reserved for the Android platform + */ + uint32_t ident; + + /* + * width, height and stride of the window in pixels + * Any of these value can be nul in which case GL commands are + * accepted and processed as usual, but not rendering occurs. + */ + int width; // w=h=0 is legal + int height; + int stride; + + /* + * format of the native window (see ui/PixelFormat.h) + */ + int format; + + /* + * Offset of the bits in the VRAM + */ + intptr_t offset; + + /* + * flags describing some attributes of this surface + * EGL_NATIVES_FLAG_DESTROY_BACKBUFFER: backbuffer not preserved after + * eglSwapBuffers + */ + uint32_t flags; + + /* + * horizontal and vertical resolution in DPI + */ + float xdpi; + float ydpi; + + /* + * refresh rate in frames per second (Hz) + */ + float fps; + + + /* + * Base memory virtual address of the surface in the CPU side + */ + intptr_t base; + + /* + * Heap the offset above is based from + */ + int fd; + + /* + * Memory type the surface resides into + */ + uint8_t memory_type; + + /* + * Reserved for future use. MUST BE ZERO. + */ + uint8_t reserved_pad[3]; + int reserved[8]; + + /* + * Vertical stride (only relevant with planar formats) + */ + + int vstride; + + /* + * Hook called by EGL to hold a reference on this structure + */ + void (*incRef)(struct egl_native_window_t* window); + + /* + * Hook called by EGL to release a reference on this structure + */ + void (*decRef)(struct egl_native_window_t* window); + + /* + * Hook called by EGL to perform a page flip. This function + * may update the size attributes above, in which case it returns + * the EGL_NATIVES_FLAG_SIZE_CHANGED bit set. + */ + uint32_t (*swapBuffers)(struct egl_native_window_t* window); + + /* + * Reserved for future use. MUST BE ZERO. + */ + void (*reserved_proc_0)(void); + + /* + * Reserved for future use. MUST BE ZERO. + */ + void (*reserved_proc_1)(void); + + /* + * Reserved for future use. MUST BE ZERO. + */ + void (*reserved_proc_2)(void); + + + /* + * Hook called by EGL when the native surface is associated to EGL + * (eglCreateWindowSurface). Can be NULL. + */ + void (*connect)(struct egl_native_window_t* window); + + /* + * Hook called by EGL when eglDestroySurface is called. Can be NULL. + */ + void (*disconnect)(struct egl_native_window_t* window); + + /* + * Reserved for future use. MUST BE ZERO. + */ + void (*reserved_proc[11])(void); + + /* + * Some storage reserved for the oem driver. + */ + intptr_t oem[4]; +}; + + +struct egl_native_pixmap_t +{ + int32_t version; /* must be 32 */ + int32_t width; + int32_t height; + int32_t stride; + uint8_t* data; + uint8_t format; + uint8_t rfu[3]; + union { + uint32_t compressedFormat; + int32_t vstride; + }; + int32_t reserved; +}; + +/*****************************************************************************/ + +/* + * This a convenience function to create a NativeWindowType surface + * that maps to the whole screen + * This function is actually implemented in libui.so + */ + +struct egl_native_window_t* android_createDisplaySurface(); + +/*****************************************************************************/ + + +/* + * OEM's egl's library (libhgl.so) must imlement these hooks to allocate + * the GPU memory they need + */ + + +typedef struct +{ + // for internal use + void* user; + // virtual address of this area + void* base; + // size of this area in bytes + size_t size; + // physical address of this area + void* phys; + // offset in this area available to the GPU + size_t offset; + // fd of this area + int fd; +} gpu_area_t; + +typedef struct +{ + // area where GPU registers are mapped + gpu_area_t regs; + // number of extra areas (currently limited to 2) + int32_t count; + // extra GPU areas (currently limited to 2) + gpu_area_t gpu[2]; +} request_gpu_t; + + +typedef request_gpu_t* (*OEM_EGL_acquire_gpu_t)(void* user); +typedef int (*OEM_EGL_release_gpu_t)(void* user, request_gpu_t* handle); +typedef void (*register_gpu_t) + (void* user, OEM_EGL_acquire_gpu_t, OEM_EGL_release_gpu_t); + +void oem_register_gpu( + void* user, + OEM_EGL_acquire_gpu_t acquire, + OEM_EGL_release_gpu_t release); + + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* ANDROID_EGLNATIVES_H */ diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h new file mode 100644 index 000000000..ac0090102 --- /dev/null +++ b/opengl/include/EGL/eglplatform.h @@ -0,0 +1,117 @@ +#ifndef __eglplatform_h_ +#define __eglplatform_h_ + +/* +** Copyright (c) 2007-2009 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Platform-specific types and definitions for egl.h + * $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ + * + * Adopters may modify khrplatform.h and this file to suit their platform. + * You are encouraged to submit all modifications to the Khronos group so that + * they can be included in future versions of this file. Please submit changes + * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla) + * by filing a bug against product "EGL" component "Registry". + */ + +#include + +/* Macros used in EGL function prototype declarations. + * + * EGL functions should be prototyped as: + * + * EGLAPI return-type EGLAPIENTRY eglFunction(arguments); + * typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments); + * + * KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h + */ + +#ifndef EGLAPI +#define EGLAPI KHRONOS_APICALL +#endif + +#define EGLAPIENTRY KHRONOS_APIENTRY +#define EGLAPIENTRYP KHRONOS_APIENTRY* + +/* The types NativeDisplayType, NativeWindowType, and NativePixmapType + * are aliases of window-system-dependent types, such as X Display * or + * Windows Device Context. They must be defined in platform-specific + * code below. The EGL-prefixed versions of Native*Type are the same + * types, renamed in EGL 1.3 so all types in the API start with "EGL". + */ + +#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include + +typedef HDC EGLNativeDisplayType; +typedef HBITMAP EGLNativePixmapType; +typedef HWND EGLNativeWindowType; + +#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */ + +typedef int EGLNativeDisplayType; +typedef void *EGLNativeWindowType; +typedef void *EGLNativePixmapType; + +#elif defined(__unix__) && !defined(ANDROID) + +/* X11 (tentative) */ +#include +#include + +typedef Display *EGLNativeDisplayType; +typedef Pixmap EGLNativePixmapType; +typedef Window EGLNativeWindowType; + + +#elif defined(ANDROID) + +#include + +typedef struct egl_native_window_t* EGLNativeWindowType; +typedef struct egl_native_pixmap_t* EGLNativePixmapType; +typedef void* EGLNativeDisplayType; + +#else +#error "Platform not recognized" +#endif + +/* EGL 1.2 types, renamed for consistency in EGL 1.3 */ +typedef EGLNativeDisplayType NativeDisplayType; +typedef EGLNativePixmapType NativePixmapType; +typedef EGLNativeWindowType NativeWindowType; + + +/* Define EGLint. This must be a signed integral type large enough to contain + * all legal attribute names and values passed into and out of EGL, whether + * their type is boolean, bitmask, enumerant (symbolic constant), integer, + * handle, or other. While in general a 32-bit integer will suffice, if + * handles are 64 bit types, then EGLint should be defined as a signed 64-bit + * integer type. + */ +typedef khronos_int32_t EGLint; + +#endif /* __eglplatform_h */ diff --git a/opengl/include/GLES/egl.h b/opengl/include/GLES/egl.h new file mode 100644 index 000000000..5778e0050 --- /dev/null +++ b/opengl/include/GLES/egl.h @@ -0,0 +1,15 @@ +/* + * Skeleton egl.h to provide compatibility for early GLES 1.0 + * applications. Several early implementations included gl.h + * in egl.h leading applications to include only egl.h + * + * $Revision: 6252 $ on $Date:: 2008-08-06 16:35:08 -0700 #$ + */ + +#ifndef __legacy_egl_h_ +#define __legacy_egl_h_ + +#include +#include + +#endif /* __legacy_egl_h_ */ diff --git a/opengl/include/GLES/gl.h b/opengl/include/GLES/gl.h new file mode 100644 index 000000000..2e8b97107 --- /dev/null +++ b/opengl/include/GLES/gl.h @@ -0,0 +1,769 @@ +#ifndef __gl_h_ +#define __gl_h_ + +/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +typedef void GLvoid; +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef khronos_int8_t GLbyte; +typedef short GLshort; +typedef int GLint; +typedef int GLsizei; +typedef khronos_uint8_t GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef khronos_float_t GLfloat; +typedef khronos_float_t GLclampf; +typedef khronos_int32_t GLfixed; +typedef khronos_int32_t GLclampx; + +typedef khronos_intptr_t GLintptr; +typedef khronos_ssize_t GLsizeiptr; + + +/*************************************************************/ + +/* OpenGL ES core versions */ +#define GL_VERSION_ES_CM_1_0 1 +#define GL_VERSION_ES_CL_1_0 1 +#define GL_VERSION_ES_CM_1_1 1 +#define GL_VERSION_ES_CL_1_1 1 + +/* ClearBufferMask */ +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 + +/* Boolean */ +#define GL_FALSE 0 +#define GL_TRUE 1 + +/* BeginMode */ +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 + +/* AlphaFunction */ +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 + +/* BlendingFactorDest */ +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 + +/* BlendingFactorSrc */ +/* GL_ZERO */ +/* GL_ONE */ +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +/* GL_SRC_ALPHA */ +/* GL_ONE_MINUS_SRC_ALPHA */ +/* GL_DST_ALPHA */ +/* GL_ONE_MINUS_DST_ALPHA */ + +/* ClipPlaneName */ +#define GL_CLIP_PLANE0 0x3000 +#define GL_CLIP_PLANE1 0x3001 +#define GL_CLIP_PLANE2 0x3002 +#define GL_CLIP_PLANE3 0x3003 +#define GL_CLIP_PLANE4 0x3004 +#define GL_CLIP_PLANE5 0x3005 + +/* ColorMaterialFace */ +/* GL_FRONT_AND_BACK */ + +/* ColorMaterialParameter */ +/* GL_AMBIENT_AND_DIFFUSE */ + +/* ColorPointerType */ +/* GL_UNSIGNED_BYTE */ +/* GL_FLOAT */ +/* GL_FIXED */ + +/* CullFaceMode */ +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_FRONT_AND_BACK 0x0408 + +/* DepthFunction */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* EnableCap */ +#define GL_FOG 0x0B60 +#define GL_LIGHTING 0x0B50 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_CULL_FACE 0x0B44 +#define GL_ALPHA_TEST 0x0BC0 +#define GL_BLEND 0x0BE2 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_DITHER 0x0BD0 +#define GL_STENCIL_TEST 0x0B90 +#define GL_DEPTH_TEST 0x0B71 +/* GL_LIGHT0 */ +/* GL_LIGHT1 */ +/* GL_LIGHT2 */ +/* GL_LIGHT3 */ +/* GL_LIGHT4 */ +/* GL_LIGHT5 */ +/* GL_LIGHT6 */ +/* GL_LIGHT7 */ +#define GL_POINT_SMOOTH 0x0B10 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_MATERIAL 0x0B57 +#define GL_NORMALIZE 0x0BA1 +#define GL_RESCALE_NORMAL 0x803A +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_COLOR_ARRAY 0x8076 +#define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 + +/* ErrorCode */ +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 + +/* FogMode */ +/* GL_LINEAR */ +#define GL_EXP 0x0800 +#define GL_EXP2 0x0801 + +/* FogParameter */ +#define GL_FOG_DENSITY 0x0B62 +#define GL_FOG_START 0x0B63 +#define GL_FOG_END 0x0B64 +#define GL_FOG_MODE 0x0B65 +#define GL_FOG_COLOR 0x0B66 + +/* FrontFaceDirection */ +#define GL_CW 0x0900 +#define GL_CCW 0x0901 + +/* GetPName */ +#define GL_CURRENT_COLOR 0x0B00 +#define GL_CURRENT_NORMAL 0x0B02 +#define GL_CURRENT_TEXTURE_COORDS 0x0B03 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_LINE_WIDTH 0x0B21 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_SHADE_MODEL 0x0B54 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_MATRIX_MODE 0x0BA0 +#define GL_VIEWPORT 0x0BA2 +#define GL_MODELVIEW_STACK_DEPTH 0x0BA3 +#define GL_PROJECTION_STACK_DEPTH 0x0BA4 +#define GL_TEXTURE_STACK_DEPTH 0x0BA5 +#define GL_MODELVIEW_MATRIX 0x0BA6 +#define GL_PROJECTION_MATRIX 0x0BA7 +#define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_ALPHA_TEST_FUNC 0x0BC1 +#define GL_ALPHA_TEST_REF 0x0BC2 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_SRC 0x0BE1 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_LIGHTS 0x0D31 +#define GL_MAX_CLIP_PLANES 0x0D32 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 +#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 +#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_RED_BITS 0x0D52 +#define GL_GREEN_BITS 0x0D53 +#define GL_BLUE_BITS 0x0D54 +#define GL_ALPHA_BITS 0x0D55 +#define GL_DEPTH_BITS 0x0D56 +#define GL_STENCIL_BITS 0x0D57 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_VERTEX_ARRAY_SIZE 0x807A +#define GL_VERTEX_ARRAY_TYPE 0x807B +#define GL_VERTEX_ARRAY_STRIDE 0x807C +#define GL_NORMAL_ARRAY_TYPE 0x807E +#define GL_NORMAL_ARRAY_STRIDE 0x807F +#define GL_COLOR_ARRAY_SIZE 0x8081 +#define GL_COLOR_ARRAY_TYPE 0x8082 +#define GL_COLOR_ARRAY_STRIDE 0x8083 +#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A +#define GL_VERTEX_ARRAY_POINTER 0x808E +#define GL_NORMAL_ARRAY_POINTER 0x808F +#define GL_COLOR_ARRAY_POINTER 0x8090 +#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB + +/* GetTextureParameter */ +/* GL_TEXTURE_MAG_FILTER */ +/* GL_TEXTURE_MIN_FILTER */ +/* GL_TEXTURE_WRAP_S */ +/* GL_TEXTURE_WRAP_T */ + +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 + +/* HintMode */ +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 + +/* HintTarget */ +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_FOG_HINT 0x0C54 +#define GL_GENERATE_MIPMAP_HINT 0x8192 + +/* LightModelParameter */ +#define GL_LIGHT_MODEL_AMBIENT 0x0B53 +#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 + +/* LightParameter */ +#define GL_AMBIENT 0x1200 +#define GL_DIFFUSE 0x1201 +#define GL_SPECULAR 0x1202 +#define GL_POSITION 0x1203 +#define GL_SPOT_DIRECTION 0x1204 +#define GL_SPOT_EXPONENT 0x1205 +#define GL_SPOT_CUTOFF 0x1206 +#define GL_CONSTANT_ATTENUATION 0x1207 +#define GL_LINEAR_ATTENUATION 0x1208 +#define GL_QUADRATIC_ATTENUATION 0x1209 + +/* DataType */ +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_FLOAT 0x1406 +#define GL_FIXED 0x140C + +/* LogicOp */ +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F + +/* MaterialFace */ +/* GL_FRONT_AND_BACK */ + +/* MaterialParameter */ +#define GL_EMISSION 0x1600 +#define GL_SHININESS 0x1601 +#define GL_AMBIENT_AND_DIFFUSE 0x1602 +/* GL_AMBIENT */ +/* GL_DIFFUSE */ +/* GL_SPECULAR */ + +/* MatrixMode */ +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 + +/* NormalPointerType */ +/* GL_BYTE */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ + +/* PixelFormat */ +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A + +/* PixelStoreParameter */ +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 + +/* PixelType */ +/* GL_UNSIGNED_BYTE */ +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 + +/* ShadingModel */ +#define GL_FLAT 0x1D00 +#define GL_SMOOTH 0x1D01 + +/* StencilFunction */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* StencilOp */ +/* GL_ZERO */ +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +/* GL_INVERT */ + +/* StringName */ +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +/* TexCoordPointerType */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ +/* GL_BYTE */ + +/* TextureEnvMode */ +#define GL_MODULATE 0x2100 +#define GL_DECAL 0x2101 +/* GL_BLEND */ +#define GL_ADD 0x0104 +/* GL_REPLACE */ + +/* TextureEnvParameter */ +#define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_ENV_COLOR 0x2201 + +/* TextureEnvTarget */ +#define GL_TEXTURE_ENV 0x2300 + +/* TextureMagFilter */ +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 + +/* TextureMinFilter */ +/* GL_NEAREST */ +/* GL_LINEAR */ +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 + +/* TextureParameterName */ +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_GENERATE_MIPMAP 0x8191 + +/* TextureTarget */ +/* GL_TEXTURE_2D */ + +/* TextureUnit */ +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 + +/* TextureWrapMode */ +#define GL_REPEAT 0x2901 +#define GL_CLAMP_TO_EDGE 0x812F + +/* VertexPointerType */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ +/* GL_BYTE */ + +/* LightName */ +#define GL_LIGHT0 0x4000 +#define GL_LIGHT1 0x4001 +#define GL_LIGHT2 0x4002 +#define GL_LIGHT3 0x4003 +#define GL_LIGHT4 0x4004 +#define GL_LIGHT5 0x4005 +#define GL_LIGHT6 0x4006 +#define GL_LIGHT7 0x4007 + +/* Buffer Objects */ +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 + +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A + +#define GL_STATIC_DRAW 0x88E4 +#define GL_DYNAMIC_DRAW 0x88E8 + +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 + +/* Texture combine + dot3 */ +#define GL_SUBTRACT 0x84E7 +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A + +#define GL_ALPHA_SCALE 0x0D1C + +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC1_ALPHA 0x8589 +#define GL_SRC2_ALPHA 0x858A + +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF + +/*------------------------------------------------------------------------* + * required OES extension tokens + *------------------------------------------------------------------------*/ + +/* OES_read_format */ +#ifndef GL_OES_read_format +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif + +/* GL_OES_compressed_paletted_texture */ +#ifndef GL_OES_compressed_paletted_texture +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif + +/* OES_point_size_array */ +#ifndef GL_OES_point_size_array +#define GL_POINT_SIZE_ARRAY_OES 0x8B9C +#define GL_POINT_SIZE_ARRAY_TYPE_OES 0x898A +#define GL_POINT_SIZE_ARRAY_STRIDE_OES 0x898B +#define GL_POINT_SIZE_ARRAY_POINTER_OES 0x898C +#define GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES 0x8B9F +#endif + +/* GL_OES_point_sprite */ +#ifndef GL_OES_point_sprite +#define GL_POINT_SPRITE_OES 0x8861 +#define GL_COORD_REPLACE_OES 0x8862 +#endif + +/*************************************************************/ + +/* Available only in Common profile */ +GL_API void GL_APIENTRY glAlphaFunc (GLenum func, GLclampf ref); +GL_API void GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GL_API void GL_APIENTRY glClearDepthf (GLclampf depth); +GL_API void GL_APIENTRY glClipPlanef (GLenum plane, const GLfloat *equation); +GL_API void GL_APIENTRY glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GL_API void GL_APIENTRY glDepthRangef (GLclampf zNear, GLclampf zFar); +GL_API void GL_APIENTRY glFogf (GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glFogfv (GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glFrustumf (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glGetClipPlanef (GLenum pname, GLfloat eqn[4]); +GL_API void GL_APIENTRY glGetFloatv (GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetLightfv (GLenum light, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetMaterialfv (GLenum face, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetTexEnvfv (GLenum env, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glLightModelf (GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glLightModelfv (GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glLightf (GLenum light, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glLightfv (GLenum light, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glLineWidth (GLfloat width); +GL_API void GL_APIENTRY glLoadMatrixf (const GLfloat *m); +GL_API void GL_APIENTRY glMaterialf (GLenum face, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glMaterialfv (GLenum face, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glMultMatrixf (const GLfloat *m); +GL_API void GL_APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GL_API void GL_APIENTRY glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz); +GL_API void GL_APIENTRY glOrthof (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glPointSize (GLfloat size); +GL_API void GL_APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); +GL_API void GL_APIENTRY glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GL_API void GL_APIENTRY glScalef (GLfloat x, GLfloat y, GLfloat z); +GL_API void GL_APIENTRY glTexEnvf (GLenum target, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glTexEnvfv (GLenum target, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glTranslatef (GLfloat x, GLfloat y, GLfloat z); + +/* Available in both Common and Common-Lite profiles */ +GL_API void GL_APIENTRY glActiveTexture (GLenum texture); +GL_API void GL_APIENTRY glAlphaFuncx (GLenum func, GLclampx ref); +GL_API void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GL_API void GL_APIENTRY glBindTexture (GLenum target, GLuint texture); +GL_API void GL_APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); +GL_API void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +GL_API void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data); +GL_API void GL_APIENTRY glClear (GLbitfield mask); +GL_API void GL_APIENTRY glClearColorx (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); +GL_API void GL_APIENTRY glClearDepthx (GLclampx depth); +GL_API void GL_APIENTRY glClearStencil (GLint s); +GL_API void GL_APIENTRY glClientActiveTexture (GLenum texture); +GL_API void GL_APIENTRY glClipPlanex (GLenum plane, const GLfixed *equation); +GL_API void GL_APIENTRY glColor4ub (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +GL_API void GL_APIENTRY glColor4x (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GL_API void GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GL_API void GL_APIENTRY glColorPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +GL_API void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +GL_API void GL_APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GL_API void GL_APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GL_API void GL_APIENTRY glCullFace (GLenum mode); +GL_API void GL_APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GL_API void GL_APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); +GL_API void GL_APIENTRY glDepthFunc (GLenum func); +GL_API void GL_APIENTRY glDepthMask (GLboolean flag); +GL_API void GL_APIENTRY glDepthRangex (GLclampx zNear, GLclampx zFar); +GL_API void GL_APIENTRY glDisable (GLenum cap); +GL_API void GL_APIENTRY glDisableClientState (GLenum array); +GL_API void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); +GL_API void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +GL_API void GL_APIENTRY glEnable (GLenum cap); +GL_API void GL_APIENTRY glEnableClientState (GLenum array); +GL_API void GL_APIENTRY glFinish (void); +GL_API void GL_APIENTRY glFlush (void); +GL_API void GL_APIENTRY glFogx (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glFogxv (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glFrontFace (GLenum mode); +GL_API void GL_APIENTRY glFrustumx (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glGetBooleanv (GLenum pname, GLboolean *params); +GL_API void GL_APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetClipPlanex (GLenum pname, GLfixed eqn[4]); +GL_API void GL_APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GL_API void GL_APIENTRY glGenTextures (GLsizei n, GLuint *textures); +GL_API GLenum GL_APIENTRY glGetError (void); +GL_API void GL_APIENTRY glGetFixedv (GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetIntegerv (GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetLightxv (GLenum light, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetMaterialxv (GLenum face, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetPointerv (GLenum pname, void **params); +GL_API const GLubyte * GL_APIENTRY glGetString (GLenum name); +GL_API void GL_APIENTRY glGetTexEnviv (GLenum env, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetTexEnvxv (GLenum env, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetTexParameterxv (GLenum target, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glHint (GLenum target, GLenum mode); +GL_API GLboolean GL_APIENTRY glIsBuffer (GLuint buffer); +GL_API GLboolean GL_APIENTRY glIsEnabled (GLenum cap); +GL_API GLboolean GL_APIENTRY glIsTexture (GLuint texture); +GL_API void GL_APIENTRY glLightModelx (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightModelxv (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLightx (GLenum light, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightxv (GLenum light, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLineWidthx (GLfixed width); +GL_API void GL_APIENTRY glLoadIdentity (void); +GL_API void GL_APIENTRY glLoadMatrixx (const GLfixed *m); +GL_API void GL_APIENTRY glLogicOp (GLenum opcode); +GL_API void GL_APIENTRY glMaterialx (GLenum face, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glMaterialxv (GLenum face, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glMatrixMode (GLenum mode); +GL_API void GL_APIENTRY glMultMatrixx (const GLfixed *m); +GL_API void GL_APIENTRY glMultiTexCoord4x (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GL_API void GL_APIENTRY glNormal3x (GLfixed nx, GLfixed ny, GLfixed nz); +GL_API void GL_APIENTRY glNormalPointer (GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glOrthox (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glPixelStorei (GLenum pname, GLint param); +GL_API void GL_APIENTRY glPointParameterx (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glPointParameterxv (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glPointSizex (GLfixed size); +GL_API void GL_APIENTRY glPolygonOffsetx (GLfixed factor, GLfixed units); +GL_API void GL_APIENTRY glPopMatrix (void); +GL_API void GL_APIENTRY glPushMatrix (void); +GL_API void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +GL_API void GL_APIENTRY glRotatex (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glSampleCoverage (GLclampf value, GLboolean invert); +GL_API void GL_APIENTRY glSampleCoveragex (GLclampx value, GLboolean invert); +GL_API void GL_APIENTRY glScalex (GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GL_API void GL_APIENTRY glShadeModel (GLenum mode); +GL_API void GL_APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); +GL_API void GL_APIENTRY glStencilMask (GLuint mask); +GL_API void GL_APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); +GL_API void GL_APIENTRY glTexCoordPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glTexEnvi (GLenum target, GLenum pname, GLint param); +GL_API void GL_APIENTRY glTexEnvx (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexEnviv (GLenum target, GLenum pname, const GLint *params); +GL_API void GL_APIENTRY glTexEnvxv (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GL_API void GL_APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); +GL_API void GL_APIENTRY glTexParameterx (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params); +GL_API void GL_APIENTRY glTexParameterxv (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +GL_API void GL_APIENTRY glTranslatex (GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glVertexPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); + +/*------------------------------------------------------------------------* + * Required OES extension functions + *------------------------------------------------------------------------*/ + +/* GL_OES_read_format */ +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#endif + +/* GL_OES_compressed_paletted_texture */ +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#endif + +/* GL_OES_point_size_array */ +#ifndef GL_OES_point_size_array +#define GL_OES_point_size_array 1 +GL_API void GL_APIENTRY glPointSizePointerOES (GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +/* GL_OES_point_sprite */ +#ifndef GL_OES_point_sprite +#define GL_OES_point_sprite 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __gl_h_ */ + diff --git a/opengl/include/GLES/glext.h b/opengl/include/GLES/glext.h new file mode 100644 index 000000000..4c01871ae --- /dev/null +++ b/opengl/include/GLES/glext.h @@ -0,0 +1,622 @@ +#ifndef __glext_h_ +#define __glext_h_ + +/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +#ifndef GL_APIENTRYP +# define GL_APIENTRYP GL_APIENTRY* +#endif + +/*------------------------------------------------------------------------* + * OES extension tokens + *------------------------------------------------------------------------*/ + +/* GL_OES_blend_equation_separate */ +#ifndef GL_OES_blend_equation_separate +/* BLEND_EQUATION_RGB_OES same as BLEND_EQUATION_OES */ +#define GL_BLEND_EQUATION_RGB_OES 0x8009 +#define GL_BLEND_EQUATION_ALPHA_OES 0x883D +#endif + +/* GL_OES_blend_func_separate */ +#ifndef GL_OES_blend_func_separate +#define GL_BLEND_DST_RGB_OES 0x80C8 +#define GL_BLEND_SRC_RGB_OES 0x80C9 +#define GL_BLEND_DST_ALPHA_OES 0x80CA +#define GL_BLEND_SRC_ALPHA_OES 0x80CB +#endif + +/* GL_OES_blend_subtract */ +#ifndef GL_OES_blend_subtract +#define GL_BLEND_EQUATION_OES 0x8009 +#define GL_FUNC_ADD_OES 0x8006 +#define GL_FUNC_SUBTRACT_OES 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_OES 0x800B +#endif + +/* GL_OES_compressed_ETC1_RGB8_texture */ +#ifndef GL_OES_compressed_ETC1_RGB8_texture +#define GL_ETC1_RGB8_OES 0x8D64 +#endif + +/* GL_OES_depth24 */ +#ifndef GL_OES_depth24 +#define GL_DEPTH_COMPONENT24_OES 0x81A6 +#endif + +/* GL_OES_depth32 */ +#ifndef GL_OES_depth32 +#define GL_DEPTH_COMPONENT32_OES 0x81A7 +#endif + +/* GL_OES_draw_texture */ +#ifndef GL_OES_draw_texture +#define GL_TEXTURE_CROP_RECT_OES 0x8B9D +#endif + +/* GL_OES_EGL_image */ +#ifndef GL_OES_EGL_image +typedef void* GLeglImageOES; +#endif + +/* GL_OES_fixed_point */ +#ifndef GL_OES_fixed_point +#define GL_FIXED_OES 0x140C +#endif + +/* GL_OES_framebuffer_object */ +#ifndef GL_OES_framebuffer_object +#define GL_NONE_OES 0 +#define GL_FRAMEBUFFER_OES 0x8D40 +#define GL_RENDERBUFFER_OES 0x8D41 +#define GL_RGBA4_OES 0x8056 +#define GL_RGB5_A1_OES 0x8057 +#define GL_RGB565_OES 0x8D62 +#define GL_DEPTH_COMPONENT16_OES 0x81A5 +#define GL_RENDERBUFFER_WIDTH_OES 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_OES 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_OES 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE_OES 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_OES 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_OES 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_OES 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_OES 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_OES 0x8D55 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_OES 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_OES 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_OES 0x8CD3 +#define GL_COLOR_ATTACHMENT0_OES 0x8CE0 +#define GL_DEPTH_ATTACHMENT_OES 0x8D00 +#define GL_STENCIL_ATTACHMENT_OES 0x8D20 +#define GL_FRAMEBUFFER_COMPLETE_OES 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES 0x8CDA +#define GL_FRAMEBUFFER_UNSUPPORTED_OES 0x8CDD +#define GL_FRAMEBUFFER_BINDING_OES 0x8CA6 +#define GL_RENDERBUFFER_BINDING_OES 0x8CA7 +#define GL_MAX_RENDERBUFFER_SIZE_OES 0x84E8 +#define GL_INVALID_FRAMEBUFFER_OPERATION_OES 0x0506 +#endif + +/* GL_OES_mapbuffer */ +#ifndef GL_OES_mapbuffer +#define GL_WRITE_ONLY_OES 0x88B9 +#define GL_BUFFER_ACCESS_OES 0x88BB +#define GL_BUFFER_MAPPED_OES 0x88BC +#define GL_BUFFER_MAP_POINTER_OES 0x88BD +#endif + +/* GL_OES_matrix_get */ +#ifndef GL_OES_matrix_get +#define GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES 0x898D +#define GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES 0x898E +#define GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES 0x898F +#endif + +/* GL_OES_matrix_palette */ +#ifndef GL_OES_matrix_palette +#define GL_MAX_VERTEX_UNITS_OES 0x86A4 +#define GL_MAX_PALETTE_MATRICES_OES 0x8842 +#define GL_MATRIX_PALETTE_OES 0x8840 +#define GL_MATRIX_INDEX_ARRAY_OES 0x8844 +#define GL_WEIGHT_ARRAY_OES 0x86AD +#define GL_CURRENT_PALETTE_MATRIX_OES 0x8843 +#define GL_MATRIX_INDEX_ARRAY_SIZE_OES 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_OES 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_OES 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_OES 0x8849 +#define GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES 0x8B9E +#define GL_WEIGHT_ARRAY_SIZE_OES 0x86AB +#define GL_WEIGHT_ARRAY_TYPE_OES 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_OES 0x86AA +#define GL_WEIGHT_ARRAY_POINTER_OES 0x86AC +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_OES 0x889E +#endif + +/* GL_OES_packed_depth_stencil */ +#ifndef GL_OES_packed_depth_stencil +#define GL_DEPTH_STENCIL_OES 0x84F9 +#define GL_UNSIGNED_INT_24_8_OES 0x84FA +#define GL_DEPTH24_STENCIL8_OES 0x88F0 +#endif + +/* GL_OES_rgb8_rgba8 */ +#ifndef GL_OES_rgb8_rgba8 +#define GL_RGB8_OES 0x8051 +#define GL_RGBA8_OES 0x8058 +#endif + +/* GL_OES_stencil1 */ +#ifndef GL_OES_stencil1 +#define GL_STENCIL_INDEX1_OES 0x8D46 +#endif + +/* GL_OES_stencil4 */ +#ifndef GL_OES_stencil4 +#define GL_STENCIL_INDEX4_OES 0x8D47 +#endif + +/* GL_OES_stencil8 */ +#ifndef GL_OES_stencil8 +#define GL_STENCIL_INDEX8_OES 0x8D48 +#endif + +/* GL_OES_stencil_wrap */ +#ifndef GL_OES_stencil_wrap +#define GL_INCR_WRAP_OES 0x8507 +#define GL_DECR_WRAP_OES 0x8508 +#endif + +/* GL_OES_texture_cube_map */ +#ifndef GL_OES_texture_cube_map +#define GL_NORMAL_MAP_OES 0x8511 +#define GL_REFLECTION_MAP_OES 0x8512 +#define GL_TEXTURE_CUBE_MAP_OES 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_OES 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_OES 0x851A +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_OES 0x851C +#define GL_TEXTURE_GEN_MODE_OES 0x2500 +#define GL_TEXTURE_GEN_STR_OES 0x8D60 +#endif + +/* GL_OES_texture_mirrored_repeat */ +#ifndef GL_OES_texture_mirrored_repeat +#define GL_MIRRORED_REPEAT_OES 0x8370 +#endif + +/*------------------------------------------------------------------------* + * AMD extension tokens + *------------------------------------------------------------------------*/ + +/* GL_AMD_compressed_3DC_texture */ +#ifndef GL_AMD_compressed_3DC_texture +#define GL_3DC_X_AMD 0x87F9 +#define GL_3DC_XY_AMD 0x87FA +#endif + +/* GL_AMD_compressed_ATC_texture */ +#ifndef GL_AMD_compressed_ATC_texture +#define GL_ATC_RGB_AMD 0x8C92 +#define GL_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93 +#define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE +#endif + +/*------------------------------------------------------------------------* + * EXT extension tokens + *------------------------------------------------------------------------*/ + +/* GL_EXT_texture_filter_anisotropic */ +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +/*------------------------------------------------------------------------* + * OES extension functions + *------------------------------------------------------------------------*/ + +/* GL_OES_blend_equation_separate */ +#ifndef GL_OES_blend_equation_separate +#define GL_OES_blend_equation_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBlendEquationSeparateOES (GLenum modeRGB, GLenum modeAlpha); +#endif +typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONSEPARATEOESPROC) (GLenum modeRGB, GLenum modeAlpha); +#endif + +/* GL_OES_blend_func_separate */ +#ifndef GL_OES_blend_func_separate +#define GL_OES_blend_func_separate 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBlendFuncSeparateOES (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +typedef void (GL_APIENTRYP PFNGLBLENDFUNCSEPARATEOESPROC) (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif + +/* GL_OES_blend_subtract */ +#ifndef GL_OES_blend_subtract +#define GL_OES_blend_subtract 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glBlendEquationOES (GLenum mode); +#endif +typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONOESPROC) (GLenum mode); +#endif + +/* GL_OES_byte_coordinates */ +#ifndef GL_OES_byte_coordinates +#define GL_OES_byte_coordinates 1 +#endif + +/* GL_OES_compressed_ETC1_RGB8_texture */ +#ifndef GL_OES_compressed_ETC1_RGB8_texture +#define GL_OES_compressed_ETC1_RGB8_texture 1 +#endif + +/* GL_OES_depth24 */ +#ifndef GL_OES_depth24 +#define GL_OES_depth24 1 +#endif + +/* GL_OES_depth32 */ +#ifndef GL_OES_depth32 +#define GL_OES_depth32 1 +#endif + +/* GL_OES_draw_texture */ +#ifndef GL_OES_draw_texture +#define GL_OES_draw_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glDrawTexsOES (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height); +GL_API void GL_APIENTRY glDrawTexiOES (GLint x, GLint y, GLint z, GLint width, GLint height); +GL_API void GL_APIENTRY glDrawTexxOES (GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height); +GL_API void GL_APIENTRY glDrawTexsvOES (const GLshort *coords); +GL_API void GL_APIENTRY glDrawTexivOES (const GLint *coords); +GL_API void GL_APIENTRY glDrawTexxvOES (const GLfixed *coords); +GL_API void GL_APIENTRY glDrawTexfOES (GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height); +GL_API void GL_APIENTRY glDrawTexfvOES (const GLfloat *coords); +#endif +typedef void (GL_APIENTRYP PFNGLDRAWTEXSOESPROC) (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXIOESPROC) (GLint x, GLint y, GLint z, GLint width, GLint height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXXOESPROC) (GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXSVOESPROC) (const GLshort *coords); +typedef void (GL_APIENTRYP PFNGLDRAWTEXIVOESPROC) (const GLint *coords); +typedef void (GL_APIENTRYP PFNGLDRAWTEXXVOESPROC) (const GLfixed *coords); +typedef void (GL_APIENTRYP PFNGLDRAWTEXFOESPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height); +typedef void (GL_APIENTRYP PFNGLDRAWTEXFVOESPROC) (const GLfloat *coords); +#endif + +/* GL_OES_EGL_image */ +#ifndef GL_OES_EGL_image +#define GL_OES_EGL_image 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image); +GL_API void GL_APIENTRY glEGLImageTargetRenderbufferStorageOES (GLenum target, GLeglImageOES image); +#endif +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image); +#endif + +/* GL_OES_element_index_uint */ +#ifndef GL_OES_element_index_uint +#define GL_OES_element_index_uint 1 +#endif + +/* GL_OES_extended_matrix_palette */ +#ifndef GL_OES_extended_matrix_palette +#define GL_OES_extended_matrix_palette 1 +#endif + +/* GL_OES_fbo_render_mipmap */ +#ifndef GL_OES_fbo_render_mipmap +#define GL_OES_fbo_render_mipmap 1 +#endif + +/* GL_OES_fixed_point */ +#ifndef GL_OES_fixed_point +#define GL_OES_fixed_point 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glAlphaFuncxOES (GLenum func, GLclampx ref); +GL_API void GL_APIENTRY glClearColorxOES (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); +GL_API void GL_APIENTRY glClearDepthxOES (GLclampx depth); +GL_API void GL_APIENTRY glClipPlanexOES (GLenum plane, const GLfixed *equation); +GL_API void GL_APIENTRY glColor4xOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GL_API void GL_APIENTRY glDepthRangexOES (GLclampx zNear, GLclampx zFar); +GL_API void GL_APIENTRY glFogxOES (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glFogxvOES (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glFrustumxOES (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glGetClipPlanexOES (GLenum pname, GLfixed eqn[4]); +GL_API void GL_APIENTRY glGetFixedvOES (GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetLightxvOES (GLenum light, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetMaterialxvOES (GLenum face, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetTexEnvxvOES (GLenum env, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glGetTexParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GL_API void GL_APIENTRY glLightModelxOES (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightModelxvOES (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLightxOES (GLenum light, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glLightxvOES (GLenum light, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glLineWidthxOES (GLfixed width); +GL_API void GL_APIENTRY glLoadMatrixxOES (const GLfixed *m); +GL_API void GL_APIENTRY glMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glMaterialxvOES (GLenum face, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glMultMatrixxOES (const GLfixed *m); +GL_API void GL_APIENTRY glMultiTexCoord4xOES (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GL_API void GL_APIENTRY glNormal3xOES (GLfixed nx, GLfixed ny, GLfixed nz); +GL_API void GL_APIENTRY glOrthoxOES (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GL_API void GL_APIENTRY glPointParameterxOES (GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glPointParameterxvOES (GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glPointSizexOES (GLfixed size); +GL_API void GL_APIENTRY glPolygonOffsetxOES (GLfixed factor, GLfixed units); +GL_API void GL_APIENTRY glRotatexOES (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glSampleCoveragexOES (GLclampx value, GLboolean invert); +GL_API void GL_APIENTRY glScalexOES (GLfixed x, GLfixed y, GLfixed z); +GL_API void GL_APIENTRY glTexEnvxOES (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexEnvxvOES (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTexParameterxOES (GLenum target, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glTranslatexOES (GLfixed x, GLfixed y, GLfixed z); +#endif +typedef void (GL_APIENTRYP PFNGLALPHAFUNCXOESPROC) (GLenum func, GLclampx ref); +typedef void (GL_APIENTRYP PFNGLCLEARCOLORXOESPROC) (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); +typedef void (GL_APIENTRYP PFNGLCLEARDEPTHXOESPROC) (GLclampx depth); +typedef void (GL_APIENTRYP PFNGLCLIPPLANEXOESPROC) (GLenum plane, const GLfixed *equation); +typedef void (GL_APIENTRYP PFNGLCOLOR4XOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (GL_APIENTRYP PFNGLDEPTHRANGEXOESPROC) (GLclampx zNear, GLclampx zFar); +typedef void (GL_APIENTRYP PFNGLFOGXOESPROC) (GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLFOGXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLFRUSTUMXOESPROC) (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +typedef void (GL_APIENTRYP PFNGLGETCLIPPLANEXOESPROC) (GLenum pname, GLfixed eqn[4]); +typedef void (GL_APIENTRYP PFNGLGETFIXEDVOESPROC) (GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETLIGHTXVOESPROC) (GLenum light, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETMATERIALXVOESPROC) (GLenum face, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETTEXENVXVOESPROC) (GLenum env, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (GL_APIENTRYP PFNGLLIGHTMODELXOESPROC) (GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLLIGHTMODELXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLLIGHTXVOESPROC) (GLenum light, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLLINEWIDTHXOESPROC) (GLfixed width); +typedef void (GL_APIENTRYP PFNGLLOADMATRIXXOESPROC) (const GLfixed *m); +typedef void (GL_APIENTRYP PFNGLMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLMATERIALXVOESPROC) (GLenum face, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLMULTMATRIXXOESPROC) (const GLfixed *m); +typedef void (GL_APIENTRYP PFNGLMULTITEXCOORD4XOESPROC) (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (GL_APIENTRYP PFNGLNORMAL3XOESPROC) (GLfixed nx, GLfixed ny, GLfixed nz); +typedef void (GL_APIENTRYP PFNGLORTHOXOESPROC) (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +typedef void (GL_APIENTRYP PFNGLPOINTPARAMETERXOESPROC) (GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLPOINTPARAMETERXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLPOINTSIZEXOESPROC) (GLfixed size); +typedef void (GL_APIENTRYP PFNGLPOLYGONOFFSETXOESPROC) (GLfixed factor, GLfixed units); +typedef void (GL_APIENTRYP PFNGLROTATEXOESPROC) (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +typedef void (GL_APIENTRYP PFNGLSAMPLECOVERAGEXOESPROC) (GLclampx value, GLboolean invert); +typedef void (GL_APIENTRYP PFNGLSCALEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (GL_APIENTRYP PFNGLTEXENVXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLTEXENVXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLTEXPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLTRANSLATEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +#endif + +/* GL_OES_framebuffer_object */ +#ifndef GL_OES_framebuffer_object +#define GL_OES_framebuffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API GLboolean GL_APIENTRY glIsRenderbufferOES (GLuint renderbuffer); +GL_API void GL_APIENTRY glBindRenderbufferOES (GLenum target, GLuint renderbuffer); +GL_API void GL_APIENTRY glDeleteRenderbuffersOES (GLsizei n, const GLuint* renderbuffers); +GL_API void GL_APIENTRY glGenRenderbuffersOES (GLsizei n, GLuint* renderbuffers); +GL_API void GL_APIENTRY glRenderbufferStorageOES (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GL_API void GL_APIENTRY glGetRenderbufferParameterivOES (GLenum target, GLenum pname, GLint* params); +GL_API GLboolean GL_APIENTRY glIsFramebufferOES (GLuint framebuffer); +GL_API void GL_APIENTRY glBindFramebufferOES (GLenum target, GLuint framebuffer); +GL_API void GL_APIENTRY glDeleteFramebuffersOES (GLsizei n, const GLuint* framebuffers); +GL_API void GL_APIENTRY glGenFramebuffersOES (GLsizei n, GLuint* framebuffers); +GL_API GLenum GL_APIENTRY glCheckFramebufferStatusOES (GLenum target); +GL_API void GL_APIENTRY glFramebufferRenderbufferOES (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GL_API void GL_APIENTRY glFramebufferTexture2DOES (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GL_API void GL_APIENTRY glGetFramebufferAttachmentParameterivOES (GLenum target, GLenum attachment, GLenum pname, GLint* params); +GL_API void GL_APIENTRY glGenerateMipmapOES (GLenum target); +#endif +typedef GLboolean (GL_APIENTRYP PFNGLISRENDERBUFFEROESPROC) (GLuint renderbuffer); +typedef void (GL_APIENTRYP PFNGLBINDRENDERBUFFEROESPROC) (GLenum target, GLuint renderbuffer); +typedef void (GL_APIENTRYP PFNGLDELETERENDERBUFFERSOESPROC) (GLsizei n, const GLuint* renderbuffers); +typedef void (GL_APIENTRYP PFNGLGENRENDERBUFFERSOESPROC) (GLsizei n, GLuint* renderbuffers); +typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GL_APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVOESPROC) (GLenum target, GLenum pname, GLint* params); +typedef GLboolean (GL_APIENTRYP PFNGLISFRAMEBUFFEROESPROC) (GLuint framebuffer); +typedef void (GL_APIENTRYP PFNGLBINDFRAMEBUFFEROESPROC) (GLenum target, GLuint framebuffer); +typedef void (GL_APIENTRYP PFNGLDELETEFRAMEBUFFERSOESPROC) (GLsizei n, const GLuint* framebuffers); +typedef void (GL_APIENTRYP PFNGLGENFRAMEBUFFERSOESPROC) (GLsizei n, GLuint* framebuffers); +typedef GLenum (GL_APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSOESPROC) (GLenum target); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEROESPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DOESPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GL_APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVOESPROC) (GLenum target, GLenum attachment, GLenum pname, GLint* params); +typedef void (GL_APIENTRYP PFNGLGENERATEMIPMAPOESPROC) (GLenum target); +#endif + +/* GL_OES_mapbuffer */ +#ifndef GL_OES_mapbuffer +#define GL_OES_mapbuffer 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void* GL_APIENTRY glMapBufferOES (GLenum target, GLenum access); +GL_API GLboolean GL_APIENTRY glUnmapBufferOES (GLenum target); +GL_API void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, void** params); +#endif +typedef void* (GL_APIENTRYP PFNGLMAPBUFFEROESPROC) (GLenum target, GLenum access); +typedef GLboolean (GL_APIENTRYP PFNGLUNMAPBUFFEROESPROC) (GLenum target); +typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, void** params); +#endif + +/* GL_OES_matrix_get */ +#ifndef GL_OES_matrix_get +#define GL_OES_matrix_get 1 +#endif + +/* GL_OES_matrix_palette */ +#ifndef GL_OES_matrix_palette +#define GL_OES_matrix_palette 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glCurrentPaletteMatrixOES (GLuint matrixpaletteindex); +GL_API void GL_APIENTRY glLoadPaletteFromModelViewMatrixOES (void); +GL_API void GL_APIENTRY glMatrixIndexPointerOES (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GL_API void GL_APIENTRY glWeightPointerOES (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif +typedef void (GL_APIENTRYP PFNGLCURRENTPALETTEMATRIXOESPROC) (GLuint matrixpaletteindex); +typedef void (GL_APIENTRYP PFNGLLOADPALETTEFROMMODELVIEWMATRIXOESPROC) (void); +typedef void (GL_APIENTRYP PFNGLMATRIXINDEXPOINTEROESPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (GL_APIENTRYP PFNGLWEIGHTPOINTEROESPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +#endif + +/* GL_OES_packed_depth_stencil */ +#ifndef GL_OES_packed_depth_stencil +#define GL_OES_packed_depth_stencil 1 +#endif + +/* GL_OES_query_matrix */ +#ifndef GL_OES_query_matrix +#define GL_OES_query_matrix 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API GLbitfield GL_APIENTRY glQueryMatrixxOES (GLfixed mantissa[16], GLint exponent[16]); +#endif +typedef GLbitfield (GL_APIENTRYP PFNGLQUERYMATRIXXOESPROC) (GLfixed mantissa[16], GLint exponent[16]); +#endif + +/* GL_OES_rgb8_rgba8 */ +#ifndef GL_OES_rgb8_rgba8 +#define GL_OES_rgb8_rgba8 1 +#endif + +/* GL_OES_single_precision */ +#ifndef GL_OES_single_precision +#define GL_OES_single_precision 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glDepthRangefOES (GLclampf zNear, GLclampf zFar); +GL_API void GL_APIENTRY glFrustumfOES (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glOrthofOES (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GL_API void GL_APIENTRY glClipPlanefOES (GLenum plane, const GLfloat *equation); +GL_API void GL_APIENTRY glGetClipPlanefOES (GLenum pname, GLfloat eqn[4]); +GL_API void GL_APIENTRY glClearDepthfOES (GLclampf depth); +#endif +typedef void (GL_APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf zNear, GLclampf zFar); +typedef void (GL_APIENTRYP PFNGLFRUSTUMFOESPROC) (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +typedef void (GL_APIENTRYP PFNGLORTHOFOESPROC) (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +typedef void (GL_APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); +typedef void (GL_APIENTRYP PFNGLGETCLIPPLANEFOESPROC) (GLenum pname, GLfloat eqn[4]); +typedef void (GL_APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); +#endif + +/* GL_OES_stencil1 */ +#ifndef GL_OES_stencil1 +#define GL_OES_stencil1 1 +#endif + +/* GL_OES_stencil4 */ +#ifndef GL_OES_stencil4 +#define GL_OES_stencil4 1 +#endif + +/* GL_OES_stencil8 */ +#ifndef GL_OES_stencil8 +#define GL_OES_stencil8 1 +#endif + +/* GL_OES_stencil_wrap */ +#ifndef GL_OES_stencil_wrap +#define GL_OES_stencil_wrap 1 +#endif + +/* GL_OES_texture_cube_map */ +#ifndef GL_OES_texture_cube_map +#define GL_OES_texture_cube_map 1 +#ifdef GL_GLEXT_PROTOTYPES +GL_API void GL_APIENTRY glTexGenfOES (GLenum coord, GLenum pname, GLfloat param); +GL_API void GL_APIENTRY glTexGenfvOES (GLenum coord, GLenum pname, const GLfloat *params); +GL_API void GL_APIENTRY glTexGeniOES (GLenum coord, GLenum pname, GLint param); +GL_API void GL_APIENTRY glTexGenivOES (GLenum coord, GLenum pname, const GLint *params); +GL_API void GL_APIENTRY glTexGenxOES (GLenum coord, GLenum pname, GLfixed param); +GL_API void GL_APIENTRY glTexGenxvOES (GLenum coord, GLenum pname, const GLfixed *params); +GL_API void GL_APIENTRY glGetTexGenfvOES (GLenum coord, GLenum pname, GLfloat *params); +GL_API void GL_APIENTRY glGetTexGenivOES (GLenum coord, GLenum pname, GLint *params); +GL_API void GL_APIENTRY glGetTexGenxvOES (GLenum coord, GLenum pname, GLfixed *params); +#endif +typedef void (GL_APIENTRYP PFNGLTEXGENFOESPROC) (GLenum coord, GLenum pname, GLfloat param); +typedef void (GL_APIENTRYP PFNGLTEXGENFVOESPROC) (GLenum coord, GLenum pname, const GLfloat *params); +typedef void (GL_APIENTRYP PFNGLTEXGENIOESPROC) (GLenum coord, GLenum pname, GLint param); +typedef void (GL_APIENTRYP PFNGLTEXGENIVOESPROC) (GLenum coord, GLenum pname, const GLint *params); +typedef void (GL_APIENTRYP PFNGLTEXGENXOESPROC) (GLenum coord, GLenum pname, GLfixed param); +typedef void (GL_APIENTRYP PFNGLTEXGENXVOESPROC) (GLenum coord, GLenum pname, const GLfixed *params); +typedef void (GL_APIENTRYP PFNGLGETTEXGENFVOESPROC) (GLenum coord, GLenum pname, GLfloat *params); +typedef void (GL_APIENTRYP PFNGLGETTEXGENIVOESPROC) (GLenum coord, GLenum pname, GLint *params); +typedef void (GL_APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, GLfixed *params); +#endif + +/* GL_OES_texture_env_crossbar */ +#ifndef GL_OES_texture_env_crossbar +#define GL_OES_texture_env_crossbar 1 +#endif + +/* GL_OES_texture_mirrored_repeat */ +#ifndef GL_OES_texture_mirrored_repeat +#define GL_OES_texture_mirrored_repeat 1 +#endif + +/*------------------------------------------------------------------------* + * AMD extension functions + *------------------------------------------------------------------------*/ + +/* GL_AMD_compressed_3DC_texture */ +#ifndef GL_AMD_compressed_3DC_texture +#define GL_AMD_compressed_3DC_texture 1 +#endif + +/* GL_AMD_compressed_ATC_texture */ +#ifndef GL_AMD_compressed_ATC_texture +#define GL_AMD_compressed_ATC_texture 1 +#endif + +/*------------------------------------------------------------------------* + * EXT extension functions + *------------------------------------------------------------------------*/ + +/* GL_EXT_texture_filter_anisotropic */ +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#endif + +/*------------------------------------------------------------------------* + * dalvik extension functions + *------------------------------------------------------------------------*/ +#ifdef ANDROID +void glColorPointerBounds(GLint size, GLenum type, GLsizei stride, + const GLvoid *ptr, GLsizei count); +void glNormalPointerBounds(GLenum type, GLsizei stride, + const GLvoid *pointer, GLsizei count); +void glTexCoordPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count); +void glVertexPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* __glext_h_ */ + diff --git a/opengl/include/GLES/glplatform.h b/opengl/include/GLES/glplatform.h new file mode 100644 index 000000000..0924caeaf --- /dev/null +++ b/opengl/include/GLES/glplatform.h @@ -0,0 +1,39 @@ +#ifndef __glplatform_h_ +#define __glplatform_h_ + +/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */ + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +/* Platform-specific types and definitions for OpenGL ES 1.X gl.h + * Last modified on 2008/12/19 + * + * Adopters may modify khrplatform.h and this file to suit their platform. + * You are encouraged to submit all modifications to the Khronos group so that + * they can be included in future versions of this file. Please submit changes + * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla) + * by filing a bug against product "OpenGL-ES" component "Registry". + */ + +#include + +#ifndef GL_API +#define GL_API KHRONOS_APICALL +#endif + +#if defined(ANDROID) + +#define GL_APIENTRY KHRONOS_APIENTRY + +// XXX: this should probably not be here +#define GL_DIRECT_TEXTURE_2D_QUALCOMM 0x7E80 + +// XXX: not sure how this is intended to be used +#define GL_GLEXT_PROTOTYPES + +#endif + +#endif /* __glplatform_h_ */ diff --git a/opengl/include/KHR/khrplatform.h b/opengl/include/KHR/khrplatform.h new file mode 100644 index 000000000..4cc27c52d --- /dev/null +++ b/opengl/include/KHR/khrplatform.h @@ -0,0 +1,241 @@ +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2009 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Platform-specific types and definitions. + * $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by sending them to the public Khronos Bugzilla + * (http://khronos.org/bugzilla) by filing a bug against product + * "Khronos (general)" component "Registry". + * + * A predefined template which fills in some of the bug fields can be + * reached using http://tinyurl.com/khrplatform-h-bugreport, but you + * must create a Bugzilla login first. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system. + * http://www.khronos.org/registry/implementers_guide.pdf + * + * + * This file should be included as + * #include + * by the Khronos API header file that uses its types and defines. + * + * The types in this file should only be used to define API-specific types. + * Types defined in this file: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * + * Macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_APIENTRY + * KHRONOS_APIATTRIBUTES + * These may be used in function prototypes as: + * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(_WIN32) && !defined(__SCITECH_SNAP__) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIENTRY + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_APIENTRY __stdcall +#else +# define KHRONOS_APIENTRY +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + + +#endif /* __khrplatform_h_ */ diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk new file mode 100644 index 000000000..99efe4c41 --- /dev/null +++ b/opengl/libagl/Android.mk @@ -0,0 +1,39 @@ +LOCAL_PATH:= $(call my-dir) + +# +# Build the software OpenGL ES library +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + egl.cpp \ + state.cpp \ + texture.cpp \ + Tokenizer.cpp \ + TokenManager.cpp \ + TextureObjectManager.cpp \ + BufferObjectManager.cpp \ + array.cpp.arm \ + fp.cpp.arm \ + light.cpp.arm \ + matrix.cpp.arm \ + mipmap.cpp.arm \ + primitives.cpp.arm \ + vertex.cpp.arm + +ifeq ($(TARGET_ARCH),arm) + LOCAL_SRC_FILES += fixed_asm.S iterators.S + LOCAL_CFLAGS += -fstrict-aliasing +endif + +ifneq ($(TARGET_SIMULATOR),true) + # we need to access the private Bionic header + LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private +endif + +LOCAL_SHARED_LIBRARIES := libcutils libutils libpixelflinger +LOCAL_LDLIBS := -lpthread -ldl +LOCAL_MODULE:= libagl + +include $(BUILD_SHARED_LIBRARY) diff --git a/opengl/libagl/BufferObjectManager.cpp b/opengl/libagl/BufferObjectManager.cpp new file mode 100644 index 000000000..6bf28ee32 --- /dev/null +++ b/opengl/libagl/BufferObjectManager.cpp @@ -0,0 +1,103 @@ +/* + ** Copyright 2008, 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 +#include +#include + +#include + +#include "BufferObjectManager.h" + + +namespace android { + +using namespace gl; + +// ---------------------------------------------------------------------------- + +EGLBufferObjectManager::EGLBufferObjectManager() +: TokenManager(), mCount(0) +{ +} + +EGLBufferObjectManager::~EGLBufferObjectManager() +{ + // destroy all the buffer objects and their storage + GLsizei n = mBuffers.size(); + for (GLsizei i=0 ; idata); + delete bo; + } +} + +buffer_t const* EGLBufferObjectManager::bind(GLuint buffer) +{ + Mutex::Autolock _l(mLock); + int32_t i = mBuffers.indexOfKey(buffer); + if (i >= 0) { + return mBuffers.valueAt(i); + } + buffer_t* bo = new buffer_t; + bo->data = 0; + bo->usage = GL_STATIC_DRAW; + bo->size = 0; + bo->name = buffer; + mBuffers.add(buffer, bo); + return bo; +} + +int EGLBufferObjectManager::allocateStore(buffer_t* bo, + GLsizeiptr size, GLenum usage) +{ + Mutex::Autolock _l(mLock); + if (size != bo->size) { + uint8_t* data = (uint8_t*)malloc(size); + if (data == 0) + return -1; + free(bo->data); + bo->data = data; + bo->size = size; + } + bo->usage = usage; + return 0; +} + +void EGLBufferObjectManager::deleteBuffers(GLsizei n, const GLuint* buffers) +{ + Mutex::Autolock _l(mLock); + while (n--) { + const GLuint t = *buffers++; + if (t) { + int32_t index = mBuffers.indexOfKey(t); + if (index >= 0) { + buffer_t* bo = mBuffers.valueAt(index); + free(bo->data); + mBuffers.removeItemsAt(index); + delete bo; + } + } + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/opengl/libagl/BufferObjectManager.h b/opengl/libagl/BufferObjectManager.h new file mode 100644 index 000000000..9e9340adc --- /dev/null +++ b/opengl/libagl/BufferObjectManager.h @@ -0,0 +1,85 @@ +/* + ** + ** Copyright 2006, 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. + */ + +#ifndef ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H +#define ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "Tokenizer.h" +#include "TokenManager.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +namespace gl { + +struct buffer_t { + GLsizeiptr size; + GLenum usage; + uint8_t* data; + uint32_t name; +}; + +}; + +class EGLBufferObjectManager : public TokenManager +{ +public: + EGLBufferObjectManager(); + ~EGLBufferObjectManager(); + + // protocol for sp<> + inline void incStrong(const void* id) const; + inline void decStrong(const void* id) const; + typedef void weakref_type; + + gl::buffer_t const* bind(GLuint buffer); + int allocateStore(gl::buffer_t* bo, GLsizeiptr size, GLenum usage); + void deleteBuffers(GLsizei n, const GLuint* buffers); + +private: + mutable volatile int32_t mCount; + mutable Mutex mLock; + KeyedVector mBuffers; +}; + +void EGLBufferObjectManager::incStrong(const void* id) const { + android_atomic_inc(&mCount); +} +void EGLBufferObjectManager::decStrong(const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete this; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H + diff --git a/opengl/libagl/TextureObjectManager.cpp b/opengl/libagl/TextureObjectManager.cpp new file mode 100644 index 000000000..ce3185417 --- /dev/null +++ b/opengl/libagl/TextureObjectManager.cpp @@ -0,0 +1,309 @@ +/* + ** Copyright 2006, 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 "context.h" +#include "TextureObjectManager.h" + +namespace android { +// ---------------------------------------------------------------------------- + +EGLTextureObject::EGLTextureObject() + : mCount(0), mSize(0) +{ + init(); +} + +EGLTextureObject::~EGLTextureObject() +{ + if (!direct) { + if (mSize && surface.data) + free(surface.data); + if (mMipmaps) + freeMipmaps(); + } +} + +void EGLTextureObject::init() +{ + memset(&surface, 0, sizeof(surface)); + surface.version = sizeof(surface); + mMipmaps = 0; + mNumExtraLod = 0; + mIsComplete = false; + wraps = GL_REPEAT; + wrapt = GL_REPEAT; + min_filter = GL_LINEAR; + mag_filter = GL_LINEAR; + internalformat = 0; + memset(crop_rect, 0, sizeof(crop_rect)); + generate_mipmap = GL_FALSE; + direct = GL_FALSE; +} + +void EGLTextureObject::copyParameters(const sp& old) +{ + wraps = old->wraps; + wrapt = old->wrapt; + min_filter = old->min_filter; + mag_filter = old->mag_filter; + memcpy(crop_rect, old->crop_rect, sizeof(crop_rect)); + generate_mipmap = old->generate_mipmap; + direct = old->direct; +} + +status_t EGLTextureObject::allocateMipmaps() +{ + // here, by construction, mMipmaps=0 && mNumExtraLod=0 + + if (!surface.data) + return NO_INIT; + + int w = surface.width; + int h = surface.height; + const int numLods = 31 - gglClz(max(w,h)); + if (numLods <= 0) + return NO_ERROR; + + mMipmaps = (GGLSurface*)malloc(numLods * sizeof(GGLSurface)); + if (!mMipmaps) + return NO_MEMORY; + + memset(mMipmaps, 0, numLods * sizeof(GGLSurface)); + mNumExtraLod = numLods; + return NO_ERROR; +} + +void EGLTextureObject::freeMipmaps() +{ + if (mMipmaps) { + for (int i=0 ; i(mip(lod)); +} + +status_t EGLTextureObject::setSurface(GGLSurface const* s) +{ + // XXX: glFlush() on 's' + if (mSize && surface.data) { + free(surface.data); + } + surface = *s; + internalformat = 0; + + // we should keep the crop_rect, but it's delicate because + // the new size of the surface could make it invalid. + // so for now, we just loose it. + memset(crop_rect, 0, sizeof(crop_rect)); + + // it would be nice if we could keep the generate_mipmap flag, + // we would have to generate them right now though. + generate_mipmap = GL_FALSE; + + direct = GL_TRUE; + mSize = 0; // we don't own this surface + if (mMipmaps) + freeMipmaps(); + mIsComplete = true; + return NO_ERROR; +} + +status_t EGLTextureObject::reallocate( + GLint level, int w, int h, int s, + int format, int compressedFormat, int bpr) +{ + const size_t size = h * bpr; + if (level == 0) + { + if (size!=mSize || !surface.data) { + if (mSize && surface.data) { + free(surface.data); + } + surface.data = (GGLubyte*)malloc(size); + if (!surface.data) { + mSize = 0; + mIsComplete = false; + return NO_MEMORY; + } + mSize = size; + } + surface.version = sizeof(GGLSurface); + surface.width = w; + surface.height = h; + surface.stride = s; + surface.format = format; + surface.compressedFormat = compressedFormat; + if (mMipmaps) + freeMipmaps(); + mIsComplete = true; + } + else + { + if (!mMipmaps) { + if (allocateMipmaps() != NO_ERROR) + return NO_MEMORY; + } + + LOGW_IF(level-1 >= mNumExtraLod, + "specifying mipmap level %d, but # of level is %d", + level, mNumExtraLod+1); + + GGLSurface& mipmap = editMip(level); + if (mipmap.data) + free(mipmap.data); + + mipmap.data = (GGLubyte*)malloc(size); + if (!mipmap.data) { + memset(&mipmap, 0, sizeof(GGLSurface)); + mIsComplete = false; + return NO_MEMORY; + } + + mipmap.version = sizeof(GGLSurface); + mipmap.width = w; + mipmap.height = h; + mipmap.stride = s; + mipmap.format = format; + mipmap.compressedFormat = compressedFormat; + + // check if the texture is complete + mIsComplete = true; + const GGLSurface* prev = &surface; + for (int i=0 ; iformat != surface.format) { + mIsComplete = false; + break; + } + + uint32_t w = (prev->width >> 1) ? : 1; + uint32_t h = (prev->height >> 1) ? : 1; + if (w != curr->width || h != curr->height) { + mIsComplete = false; + break; + } + prev = curr; + } + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +EGLSurfaceManager::EGLSurfaceManager() + : TokenManager(), mCount(0) +{ +} + +EGLSurfaceManager::~EGLSurfaceManager() +{ + // everything gets freed automatically here... +} + +sp EGLSurfaceManager::createTexture(GLuint name) +{ + sp result; + + Mutex::Autolock _l(mLock); + if (mTextures.indexOfKey(name) >= 0) + return result; // already exists! + + result = new EGLTextureObject(); + + status_t err = mTextures.add(name, result); + if (err < 0) + result.clear(); + + return result; +} + +sp EGLSurfaceManager::removeTexture(GLuint name) +{ + Mutex::Autolock _l(mLock); + const ssize_t index = mTextures.indexOfKey(name); + if (index >= 0) { + sp result(mTextures.valueAt(index)); + mTextures.removeItemsAt(index); + return result; + } + return 0; +} + +sp EGLSurfaceManager::replaceTexture(GLuint name) +{ + sp tex; + Mutex::Autolock _l(mLock); + const ssize_t index = mTextures.indexOfKey(name); + if (index >= 0) { + const sp& old = mTextures.valueAt(index); + const uint32_t refs = old->getStrongCount(); + if (ggl_likely(refs == 1)) { + // we're the only owner + tex = old; + } else { + // keep the texture's parameters + tex = new EGLTextureObject(); + tex->copyParameters(old); + mTextures.removeItemsAt(index); + mTextures.add(name, tex); + } + } + return tex; +} + +void EGLSurfaceManager::deleteTextures(GLsizei n, const GLuint *tokens) +{ + // free all textures + Mutex::Autolock _l(mLock); + for (GLsizei i=0 ; i EGLSurfaceManager::texture(GLuint name) +{ + Mutex::Autolock _l(mLock); + const ssize_t index = mTextures.indexOfKey(name); + if (index >= 0) + return mTextures.valueAt(index); + return 0; +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/opengl/libagl/TextureObjectManager.h b/opengl/libagl/TextureObjectManager.h new file mode 100644 index 000000000..74ed1a45b --- /dev/null +++ b/opengl/libagl/TextureObjectManager.h @@ -0,0 +1,140 @@ +/* +** Copyright 2006, 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. +*/ + +#ifndef ANDROID_OPENGLES_SURFACE_H +#define ANDROID_OPENGLES_SURFACE_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include "Tokenizer.h" +#include "TokenManager.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +class EGLTextureObject +{ +public: + EGLTextureObject(); + ~EGLTextureObject(); + + // protocol for sp<> + inline void incStrong(const void* id) const; + inline void decStrong(const void* id) const; + inline uint32_t getStrongCount() const; + + status_t setSurface(GGLSurface const* s); + status_t reallocate(GLint level, + int w, int h, int s, + int format, int compressedFormat, int bpr); + inline size_t size() const; + const GGLSurface& mip(int lod) const; + GGLSurface& editMip(int lod); + bool hasMipmaps() const { return mMipmaps!=0; } + bool isComplete() const { return mIsComplete; } + void copyParameters(const sp& old); + +private: + status_t allocateMipmaps(); + void freeMipmaps(); + void init(); + mutable int32_t mCount; + size_t mSize; + GGLSurface *mMipmaps; + int mNumExtraLod; + bool mIsComplete; + +public: + GGLSurface surface; + GLenum wraps; + GLenum wrapt; + GLenum min_filter; + GLenum mag_filter; + GLenum internalformat; + GLint crop_rect[4]; + GLint generate_mipmap; + GLint direct; +}; + +void EGLTextureObject::incStrong(const void* id) const { + android_atomic_inc(&mCount); +} +void EGLTextureObject::decStrong(const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete this; + } +} +uint32_t EGLTextureObject::getStrongCount() const { + return mCount; +} +size_t EGLTextureObject::size() const { + return mSize; +} + +// ---------------------------------------------------------------------------- + +class EGLSurfaceManager : public TokenManager +{ +public: + EGLSurfaceManager(); + ~EGLSurfaceManager(); + + // protocol for sp<> + inline void incStrong(const void* id) const; + inline void decStrong(const void* id) const; + typedef void weakref_type; + + sp createTexture(GLuint name); + sp removeTexture(GLuint name); + sp replaceTexture(GLuint name); + void deleteTextures(GLsizei n, const GLuint *tokens); + sp texture(GLuint name); + +private: + mutable int32_t mCount; + mutable Mutex mLock; + KeyedVector< GLuint, sp > mTextures; +}; + +void EGLSurfaceManager::incStrong(const void* id) const { + android_atomic_inc(&mCount); +} +void EGLSurfaceManager::decStrong(const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete this; + } +} + + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_OPENGLES_SURFACE_H + diff --git a/opengl/libagl/TokenManager.cpp b/opengl/libagl/TokenManager.cpp new file mode 100644 index 000000000..eea6025d5 --- /dev/null +++ b/opengl/libagl/TokenManager.cpp @@ -0,0 +1,62 @@ +/* libs/opengles/surface.cpp +** +** Copyright 2006, 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 "TokenManager.h" + +namespace android { +// ---------------------------------------------------------------------------- + +TokenManager::TokenManager() +{ + // token 0 is always reserved + mTokenizer.reserve(0); +} + +TokenManager::~TokenManager() +{ +} + +status_t TokenManager::getToken(GLsizei n, GLuint *tokens) +{ + Mutex::Autolock _l(mLock); + for (GLsizei i=0 ; i +#include +#include + +#include + +#include + +#include "Tokenizer.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +class TokenManager +{ +public: + TokenManager(); + ~TokenManager(); + + status_t getToken(GLsizei n, GLuint *tokens); + void recycleTokens(GLsizei n, const GLuint *tokens); + bool isTokenValid(GLuint token) const; + +private: + mutable Mutex mLock; + Tokenizer mTokenizer; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_OPENGLES_TOKEN_MANAGER_H + diff --git a/opengl/libagl/Tokenizer.cpp b/opengl/libagl/Tokenizer.cpp new file mode 100644 index 000000000..9b3ea1ac9 --- /dev/null +++ b/opengl/libagl/Tokenizer.cpp @@ -0,0 +1,173 @@ +/* libs/opengles/Tokenizer.cpp +** +** Copyright 2006, 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 "Tokenizer.h" + +// ---------------------------------------------------------------------------- + +namespace android { + +ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t) + +Tokenizer::Tokenizer() +{ +} + +Tokenizer::Tokenizer(const Tokenizer& other) + : mRanges(other.mRanges) +{ +} + +Tokenizer::~Tokenizer() +{ +} + +uint32_t Tokenizer::acquire() +{ + if (!mRanges.size() || mRanges[0].first) { + _insertTokenAt(0,0); + return 0; + } + + // just extend the first run + const run_t& run = mRanges[0]; + uint32_t token = run.first + run.length; + _insertTokenAt(token, 1); + return token; +} + +bool Tokenizer::isAcquired(uint32_t token) const +{ + return (_indexOrderOf(token) >= 0); +} + +status_t Tokenizer::reserve(uint32_t token) +{ + size_t o; + const ssize_t i = _indexOrderOf(token, &o); + if (i >= 0) { + return BAD_VALUE; // this token is already taken + } + ssize_t err = _insertTokenAt(token, o); + return (err<0) ? err : status_t(NO_ERROR); +} + +status_t Tokenizer::release(uint32_t token) +{ + const ssize_t i = _indexOrderOf(token); + if (i >= 0) { + const run_t& run = mRanges[i]; + if ((token >= run.first) && (token < run.first+run.length)) { + // token in this range, we need to split + run_t& run = mRanges.editItemAt(i); + if ((token == run.first) || (token == run.first+run.length-1)) { + if (token == run.first) { + run.first += 1; + } + run.length -= 1; + if (run.length == 0) { + // XXX: should we systematically remove a run that's empty? + mRanges.removeItemsAt(i); + } + } else { + // split the run + run_t new_run; + new_run.first = token+1; + new_run.length = run.first+run.length - new_run.first; + run.length = token - run.first; + mRanges.insertAt(new_run, i+1); + } + return NO_ERROR; + } + } + return NAME_NOT_FOUND; +} + +ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = mRanges.size()-1; + ssize_t mid; + const run_t* a = mRanges.array(); + while (l <= h) { + mid = l + (h - l)/2; + const run_t* const curr = a + mid; + int c = 0; + if (token < curr->first) c = 1; + else if (token >= curr->first+curr->length) c = -1; + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index) +{ + const size_t c = mRanges.size(); + + if (index >= 1) { + // do we need to merge with the previous run? + run_t& p = mRanges.editItemAt(index-1); + if (p.first+p.length == token) { + p.length += 1; + if (index < c) { + const run_t& n = mRanges[index]; + if (token+1 == n.first) { + p.length += n.length; + mRanges.removeItemsAt(index); + } + } + return index; + } + } + + if (index < c) { + // do we need to merge with the next run? + run_t& n = mRanges.editItemAt(index); + if (token+1 == n.first) { + n.first -= 1; + n.length += 1; + return index; + } + } + + return mRanges.insertAt(run_t(token,1), index); +} + +void Tokenizer::dump() const +{ + const run_t* ranges = mRanges.array(); + const size_t c = mRanges.size(); + LOGD("Tokenizer (%p, size = %u)\n", this, c); + for (size_t i=0 ; i +#include + +// ---------------------------------------------------------------------------- + +namespace android { + +class Tokenizer +{ +public: + Tokenizer(); + Tokenizer(const Tokenizer& other); + ~Tokenizer(); + + uint32_t acquire(); + status_t reserve(uint32_t token); + status_t release(uint32_t token); + bool isAcquired(uint32_t token) const; + + void dump() const; + + struct run_t { + run_t() {}; + run_t(uint32_t f, uint32_t l) : first(f), length(l) {} + uint32_t first; + uint32_t length; + }; +private: + ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const; + ssize_t _insertTokenAt(uint32_t token, size_t index); + Vector mRanges; +}; + +}; // namespace android + +// ---------------------------------------------------------------------------- + +#endif // ANDROID_OPENGLES_TOKENIZER_H diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp new file mode 100644 index 000000000..8fa7566aa --- /dev/null +++ b/opengl/libagl/array.cpp @@ -0,0 +1,1557 @@ +/* +** Copyright 2006, 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 "context.h" +#include "fp.h" +#include "state.h" +#include "matrix.h" +#include "vertex.h" +#include "light.h" +#include "primitives.h" +#include "texture.h" +#include "BufferObjectManager.h" + +// ---------------------------------------------------------------------------- + +#define VC_CACHE_STATISTICS 0 +#define VC_CACHE_TYPE_NONE 0 +#define VC_CACHE_TYPE_INDEXED 1 +#define VC_CACHE_TYPE_LRU 2 +#define VC_CACHE_TYPE VC_CACHE_TYPE_INDEXED + +#if VC_CACHE_STATISTICS +#include +#endif + +// ---------------------------------------------------------------------------- + +namespace android { + +static void validate_arrays(ogles_context_t* c, GLenum mode); + +static void compileElements__generic(ogles_context_t*, + vertex_t*, GLint, GLsizei); +static void compileElement__generic(ogles_context_t*, + vertex_t*, GLint); + +static void drawPrimitivesPoints(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesLineStrip(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesLineLoop(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesLines(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesTriangleStrip(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesTriangleFan(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesTriangles(ogles_context_t*, GLint, GLsizei); + +static void drawIndexedPrimitivesPoints(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesLineStrip(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesLineLoop(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesLines(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesTriangleStrip(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesTriangleFan(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesTriangles(ogles_context_t*, + GLsizei, const GLvoid*); + +// ---------------------------------------------------------------------------- + +typedef void (*arrays_prims_fct_t)(ogles_context_t*, GLint, GLsizei); +static const arrays_prims_fct_t drawArraysPrims[] = { + drawPrimitivesPoints, + drawPrimitivesLines, + drawPrimitivesLineLoop, + drawPrimitivesLineStrip, + drawPrimitivesTriangles, + drawPrimitivesTriangleStrip, + drawPrimitivesTriangleFan +}; + +typedef void (*elements_prims_fct_t)(ogles_context_t*, GLsizei, const GLvoid*); +static const elements_prims_fct_t drawElementsPrims[] = { + drawIndexedPrimitivesPoints, + drawIndexedPrimitivesLines, + drawIndexedPrimitivesLineLoop, + drawIndexedPrimitivesLineStrip, + drawIndexedPrimitivesTriangles, + drawIndexedPrimitivesTriangleStrip, + drawIndexedPrimitivesTriangleFan +}; + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void ogles_init_array(ogles_context_t* c) +{ + c->arrays.vertex.size = 4; + c->arrays.vertex.type = GL_FLOAT; + c->arrays.color.size = 4; + c->arrays.color.type = GL_FLOAT; + c->arrays.normal.size = 4; + c->arrays.normal.type = GL_FLOAT; + for (int i=0 ; iarrays.texture[i].size = 4; + c->arrays.texture[i].type = GL_FLOAT; + } + c->vc.init(); + + if (!c->vc.vBuffer) { + // this could have failed + ogles_error(c, GL_OUT_OF_MEMORY); + } +} + +void ogles_uninit_array(ogles_context_t* c) +{ + c->vc.uninit(); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Array fetchers +#endif + +static void currentColor(ogles_context_t* c, GLfixed* v, const GLvoid*) { + memcpy(v, c->current.color.v, sizeof(vec4_t)); +} +static void currentColor_clamp(ogles_context_t* c, GLfixed* v, const GLvoid*) { + memcpy(v, c->currentColorClamped.v, sizeof(vec4_t)); +} +static void currentNormal(ogles_context_t* c, GLfixed* v, const GLvoid*) { + memcpy(v, c->currentNormal.v, sizeof(vec3_t)); +} +static void currentTexCoord(ogles_context_t* c, GLfixed* v, const GLvoid*) { + memcpy(v, c->current.texture[c->arrays.tmu].v, sizeof(vec4_t)); +} + + +static void fetchNop(ogles_context_t*, GLfixed*, const GLvoid*) { +} +static void fetch2b(ogles_context_t*, GLfixed* v, const GLbyte* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); +} +static void fetch2s(ogles_context_t*, GLfixed* v, const GLshort* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); +} +static void fetch2x(ogles_context_t*, GLfixed* v, const GLfixed* p) { + memcpy(v, p, 2*sizeof(GLfixed)); +} +static void fetch2f(ogles_context_t*, GLfixed* v, const GLfloat* p) { + v[0] = gglFloatToFixed(p[0]); + v[1] = gglFloatToFixed(p[1]); +} +static void fetch3b(ogles_context_t*, GLfixed* v, const GLbyte* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); + v[2] = gglIntToFixed(p[2]); +} +static void fetch3s(ogles_context_t*, GLfixed* v, const GLshort* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); + v[2] = gglIntToFixed(p[2]); +} +static void fetch3x(ogles_context_t*, GLfixed* v, const GLfixed* p) { + memcpy(v, p, 3*sizeof(GLfixed)); +} +static void fetch3f(ogles_context_t*, GLfixed* v, const GLfloat* p) { + v[0] = gglFloatToFixed(p[0]); + v[1] = gglFloatToFixed(p[1]); + v[2] = gglFloatToFixed(p[2]); +} +static void fetch4b(ogles_context_t*, GLfixed* v, const GLbyte* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); + v[2] = gglIntToFixed(p[2]); + v[3] = gglIntToFixed(p[3]); +} +static void fetch4s(ogles_context_t*, GLfixed* v, const GLshort* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); + v[2] = gglIntToFixed(p[2]); + v[3] = gglIntToFixed(p[3]); +} +static void fetch4x(ogles_context_t*, GLfixed* v, const GLfixed* p) { + memcpy(v, p, 4*sizeof(GLfixed)); +} +static void fetch4f(ogles_context_t*, GLfixed* v, const GLfloat* p) { + v[0] = gglFloatToFixed(p[0]); + v[1] = gglFloatToFixed(p[1]); + v[2] = gglFloatToFixed(p[2]); + v[3] = gglFloatToFixed(p[3]); +} +static void fetchExpand4ub(ogles_context_t*, GLfixed* v, const GLubyte* p) { + v[0] = GGL_UB_TO_X(p[0]); + v[1] = GGL_UB_TO_X(p[1]); + v[2] = GGL_UB_TO_X(p[2]); + v[3] = GGL_UB_TO_X(p[3]); +} +static void fetchClamp4x(ogles_context_t*, GLfixed* v, const GLfixed* p) { + v[0] = gglClampx(p[0]); + v[1] = gglClampx(p[1]); + v[2] = gglClampx(p[2]); + v[3] = gglClampx(p[3]); +} +static void fetchClamp4f(ogles_context_t*, GLfixed* v, const GLfloat* p) { + v[0] = gglClampx(gglFloatToFixed(p[0])); + v[1] = gglClampx(gglFloatToFixed(p[1])); + v[2] = gglClampx(gglFloatToFixed(p[2])); + v[3] = gglClampx(gglFloatToFixed(p[3])); +} +static void fetchExpand3ub(ogles_context_t*, GLfixed* v, const GLubyte* p) { + v[0] = GGL_UB_TO_X(p[0]); + v[1] = GGL_UB_TO_X(p[1]); + v[2] = GGL_UB_TO_X(p[2]); + v[3] = 0x10000; +} +static void fetchClamp3x(ogles_context_t*, GLfixed* v, const GLfixed* p) { + v[0] = gglClampx(p[0]); + v[1] = gglClampx(p[1]); + v[2] = gglClampx(p[2]); + v[3] = 0x10000; +} +static void fetchClamp3f(ogles_context_t*, GLfixed* v, const GLfloat* p) { + v[0] = gglClampx(gglFloatToFixed(p[0])); + v[1] = gglClampx(gglFloatToFixed(p[1])); + v[2] = gglClampx(gglFloatToFixed(p[2])); + v[3] = 0x10000; +} +static void fetchExpand3b(ogles_context_t*, GLfixed* v, const GLbyte* p) { + v[0] = GGL_B_TO_X(p[0]); + v[1] = GGL_B_TO_X(p[1]); + v[2] = GGL_B_TO_X(p[2]); +} +static void fetchExpand3s(ogles_context_t*, GLfixed* v, const GLshort* p) { + v[0] = GGL_S_TO_X(p[0]); + v[1] = GGL_S_TO_X(p[1]); + v[2] = GGL_S_TO_X(p[2]); +} + +typedef array_t::fetcher_t fn_t; + +static const fn_t color_fct[2][16] = { // size={3,4}, type={ub,f,x} + { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0, + (fn_t)fetch3f, 0, 0, 0, 0, 0, + (fn_t)fetch3x }, + { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0, + (fn_t)fetch4f, 0, 0, 0, 0, 0, + (fn_t)fetch4x }, +}; +static const fn_t color_clamp_fct[2][16] = { // size={3,4}, type={ub,f,x} + { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0, + (fn_t)fetchClamp3f, 0, 0, 0, 0, 0, + (fn_t)fetchClamp3x }, + { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0, + (fn_t)fetchClamp4f, 0, 0, 0, 0, 0, + (fn_t)fetchClamp4x }, +}; +static const fn_t normal_fct[1][16] = { // size={3}, type={b,s,f,x} + { (fn_t)fetchExpand3b, 0, + (fn_t)fetchExpand3s, 0, 0, 0, + (fn_t)fetch3f, 0, 0, 0, 0, 0, + (fn_t)fetch3x }, +}; +static const fn_t vertex_fct[3][16] = { // size={2,3,4}, type={b,s,f,x} + { (fn_t)fetch2b, 0, + (fn_t)fetch2s, 0, 0, 0, + (fn_t)fetch2f, 0, 0, 0, 0, 0, + (fn_t)fetch3x }, + { (fn_t)fetch3b, 0, + (fn_t)fetch3s, 0, 0, 0, + (fn_t)fetch3f, 0, 0, 0, 0, 0, + (fn_t)fetch3x }, + { (fn_t)fetch4b, 0, + (fn_t)fetch4s, 0, 0, 0, + (fn_t)fetch4f, 0, 0, 0, 0, 0, + (fn_t)fetch4x } +}; +static const fn_t texture_fct[3][16] = { // size={2,3,4}, type={b,s,f,x} + { (fn_t)fetch2b, 0, + (fn_t)fetch2s, 0, 0, 0, + (fn_t)fetch2f, 0, 0, 0, 0, 0, + (fn_t)fetch2x }, + { (fn_t)fetch3b, 0, + (fn_t)fetch3s, 0, 0, 0, + (fn_t)fetch3f, 0, 0, 0, 0, 0, + (fn_t)fetch3x }, + { (fn_t)fetch4b, 0, + (fn_t)fetch4s, 0, 0, 0, + (fn_t)fetch4f, 0, 0, 0, 0, 0, + (fn_t)fetch4x } +}; + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark array_t +#endif + +void array_t::init( + GLint size, GLenum type, GLsizei stride, + const GLvoid *pointer, const buffer_t* bo, GLsizei count) +{ + if (!stride) { + stride = size; + switch (type) { + case GL_SHORT: + case GL_UNSIGNED_SHORT: + stride *= 2; + break; + case GL_FLOAT: + case GL_FIXED: + stride *= 4; + break; + } + } + this->size = size; + this->type = type; + this->stride = stride; + this->pointer = pointer; + this->bo = bo; + this->bounds = count; +} + +inline void array_t::resolve() +{ + physical_pointer = (bo) ? (bo->data + uintptr_t(pointer)) : pointer; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark vertex_cache_t +#endif + +void vertex_cache_t::init() +{ + // make sure the size of vertex_t allows cache-line alignment + CTA<(sizeof(vertex_t) & 0x1F) == 0> assertAlignedSize; + + const int align = 32; + const size_t s = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; + const size_t size = s*sizeof(vertex_t) + align; + base = malloc(size); + if (base) { + memset(base, 0, size); + vBuffer = (vertex_t*)((size_t(base) + align - 1) & ~(align-1)); + vCache = vBuffer + VERTEX_BUFFER_SIZE; + sequence = 0; + } +} + +void vertex_cache_t::uninit() +{ + free(base); + base = vBuffer = vCache = 0; +} + +void vertex_cache_t::clear() +{ +#if VC_CACHE_STATISTICS + startTime = systemTime(SYSTEM_TIME_THREAD); + total = 0; + misses = 0; +#endif + +#if VC_CACHE_TYPE == VC_CACHE_TYPE_LRU + vertex_t* v = vBuffer; + size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; + do { + v->mru = 0; + v++; + } while (--count); +#endif + + sequence += INDEX_SEQ; + if (sequence >= 0x80000000LU) { + sequence = INDEX_SEQ; + vertex_t* v = vBuffer; + size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; + do { + v->index = 0; + v++; + } while (--count); + } +} + +void vertex_cache_t::dump_stats(GLenum mode) +{ +#if VC_CACHE_STATISTICS + nsecs_t time = systemTime(SYSTEM_TIME_THREAD) - startTime; + uint32_t hits = total - misses; + uint32_t prim_count; + switch (mode) { + case GL_POINTS: prim_count = total; break; + case GL_LINE_STRIP: prim_count = total - 1; break; + case GL_LINE_LOOP: prim_count = total - 1; break; + case GL_LINES: prim_count = total / 2; break; + case GL_TRIANGLE_STRIP: prim_count = total - 2; break; + case GL_TRIANGLE_FAN: prim_count = total - 2; break; + case GL_TRIANGLES: prim_count = total / 3; break; + default: return; + } + printf( "total=%5u, hits=%5u, miss=%5u, hitrate=%3u%%," + " prims=%5u, time=%6u us, prims/s=%d, v/t=%f\n", + total, hits, misses, (hits*100)/total, + prim_count, int(ns2us(time)), int(prim_count*float(seconds(1))/time), + float(misses) / prim_count); +#endif +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +static __attribute__((noinline)) +void enableDisableClientState(ogles_context_t* c, GLenum array, bool enable) +{ + const int tmu = c->arrays.activeTexture; + array_t* a; + switch (array) { + case GL_COLOR_ARRAY: a = &c->arrays.color; break; + case GL_NORMAL_ARRAY: a = &c->arrays.normal; break; + case GL_TEXTURE_COORD_ARRAY: a = &c->arrays.texture[tmu]; break; + case GL_VERTEX_ARRAY: a = &c->arrays.vertex; break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + a->enable = enable ? GL_TRUE : GL_FALSE; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Vertex Cache +#endif + +static __attribute__((noinline)) +vertex_t* cache_vertex(ogles_context_t* c, vertex_t* v, uint32_t index) +{ + #if VC_CACHE_STATISTICS + c->vc.misses++; + #endif + if (ggl_unlikely(v->locked)) { + // we're just looking for an entry in the cache that is not locked. + // and we know that there cannot be more than 2 locked entries + // because a triangle needs at most 3 vertices. + // We never use the first and second entries because they might be in + // use by the striper or faner. Any other entry will do as long as + // it's not locked. + // We compute directly the index of a "free" entry from the locked + // state of v[2] and v[3]. + v = c->vc.vBuffer + 2; + v += v[0].locked | (v[1].locked<<1); + } + // note: compileElement clears v->flags + c->arrays.compileElement(c, v, index); + v->locked = 1; + return v; +} + +static __attribute__((noinline)) +vertex_t* fetch_vertex(ogles_context_t* c, size_t index) +{ + index |= c->vc.sequence; + +#if VC_CACHE_TYPE == VC_CACHE_TYPE_INDEXED + + vertex_t* const v = c->vc.vCache + + (index & (vertex_cache_t::VERTEX_CACHE_SIZE-1)); + + if (ggl_likely(v->index == index)) { + v->locked = 1; + return v; + } + return cache_vertex(c, v, index); + +#elif VC_CACHE_TYPE == VC_CACHE_TYPE_LRU + + vertex_t* v = c->vc.vCache + + (index & ((vertex_cache_t::VERTEX_CACHE_SIZE-1)>>1))*2; + + // always record LRU in v[0] + if (ggl_likely(v[0].index == index)) { + v[0].locked = 1; + v[0].mru = 0; + return &v[0]; + } + + if (ggl_likely(v[1].index == index)) { + v[1].locked = 1; + v[0].mru = 1; + return &v[1]; + } + + const int lru = 1 - v[0].mru; + v[0].mru = lru; + return cache_vertex(c, &v[lru], index); + +#elif VC_CACHE_TYPE == VC_CACHE_TYPE_NONE + + // just for debugging... + vertex_t* v = c->vc.vBuffer + 2; + return cache_vertex(c, v, index); + +#endif +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Primitive Assembly +#endif + +void drawPrimitivesPoints(ogles_context_t* c, GLint first, GLsizei count) +{ + if (ggl_unlikely(count < 1)) + return; + + // vertex cache size must be multiple of 1 + const GLsizei vcs = + (vertex_cache_t::VERTEX_BUFFER_SIZE + + vertex_cache_t::VERTEX_CACHE_SIZE); + do { + vertex_t* v = c->vc.vBuffer; + GLsizei num = count > vcs ? vcs : count; + c->arrays.cull = vertex_t::CLIP_ALL; + c->arrays.compileElements(c, v, first, num); + first += num; + count -= num; + if (!c->arrays.cull) { + // quick/trivial reject of the whole batch + do { + const uint32_t cc = v[0].flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderPoint(c, v); + v++; + num--; + } while (num); + } + } while (count); +} + +// ---------------------------------------------------------------------------- + +void drawPrimitivesLineStrip(ogles_context_t* c, GLint first, GLsizei count) +{ + if (ggl_unlikely(count < 2)) + return; + + vertex_t *v, *v0, *v1; + c->arrays.cull = vertex_t::CLIP_ALL; + c->arrays.compileElement(c, c->vc.vBuffer, first); + first += 1; + count -= 1; + + // vertex cache size must be multiple of 1 + const GLsizei vcs = + (vertex_cache_t::VERTEX_BUFFER_SIZE + + vertex_cache_t::VERTEX_CACHE_SIZE - 1); + do { + v0 = c->vc.vBuffer + 0; + v = c->vc.vBuffer + 1; + GLsizei num = count > vcs ? vcs : count; + c->arrays.compileElements(c, v, first, num); + first += num; + count -= num; + if (!c->arrays.cull) { + // quick/trivial reject of the whole batch + do { + v1 = v++; + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); + v0 = v1; + num--; + } while (num); + } + // copy back the last processed vertex + c->vc.vBuffer[0] = *v0; + c->arrays.cull = v0->flags & vertex_t::CLIP_ALL; + } while (count); +} + +void drawPrimitivesLineLoop(ogles_context_t* c, GLint first, GLsizei count) +{ + if (ggl_unlikely(count < 2)) + return; + drawPrimitivesLineStrip(c, first, count); + if (ggl_likely(count >= 3)) { + vertex_t* v0 = c->vc.vBuffer; + vertex_t* v1 = c->vc.vBuffer + 1; + c->arrays.compileElement(c, v1, first); + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); + } +} + +void drawPrimitivesLines(ogles_context_t* c, GLint first, GLsizei count) +{ + if (ggl_unlikely(count < 2)) + return; + + // vertex cache size must be multiple of 2 + const GLsizei vcs = + ((vertex_cache_t::VERTEX_BUFFER_SIZE + + vertex_cache_t::VERTEX_CACHE_SIZE) / 2) * 2; + do { + vertex_t* v = c->vc.vBuffer; + GLsizei num = count > vcs ? vcs : count; + c->arrays.cull = vertex_t::CLIP_ALL; + c->arrays.compileElements(c, v, first, num); + first += num; + count -= num; + if (!c->arrays.cull) { + // quick/trivial reject of the whole batch + num -= 2; + do { + const uint32_t cc = v[0].flags & v[1].flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v, v+1); + v += 2; + num -= 2; + } while (num >= 0); + } + } while (count >= 2); +} + +// ---------------------------------------------------------------------------- + +static void drawPrimitivesTriangleFanOrStrip(ogles_context_t* c, + GLint first, GLsizei count, int winding) +{ + // winding == 2 : fan + // winding == 1 : strip + + if (ggl_unlikely(count < 3)) + return; + + vertex_t *v, *v0, *v1, *v2; + c->arrays.cull = vertex_t::CLIP_ALL; + c->arrays.compileElements(c, c->vc.vBuffer, first, 2); + first += 2; + count -= 2; + + // vertex cache size must be multiple of 2. This is extremely important + // because it allows us to preserve the same winding when the whole + // batch is culled. We also need 2 extra vertices in the array, because + // we always keep the two first ones. + const GLsizei vcs = + ((vertex_cache_t::VERTEX_BUFFER_SIZE + + vertex_cache_t::VERTEX_CACHE_SIZE - 2) / 2) * 2; + do { + v0 = c->vc.vBuffer + 0; + v1 = c->vc.vBuffer + 1; + v = c->vc.vBuffer + 2; + GLsizei num = count > vcs ? vcs : count; + c->arrays.compileElements(c, v, first, num); + first += num; + count -= num; + if (!c->arrays.cull) { + // quick/trivial reject of the whole batch + do { + v2 = v++; + const uint32_t cc = v0->flags & v1->flags & v2->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderTriangle(c, v0, v1, v2); + swap(((winding^=1) ? v1 : v0), v2); + num--; + } while (num); + } + if (count) { + v0 = c->vc.vBuffer + 2 + num - 2; + v1 = c->vc.vBuffer + 2 + num - 1; + if ((winding&2) == 0) { + // for strips copy back the two last compiled vertices + c->vc.vBuffer[0] = *v0; + } + c->vc.vBuffer[1] = *v1; + c->arrays.cull = v0->flags & v1->flags & vertex_t::CLIP_ALL; + } + } while (count > 0); +} + +void drawPrimitivesTriangleStrip(ogles_context_t* c, + GLint first, GLsizei count) { + drawPrimitivesTriangleFanOrStrip(c, first, count, 1); +} + +void drawPrimitivesTriangleFan(ogles_context_t* c, + GLint first, GLsizei count) { + drawPrimitivesTriangleFanOrStrip(c, first, count, 2); +} + +void drawPrimitivesTriangles(ogles_context_t* c, GLint first, GLsizei count) +{ + if (ggl_unlikely(count < 3)) + return; + + // vertex cache size must be multiple of 3 + const GLsizei vcs = + ((vertex_cache_t::VERTEX_BUFFER_SIZE + + vertex_cache_t::VERTEX_CACHE_SIZE) / 3) * 3; + do { + vertex_t* v = c->vc.vBuffer; + GLsizei num = count > vcs ? vcs : count; + c->arrays.cull = vertex_t::CLIP_ALL; + c->arrays.compileElements(c, v, first, num); + first += num; + count -= num; + if (!c->arrays.cull) { + // quick/trivial reject of the whole batch + num -= 3; + do { + const uint32_t cc = v[0].flags & v[1].flags & v[2].flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderTriangle(c, v, v+1, v+2); + v += 3; + num -= 3; + } while (num >= 0); + } + } while (count >= 3); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +// this looks goofy, but gcc does a great job with this... +static inline unsigned int read_index(int type, const GLvoid*& p) { + unsigned int r; + if (type) { + r = *(const GLubyte*)p; + p = (const GLubyte*)p + 1; + } else { + r = *(const GLushort*)p; + p = (const GLushort*)p + 1; + } + return r; +} + +// ---------------------------------------------------------------------------- + +void drawIndexedPrimitivesPoints(ogles_context_t* c, + GLsizei count, const GLvoid *indices) +{ + if (ggl_unlikely(count < 1)) + return; + const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); + do { + vertex_t * v = fetch_vertex(c, read_index(type, indices)); + if (ggl_likely(!(v->flags & vertex_t::CLIP_ALL))) + c->prims.renderPoint(c, v); + v->locked = 0; + count--; + } while(count); +} + +// ---------------------------------------------------------------------------- + +void drawIndexedPrimitivesLineStrip(ogles_context_t* c, + GLsizei count, const GLvoid *indices) +{ + if (ggl_unlikely(count < 2)) + return; + + vertex_t * const v = c->vc.vBuffer; + vertex_t* v0 = v; + vertex_t* v1; + + const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); + c->arrays.compileElement(c, v0, read_index(type, indices)); + count -= 1; + do { + v1 = fetch_vertex(c, read_index(type, indices)); + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); + v0->locked = 0; + v0 = v1; + count--; + } while (count); + v1->locked = 0; +} + +void drawIndexedPrimitivesLineLoop(ogles_context_t* c, + GLsizei count, const GLvoid *indices) +{ + if (ggl_unlikely(count <= 2)) { + drawIndexedPrimitivesLines(c, count, indices); + return; + } + + vertex_t * const v = c->vc.vBuffer; + vertex_t* v0 = v; + vertex_t* v1; + + const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); + c->arrays.compileElement(c, v0, read_index(type, indices)); + count -= 1; + do { + v1 = fetch_vertex(c, read_index(type, indices)); + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); + v0->locked = 0; + v0 = v1; + count--; + } while (count); + v1->locked = 0; + + v1 = c->vc.vBuffer; + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); +} + +void drawIndexedPrimitivesLines(ogles_context_t* c, + GLsizei count, const GLvoid *indices) +{ + if (ggl_unlikely(count < 2)) + return; + + count -= 2; + const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); + do { + vertex_t* const v0 = fetch_vertex(c, read_index(type, indices)); + vertex_t* const v1 = fetch_vertex(c, read_index(type, indices)); + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); + v0->locked = 0; + v1->locked = 0; + count -= 2; + } while (count >= 0); +} + +// ---------------------------------------------------------------------------- + +static void drawIndexedPrimitivesTriangleFanOrStrip(ogles_context_t* c, + GLsizei count, const GLvoid *indices, int winding) +{ + // winding == 2 : fan + // winding == 1 : strip + + if (ggl_unlikely(count < 3)) + return; + + vertex_t * const v = c->vc.vBuffer; + vertex_t* v0 = v; + vertex_t* v1 = v+1; + vertex_t* v2; + + const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); + c->arrays.compileElement(c, v0, read_index(type, indices)); + c->arrays.compileElement(c, v1, read_index(type, indices)); + count -= 2; + + // note: GCC 4.1.1 here makes a prety interesting optimization + // where it duplicates the loop below based on c->arrays.indicesType + + do { + v2 = fetch_vertex(c, read_index(type, indices)); + const uint32_t cc = v0->flags & v1->flags & v2->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderTriangle(c, v0, v1, v2); + vertex_t* & consumed = ((winding^=1) ? v1 : v0); + consumed->locked = 0; + consumed = v2; + count--; + } while (count); + v0->locked = v1->locked = 0; + v2->locked = 0; +} + +void drawIndexedPrimitivesTriangleStrip(ogles_context_t* c, + GLsizei count, const GLvoid *indices) { + drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 1); +} + +void drawIndexedPrimitivesTriangleFan(ogles_context_t* c, + GLsizei count, const GLvoid *indices) { + drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 2); +} + +void drawIndexedPrimitivesTriangles(ogles_context_t* c, + GLsizei count, const GLvoid *indices) +{ + if (ggl_unlikely(count < 3)) + return; + + count -= 3; + if (ggl_likely(c->arrays.indicesType == GL_UNSIGNED_SHORT)) { + // This case is probably our most common case... + uint16_t const * p = (uint16_t const *)indices; + do { + vertex_t* const v0 = fetch_vertex(c, *p++); + vertex_t* const v1 = fetch_vertex(c, *p++); + vertex_t* const v2 = fetch_vertex(c, *p++); + const uint32_t cc = v0->flags & v1->flags & v2->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderTriangle(c, v0, v1, v2); + v0->locked = 0; + v1->locked = 0; + v2->locked = 0; + count -= 3; + } while (count >= 0); + } else { + uint8_t const * p = (uint8_t const *)indices; + do { + vertex_t* const v0 = fetch_vertex(c, *p++); + vertex_t* const v1 = fetch_vertex(c, *p++); + vertex_t* const v2 = fetch_vertex(c, *p++); + const uint32_t cc = v0->flags & v1->flags & v2->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderTriangle(c, v0, v1, v2); + v0->locked = 0; + v1->locked = 0; + v2->locked = 0; + count -= 3; + } while (count >= 0); + } +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Array compilers +#endif + +void compileElement__generic(ogles_context_t* c, + vertex_t* v, GLint first) +{ + v->flags = 0; + v->index = first; + first &= vertex_cache_t::INDEX_MASK; + const GLubyte* vp = c->arrays.vertex.element(first); + c->arrays.vertex.fetch(c, v->obj.v, vp); + c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj); + c->arrays.perspective(c, v); +} + +void compileElements__generic(ogles_context_t* c, + vertex_t* v, GLint first, GLsizei count) +{ + const GLubyte* vp = c->arrays.vertex.element( + first & vertex_cache_t::INDEX_MASK); + const size_t stride = c->arrays.vertex.stride; + transform_t const* const mvp = &c->transforms.mvp; + do { + v->flags = 0; + v->index = first++; + c->arrays.vertex.fetch(c, v->obj.v, vp); + c->arrays.mvp_transform(mvp, &v->clip, &v->obj); + c->arrays.perspective(c, v); + vp += stride; + v++; + } while (--count); +} + +/* +void compileElements__3x_full(ogles_context_t* c, + vertex_t* v, GLint first, GLsizei count) +{ + const GLfixed* vp = (const GLfixed*)c->arrays.vertex.element(first); + const size_t stride = c->arrays.vertex.stride / 4; +// const GLfixed* const& m = c->transforms.mvp.matrix.m; + + GLfixed m[16]; + memcpy(&m, c->transforms.mvp.matrix.m, sizeof(m)); + + do { + const GLfixed rx = vp[0]; + const GLfixed ry = vp[1]; + const GLfixed rz = vp[2]; + vp += stride; + v->index = first++; + v->clip.x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); + v->clip.y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]); + v->clip.z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]); + v->clip.w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]); + + const GLfixed w = v->clip.w; + uint32_t clip = 0; + if (v->clip.x < -w) clip |= vertex_t::CLIP_L; + if (v->clip.x > w) clip |= vertex_t::CLIP_R; + if (v->clip.y < -w) clip |= vertex_t::CLIP_B; + if (v->clip.y > w) clip |= vertex_t::CLIP_T; + if (v->clip.z < -w) clip |= vertex_t::CLIP_N; + if (v->clip.z > w) clip |= vertex_t::CLIP_F; + v->flags = clip; + c->arrays.cull &= clip; + + //c->arrays.perspective(c, v); + v++; + } while (--count); +} +*/ + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark clippers +#endif + +static void clipVec4(vec4_t& nv, + GLfixed t, const vec4_t& s, const vec4_t& p) +{ + for (int i=0; i<4 ; i++) + nv.v[i] = gglMulAddx(t, s.v[i] - p.v[i], p.v[i], 28); +} + +static void clipVertex(ogles_context_t* c, vertex_t* nv, + GLfixed t, const vertex_t* s, const vertex_t* p) +{ + clipVec4(nv->clip, t, s->clip, p->clip); + nv->fog = gglMulAddx(t, s->fog - p->fog, p->fog, 28); + ogles_vertex_project(c, nv); + nv->flags |= vertex_t::LIT | vertex_t::EYE | vertex_t::TT; + nv->flags &= ~vertex_t::CLIP_ALL; +} + +static void clipVertexC(ogles_context_t* c, vertex_t* nv, + GLfixed t, const vertex_t* s, const vertex_t* p) +{ + clipVec4(nv->color, t, s->color, p->color); + clipVertex(c, nv, t, s, p); +} + +static void clipVertexT(ogles_context_t* c, vertex_t* nv, + GLfixed t, const vertex_t* s, const vertex_t* p) +{ + for (int i=0 ; irasterizer.state.texture[i].enable) + clipVec4(nv->texture[i], t, s->texture[i], p->texture[i]); + } + clipVertex(c, nv, t, s, p); +} + +static void clipVertexAll(ogles_context_t* c, vertex_t* nv, + GLfixed t, const vertex_t* s, const vertex_t* p) +{ + clipVec4(nv->color, t, s->color, p->color); + clipVertexT(c, nv, t, s, p); +} + +static void clipEye(ogles_context_t* c, vertex_t* nv, + GLfixed t, const vertex_t* s, const vertex_t* p) +{ + nv->clear(); + c->arrays.clipVertex(c, nv, t, p, s); + clipVec4(nv->eye, t, s->eye, p->eye); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void validate_arrays(ogles_context_t* c, GLenum mode) +{ + uint32_t enables = c->rasterizer.state.enables; + + // Perspective correction is not need if Ortho transform, but + // the user can still provide the w coordinate manually, so we can't + // automatically turn it off (in fact we could when the 4th coordinate + // is not spcified in the vertex array). + // W interpolation is never needed for points. + GLboolean perspective = + c->perspective && mode!=GL_POINTS && (enables & GGL_ENABLE_TMUS); + c->rasterizer.procs.enableDisable(c, GGL_W_LERP, perspective); + + // set anti-aliasing + GLboolean smooth = GL_FALSE; + switch (mode) { + case GL_POINTS: + smooth = c->point.smooth; + break; + case GL_LINES: + case GL_LINE_LOOP: + case GL_LINE_STRIP: + smooth = c->line.smooth; + break; + } + if (((enables & GGL_ENABLE_AA)?1:0) != smooth) + c->rasterizer.procs.enableDisable(c, GGL_AA, smooth); + + // set the shade model for this primitive + c->rasterizer.procs.shadeModel(c, + (mode == GL_POINTS) ? GL_FLAT : c->lighting.shadeModel); + + // compute all the matrices we'll need... + uint32_t want = + transform_state_t::MVP | + transform_state_t::VIEWPORT; + if (c->lighting.enable) { // needs normal transforms and eye coords + want |= transform_state_t::MVUI; + want |= transform_state_t::MODELVIEW; + } + if (enables & GGL_ENABLE_TMUS) { // needs texture transforms + want |= transform_state_t::TEXTURE; + } + if (c->clipPlanes.enable || (enables & GGL_ENABLE_FOG)) { + want |= transform_state_t::MODELVIEW; // needs eye coords + } + ogles_validate_transform(c, want); + + // textures... + if (enables & GGL_ENABLE_TMUS) + ogles_validate_texture(c); + + // vertex compilers + c->arrays.compileElement = compileElement__generic; + c->arrays.compileElements = compileElements__generic; + + // vertex transform + c->arrays.mvp_transform = + c->transforms.mvp.pointv[c->arrays.vertex.size - 2]; + + c->arrays.mv_transform = + c->transforms.modelview.transform.pointv[c->arrays.vertex.size - 2]; + + /* + * *********************************************************************** + * pick fetchers + * *********************************************************************** + */ + + array_machine_t& am = c->arrays; + am.vertex.fetch = fetchNop; + am.normal.fetch = currentNormal; + am.color.fetch = currentColor; + + if (am.vertex.enable) { + am.vertex.resolve(); + if (am.vertex.bo || am.vertex.pointer) { + am.vertex.fetch = vertex_fct[am.vertex.size-2][am.vertex.type & 0xF]; + } + } + + if (am.normal.enable) { + am.normal.resolve(); + if (am.normal.bo || am.normal.pointer) { + am.normal.fetch = normal_fct[am.normal.size-3][am.normal.type & 0xF]; + } + } + + if (am.color.enable) { + am.color.resolve(); + if (c->lighting.enable) { + if (am.color.bo || am.color.pointer) { + am.color.fetch = color_fct[am.color.size-3][am.color.type & 0xF]; + } + } else { + if (am.color.bo || am.color.pointer) { + am.color.fetch = color_clamp_fct[am.color.size-3][am.color.type & 0xF]; + } + } + } + + int activeTmuCount = 0; + for (int i=0 ; irasterizer.state.texture[i].enable) { + + // texture fetchers... + if (am.texture[i].enable) { + am.texture[i].resolve(); + if (am.texture[i].bo || am.texture[i].pointer) { + am.texture[i].fetch = texture_fct[am.texture[i].size-2][am.texture[i].type & 0xF]; + } + } + + // texture transform... + const int index = c->arrays.texture[i].size - 2; + c->arrays.tex_transform[i] = + c->transforms.texture[i].transform.pointv[index]; + + am.tmu = i; + activeTmuCount++; + } + } + + // pick the vertex-clipper + uint32_t clipper = 0; + // we must reload 'enables' here + enables = c->rasterizer.state.enables; + if (enables & GGL_ENABLE_SMOOTH) + clipper |= 1; // we need to interpolate colors + if (enables & GGL_ENABLE_TMUS) + clipper |= 2; // we need to interpolate textures + switch (clipper) { + case 0: c->arrays.clipVertex = clipVertex; break; + case 1: c->arrays.clipVertex = clipVertexC; break; + case 2: c->arrays.clipVertex = clipVertexT; break; + case 3: c->arrays.clipVertex = clipVertexAll; break; + } + c->arrays.clipEye = clipEye; + + // pick the primitive rasterizer + ogles_validate_primitives(c); +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + +#if 0 +#pragma mark - +#pragma mark array API +#endif + +void glVertexPointer( + GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) +{ + ogles_context_t* c = ogles_context_t::get(); + if (size<2 || size>4 || stride<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (type) { + case GL_BYTE: + case GL_SHORT: + case GL_FIXED: + case GL_FLOAT: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->arrays.vertex.init(size, type, stride, pointer, c->arrays.array_buffer, 0); +} + +void glColorPointer( + GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) +{ + ogles_context_t* c = ogles_context_t::get(); + // in theory ogles doesn't allow color arrays of size 3 + // but it is very useful to 'visualize' the normal array. + if (size<3 || size>4 || stride<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (type) { + case GL_UNSIGNED_BYTE: + case GL_FIXED: + case GL_FLOAT: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->arrays.color.init(size, type, stride, pointer, c->arrays.array_buffer, 0); +} + +void glNormalPointer( + GLenum type, GLsizei stride, const GLvoid *pointer) +{ + ogles_context_t* c = ogles_context_t::get(); + if (stride<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (type) { + case GL_BYTE: + case GL_SHORT: + case GL_FIXED: + case GL_FLOAT: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->arrays.normal.init(3, type, stride, pointer, c->arrays.array_buffer, 0); +} + +void glTexCoordPointer( + GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) +{ + ogles_context_t* c = ogles_context_t::get(); + if (size<2 || size>4 || stride<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (type) { + case GL_BYTE: + case GL_SHORT: + case GL_FIXED: + case GL_FLOAT: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + const int tmu = c->arrays.activeTexture; + c->arrays.texture[tmu].init(size, type, stride, pointer, + c->arrays.array_buffer, 0); +} + + +void glEnableClientState(GLenum array) { + ogles_context_t* c = ogles_context_t::get(); + enableDisableClientState(c, array, true); +} + +void glDisableClientState(GLenum array) { + ogles_context_t* c = ogles_context_t::get(); + enableDisableClientState(c, array, false); +} + +void glClientActiveTexture(GLenum texture) +{ + ogles_context_t* c = ogles_context_t::get(); + if (texture=GL_TEXTURE0+GGL_TEXTURE_UNIT_COUNT) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->arrays.activeTexture = texture - GL_TEXTURE0; +} + +void glDrawArrays(GLenum mode, GLint first, GLsizei count) +{ + ogles_context_t* c = ogles_context_t::get(); + if (count<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (mode) { + case GL_POINTS: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + case GL_LINES: + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_TRIANGLES: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + + if (count == 0 || !c->arrays.vertex.enable) + return; + if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK)) + return; // all triangles are culled + + validate_arrays(c, mode); + drawArraysPrims[mode](c, first, count); + +#if VC_CACHE_STATISTICS + c->vc.total = count; + c->vc.dump_stats(mode); +#endif +} + +void glDrawElements( + GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) +{ + ogles_context_t* c = ogles_context_t::get(); + if (count<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (mode) { + case GL_POINTS: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + case GL_LINES: + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_TRIANGLES: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + switch (type) { + case GL_UNSIGNED_BYTE: + case GL_UNSIGNED_SHORT: + c->arrays.indicesType = type; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (count == 0 || !c->arrays.vertex.enable) + return; + if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK)) + return; // all triangles are culled + + // clear the vertex-cache + c->vc.clear(); + validate_arrays(c, mode); + + // if indices are in a buffer object, the pointer is treated as an + // offset in that buffer. + if (c->arrays.element_array_buffer) { + indices = c->arrays.element_array_buffer->data + uintptr_t(indices); + } + + drawElementsPrims[mode](c, count, indices); + +#if VC_CACHE_STATISTICS + c->vc.total = count; + c->vc.dump_stats(mode); +#endif +} + +// ---------------------------------------------------------------------------- +// buffers +// ---------------------------------------------------------------------------- + +void glBindBuffer(GLenum target, GLuint buffer) +{ + ogles_context_t* c = ogles_context_t::get(); + if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + // create a buffer object, or bind an existing one + buffer_t const* bo = 0; + if (buffer) { + bo = c->bufferObjectManager->bind(buffer); + if (!bo) { + ogles_error(c, GL_OUT_OF_MEMORY); + return; + } + } + ((target == GL_ARRAY_BUFFER) ? + c->arrays.array_buffer : c->arrays.element_array_buffer) = bo; +} + +void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) +{ + ogles_context_t* c = ogles_context_t::get(); + if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (size<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if ((usage!=GL_STATIC_DRAW) && (usage!=GL_DYNAMIC_DRAW)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ? + c->arrays.array_buffer : c->arrays.element_array_buffer); + + if (bo == 0) { + // can't modify buffer 0 + ogles_error(c, GL_INVALID_OPERATION); + return; + } + + buffer_t* edit_bo = const_cast(bo); + if (c->bufferObjectManager->allocateStore(edit_bo, size, usage) != 0) { + ogles_error(c, GL_OUT_OF_MEMORY); + return; + } + if (data) { + memcpy(bo->data, data, size); + } +} + +void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) +{ + ogles_context_t* c = ogles_context_t::get(); + if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (offset<0 || size<0 || data==0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ? + c->arrays.array_buffer : c->arrays.element_array_buffer); + + if (bo == 0) { + // can't modify buffer 0 + ogles_error(c, GL_INVALID_OPERATION); + return; + } + if (offset+size > bo->size) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + memcpy(bo->data + offset, data, size); +} + +void glDeleteBuffers(GLsizei n, const GLuint* buffers) +{ + ogles_context_t* c = ogles_context_t::get(); + if (n<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + for (int i=0 ; iarrays.element_array_buffer->name == name) { + c->arrays.element_array_buffer = 0; + } + if (c->arrays.array_buffer->name == name) { + c->arrays.array_buffer = 0; + } + if (c->arrays.vertex.bo->name == name) { + c->arrays.vertex.bo = 0; + } + if (c->arrays.normal.bo->name == name) { + c->arrays.normal.bo = 0; + } + if (c->arrays.color.bo->name == name) { + c->arrays.color.bo = 0; + } + for (int t=0 ; tarrays.texture[t].bo->name == name) { + c->arrays.texture[t].bo = 0; + } + } + } + } + c->bufferObjectManager->deleteBuffers(n, buffers); + c->bufferObjectManager->recycleTokens(n, buffers); +} + +void glGenBuffers(GLsizei n, GLuint* buffers) +{ + ogles_context_t* c = ogles_context_t::get(); + if (n<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + c->bufferObjectManager->getToken(n, buffers); +} diff --git a/opengl/libagl/array.h b/opengl/libagl/array.h new file mode 100644 index 000000000..e15697860 --- /dev/null +++ b/opengl/libagl/array.h @@ -0,0 +1,37 @@ +/* libs/opengles/array.h +** +** Copyright 2006, 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. +*/ + +#ifndef ANDROID_OPENGLES_ARRAY_H +#define ANDROID_OPENGLES_ARRAY_H + +#include +#include +#include + +namespace android { + +namespace gl { +struct ogles_context_t; +}; + +void ogles_init_array(ogles_context_t* c); +void ogles_uninit_array(ogles_context_t* c); + +}; // namespace android + +#endif // ANDROID_OPENGLES_ARRAY_H + diff --git a/opengl/libagl/context.h b/opengl/libagl/context.h new file mode 100644 index 000000000..ef36b569d --- /dev/null +++ b/opengl/libagl/context.h @@ -0,0 +1,20 @@ +/* libs/opengles/context.h +** +** Copyright 2006, 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 + +using namespace android::gl; diff --git a/opengl/libagl/dxt.cpp b/opengl/libagl/dxt.cpp new file mode 100644 index 000000000..238c81fae --- /dev/null +++ b/opengl/libagl/dxt.cpp @@ -0,0 +1,636 @@ +/* libs/opengles/dxt.cpp +** +** Copyright 2007, 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. +*/ + +#define TIMING 0 + +#if TIMING +#include // for optimization timing +#include +#include +#endif + +#include +#include + +#include "context.h" + +#define TIMING 0 + +namespace android { + +static uint8_t avg23tab[64*64]; +static volatile int tables_initialized = 0; + +// Definitions below are equivalent to these over the valid range of arguments +// #define div5(x) ((x)/5) +// #define div7(x) ((x)/7) + +// Use fixed-point to divide by 5 and 7 +// 3277 = 2^14/5 + 1 +// 2341 = 2^14/7 + 1 +#define div5(x) (((x)*3277) >> 14) +#define div7(x) (((x)*2341) >> 14) + +// Table with entry [a << 6 | b] = (2*a + b)/3 for 0 <= a,b < 64 +#define avg23(x0,x1) avg23tab[((x0) << 6) | (x1)] + +// Extract 5/6/5 RGB +#define red(x) (((x) >> 11) & 0x1f) +#define green(x) (((x) >> 5) & 0x3f) +#define blue(x) ( (x) & 0x1f) + +/* + * Convert 5/6/5 RGB (as 3 ints) to 8/8/8 + * + * Operation count: 8 <<, 0 &, 5 | + */ +inline static int rgb565SepTo888(int r, int g, int b) + +{ + return ((((r << 3) | (r >> 2)) << 16) | + (((g << 2) | (g >> 4)) << 8) | + ((b << 3) | (b >> 2))); +} + +/* + * Convert 5/6/5 RGB (as a single 16-bit word) to 8/8/8 + * + * r4r3r2r1 r0g5g4g3 g2g1g0b4 b3b2b1b0 rgb + * r4r3r2 r1r0g5g4 g3g2g1g0 b4b3b2b1 b0 0 0 0 rgb << 3 + * r4r3r2r1 r0r4r3r2 g5g4g3g2 g1g0g5g4 b4b3b2b1 b0b4b3b2 desired result + * + * Construct the 24-bit RGB word as: + * + * r4r3r2r1 r0------ -------- -------- -------- -------- (rgb << 8) & 0xf80000 + * r4r3r2 -------- -------- -------- -------- (rgb << 3) & 0x070000 + * g5g4g3g2 g1g0---- -------- -------- (rgb << 5) & 0x00fc00 + * g5g4 -------- -------- (rgb >> 1) & 0x000300 + * b4b3b2b1 b0------ (rgb << 3) & 0x0000f8 + * b4b3b2 (rgb >> 2) & 0x000007 + * + * Operation count: 5 <<, 6 &, 5 | (n.b. rgb >> 3 is used twice) + */ +inline static int rgb565To888(int rgb) + +{ + int rgb3 = rgb >> 3; + return (((rgb << 8) & 0xf80000) | + ( rgb3 & 0x070000) | + ((rgb << 5) & 0x00fc00) | + ((rgb >> 1) & 0x000300) | + ( rgb3 & 0x0000f8) | + ((rgb >> 2) & 0x000007)); +} + +#if __BYTE_ORDER == __BIG_ENDIAN +static uint32_t swap(uint32_t x) { + int b0 = (x >> 24) & 0xff; + int b1 = (x >> 16) & 0xff; + int b2 = (x >> 8) & 0xff; + int b3 = (x ) & 0xff; + + return (uint32_t)((b3 << 24) | (b2 << 16) | (b1 << 8) | b0); +} +#endif + +static void +init_tables() +{ + if (tables_initialized) { + return; + } + + for (int i = 0; i < 64; i++) { + for (int j = 0; j < 64; j++) { + int avg = (2*i + j)/3; + avg23tab[(i << 6) | j] = avg; + } + } + + asm volatile ("" : : : "memory"); + tables_initialized = 1; +} + +/* + * Utility to scan a DXT1 compressed texture to determine whether it + * contains a transparent pixel (color0 < color1, code == 3). This + * may be useful if the application lacks information as to whether + * the true format is GL_COMPRESSED_RGB_S3TC_DXT1_EXT or + * GL_COMPRESSED_RGBA_S3TC_DXT1_EXT. + */ +bool +DXT1HasAlpha(const GLvoid *data, int width, int height) { +#if TIMING + struct timeval start_t, end_t; + struct timezone tz; + + gettimeofday(&start_t, &tz); +#endif + + bool hasAlpha = false; + + int xblocks = (width + 3)/4; + int yblocks = (height + 3)/4; + int numblocks = xblocks*yblocks; + + uint32_t const *d32 = (uint32_t *)data; + for (int b = 0; b < numblocks; b++) { + uint32_t colors = *d32++; + +#if __BYTE_ORDER == __BIG_ENDIAN + colors = swap(colors); +#endif + + uint16_t color0 = colors & 0xffff; + uint16_t color1 = colors >> 16; + + if (color0 < color1) { + // There's no need to endian-swap within 'bits' + // since we don't care which pixel is the transparent one + uint32_t bits = *d32++; + + // Detect if any (odd, even) pair of bits are '11' + // bits: b31 b30 b29 ... b3 b2 b1 b0 + // bits >> 1: b31 b31 b30 ... b4 b3 b2 b1 + // &: b31 (b31 & b30) (b29 & b28) ... (b2 & b1) (b1 & b0) + // & 0x55..: 0 (b31 & b30) 0 ... 0 (b1 & b0) + if (((bits & (bits >> 1)) & 0x55555555) != 0) { + hasAlpha = true; + goto done; + } + } else { + // Skip 4 bytes + ++d32; + } + } + + done: +#if TIMING + gettimeofday(&end_t, &tz); + long usec = (end_t.tv_sec - start_t.tv_sec)*1000000 + + (end_t.tv_usec - start_t.tv_usec); + + printf("Scanned w=%d h=%d in %ld usec\n", width, height, usec); +#endif + + return hasAlpha; +} + +static void +decodeDXT1(const GLvoid *data, int width, int height, + void *surface, int stride, + bool hasAlpha) + +{ + init_tables(); + + uint32_t const *d32 = (uint32_t *)data; + + // Color table for the current block + uint16_t c[4]; + c[0] = c[1] = c[2] = c[3] = 0; + + // Specified colors from the previous block + uint16_t prev_color0 = 0x0000; + uint16_t prev_color1 = 0x0000; + + uint16_t* rowPtr = (uint16_t*)surface; + for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) { + uint16_t *blockPtr = rowPtr; + for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) { + uint32_t colors = *d32++; + uint32_t bits = *d32++; + +#if __BYTE_ORDER == __BIG_ENDIAN + colors = swap(colors); + bits = swap(bits); +#endif + + // Raw colors + uint16_t color0 = colors & 0xffff; + uint16_t color1 = colors >> 16; + + // If the new block has the same base colors as the + // previous one, we don't need to recompute the color + // table c[] + if (color0 != prev_color0 || color1 != prev_color1) { + // Store raw colors for comparison with next block + prev_color0 = color0; + prev_color1 = color1; + + int r0 = red(color0); + int g0 = green(color0); + int b0 = blue(color0); + + int r1 = red(color1); + int g1 = green(color1); + int b1 = blue(color1); + + if (hasAlpha) { + c[0] = (r0 << 11) | ((g0 >> 1) << 6) | (b0 << 1) | 0x1; + c[1] = (r1 << 11) | ((g1 >> 1) << 6) | (b1 << 1) | 0x1; + } else { + c[0] = color0; + c[1] = color1; + } + + int r2, g2, b2, r3, g3, b3, a3; + + int bbits = bits >> 1; + bool has2 = ((bbits & ~bits) & 0x55555555) != 0; + bool has3 = ((bbits & bits) & 0x55555555) != 0; + + if (has2 || has3) { + if (color0 > color1) { + r2 = avg23(r0, r1); + g2 = avg23(g0, g1); + b2 = avg23(b0, b1); + + r3 = avg23(r1, r0); + g3 = avg23(g1, g0); + b3 = avg23(b1, b0); + a3 = 1; + } else { + r2 = (r0 + r1) >> 1; + g2 = (g0 + g1) >> 1; + b2 = (b0 + b1) >> 1; + + r3 = g3 = b3 = a3 = 0; + } + if (hasAlpha) { + c[2] = (r2 << 11) | ((g2 >> 1) << 6) | + (b2 << 1) | 0x1; + c[3] = (r3 << 11) | ((g3 >> 1) << 6) | + (b3 << 1) | a3; + } else { + c[2] = (r2 << 11) | (g2 << 5) | b2; + c[3] = (r3 << 11) | (g3 << 5) | b3; + } + } + } + + uint16_t* blockRowPtr = blockPtr; + for (int y = 0; y < 4; y++, blockRowPtr += stride) { + // Don't process rows past the botom + if (base_y + y >= height) { + break; + } + + int w = min(width - base_x, 4); + for (int x = 0; x < w; x++) { + int code = bits & 0x3; + bits >>= 2; + + blockRowPtr[x] = c[code]; + } + } + } + } +} + +// Output data as internalformat=GL_RGBA, type=GL_UNSIGNED_BYTE +static void +decodeDXT3(const GLvoid *data, int width, int height, + void *surface, int stride) + +{ + init_tables(); + + uint32_t const *d32 = (uint32_t *)data; + + // Specified colors from the previous block + uint16_t prev_color0 = 0x0000; + uint16_t prev_color1 = 0x0000; + + // Color table for the current block + uint32_t c[4]; + c[0] = c[1] = c[2] = c[3] = 0; + + uint32_t* rowPtr = (uint32_t*)surface; + for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) { + uint32_t *blockPtr = rowPtr; + for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) { + +#if __BYTE_ORDER == __BIG_ENDIAN + uint32_t alphahi = *d32++; + uint32_t alphalo = *d32++; + alphahi = swap(alphahi); + alphalo = swap(alphalo); +#else + uint32_t alphalo = *d32++; + uint32_t alphahi = *d32++; +#endif + + uint32_t colors = *d32++; + uint32_t bits = *d32++; + +#if __BYTE_ORDER == __BIG_ENDIAN + colors = swap(colors); + bits = swap(bits); +#endif + + uint64_t alpha = ((uint64_t)alphahi << 32) | alphalo; + + // Raw colors + uint16_t color0 = colors & 0xffff; + uint16_t color1 = colors >> 16; + + // If the new block has the same base colors as the + // previous one, we don't need to recompute the color + // table c[] + if (color0 != prev_color0 || color1 != prev_color1) { + // Store raw colors for comparison with next block + prev_color0 = color0; + prev_color1 = color1; + + int bbits = bits >> 1; + bool has2 = ((bbits & ~bits) & 0x55555555) != 0; + bool has3 = ((bbits & bits) & 0x55555555) != 0; + + if (has2 || has3) { + int r0 = red(color0); + int g0 = green(color0); + int b0 = blue(color0); + + int r1 = red(color1); + int g1 = green(color1); + int b1 = blue(color1); + + int r2 = avg23(r0, r1); + int g2 = avg23(g0, g1); + int b2 = avg23(b0, b1); + + int r3 = avg23(r1, r0); + int g3 = avg23(g1, g0); + int b3 = avg23(b1, b0); + + c[0] = rgb565SepTo888(r0, g0, b0); + c[1] = rgb565SepTo888(r1, g1, b1); + c[2] = rgb565SepTo888(r2, g2, b2); + c[3] = rgb565SepTo888(r3, g3, b3); + } else { + // Convert to 8 bits + c[0] = rgb565To888(color0); + c[1] = rgb565To888(color1); + } + } + + uint32_t* blockRowPtr = blockPtr; + for (int y = 0; y < 4; y++, blockRowPtr += stride) { + // Don't process rows past the botom + if (base_y + y >= height) { + break; + } + + int w = min(width - base_x, 4); + for (int x = 0; x < w; x++) { + int a = alpha & 0xf; + alpha >>= 4; + + int code = bits & 0x3; + bits >>= 2; + + blockRowPtr[x] = c[code] | (a << 28) | (a << 24); + } + } + } + } +} + +// Output data as internalformat=GL_RGBA, type=GL_UNSIGNED_BYTE +static void +decodeDXT5(const GLvoid *data, int width, int height, + void *surface, int stride) + +{ + init_tables(); + + uint32_t const *d32 = (uint32_t *)data; + + // Specified alphas from the previous block + uint8_t prev_alpha0 = 0x00; + uint8_t prev_alpha1 = 0x00; + + // Specified colors from the previous block + uint16_t prev_color0 = 0x0000; + uint16_t prev_color1 = 0x0000; + + // Alpha table for the current block + uint8_t a[8]; + a[0] = a[1] = a[2] = a[3] = a[4] = a[5] = a[6] = a[7] = 0; + + // Color table for the current block + uint32_t c[4]; + c[0] = c[1] = c[2] = c[3] = 0; + + int good_a5 = 0; + int bad_a5 = 0; + int good_a6 = 0; + int bad_a6 = 0; + int good_a7 = 0; + int bad_a7 = 0; + + uint32_t* rowPtr = (uint32_t*)surface; + for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) { + uint32_t *blockPtr = rowPtr; + for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) { + +#if __BYTE_ORDER == __BIG_ENDIAN + uint32_t alphahi = *d32++; + uint32_t alphalo = *d32++; + alphahi = swap(alphahi); + alphalo = swap(alphalo); +#else + uint32_t alphalo = *d32++; + uint32_t alphahi = *d32++; +#endif + + uint32_t colors = *d32++; + uint32_t bits = *d32++; + +#if __BYTE_ORDER == __BIG_ENDIANx + colors = swap(colors); + bits = swap(bits); +#endif + + uint64_t alpha = ((uint64_t)alphahi << 32) | alphalo; + uint64_t alpha0 = alpha & 0xff; + alpha >>= 8; + uint64_t alpha1 = alpha & 0xff; + alpha >>= 8; + + if (alpha0 != prev_alpha0 || alpha1 != prev_alpha1) { + prev_alpha0 = alpha0; + prev_alpha1 = alpha1; + + a[0] = alpha0; + a[1] = alpha1; + int a01 = alpha0 + alpha1 - 1; + if (alpha0 > alpha1) { + a[2] = div7(6*alpha0 + alpha1); + a[4] = div7(4*alpha0 + 3*alpha1); + a[6] = div7(2*alpha0 + 5*alpha1); + + // Use symmetry to derive half of the values + // A few values will be off by 1 (~.5%) + // Alternate which values are computed directly + // and which are derived to try to reduce bias + a[3] = a01 - a[6]; + a[5] = a01 - a[4]; + a[7] = a01 - a[2]; + } else { + a[2] = div5(4*alpha0 + alpha1); + a[4] = div5(2*alpha0 + 3*alpha1); + a[3] = a01 - a[4]; + a[5] = a01 - a[2]; + a[6] = 0x00; + a[7] = 0xff; + } + } + + // Raw colors + uint16_t color0 = colors & 0xffff; + uint16_t color1 = colors >> 16; + + // If the new block has the same base colors as the + // previous one, we don't need to recompute the color + // table c[] + if (color0 != prev_color0 || color1 != prev_color1) { + // Store raw colors for comparison with next block + prev_color0 = color0; + prev_color1 = color1; + + int bbits = bits >> 1; + bool has2 = ((bbits & ~bits) & 0x55555555) != 0; + bool has3 = ((bbits & bits) & 0x55555555) != 0; + + if (has2 || has3) { + int r0 = red(color0); + int g0 = green(color0); + int b0 = blue(color0); + + int r1 = red(color1); + int g1 = green(color1); + int b1 = blue(color1); + + int r2 = avg23(r0, r1); + int g2 = avg23(g0, g1); + int b2 = avg23(b0, b1); + + int r3 = avg23(r1, r0); + int g3 = avg23(g1, g0); + int b3 = avg23(b1, b0); + + c[0] = rgb565SepTo888(r0, g0, b0); + c[1] = rgb565SepTo888(r1, g1, b1); + c[2] = rgb565SepTo888(r2, g2, b2); + c[3] = rgb565SepTo888(r3, g3, b3); + } else { + // Convert to 8 bits + c[0] = rgb565To888(color0); + c[1] = rgb565To888(color1); + } + } + + uint32_t* blockRowPtr = blockPtr; + for (int y = 0; y < 4; y++, blockRowPtr += stride) { + // Don't process rows past the botom + if (base_y + y >= height) { + break; + } + + int w = min(width - base_x, 4); + for (int x = 0; x < w; x++) { + int acode = alpha & 0x7; + alpha >>= 3; + + int code = bits & 0x3; + bits >>= 2; + + blockRowPtr[x] = c[code] | (a[acode] << 24); + } + } + } + } +} + +/* + * Decode a DXT-compressed texture into memory. DXT textures consist of + * a series of 4x4 pixel blocks in left-to-right, top-down order. + * The number of blocks is given by ceil(width/4)*ceil(height/4). + * + * 'data' points to the texture data. 'width' and 'height' indicate the + * dimensions of the texture. We assume width and height are >= 0 but + * do not require them to be powers of 2 or divisible by any factor. + * + * The output is written to 'surface' with each scanline separated by + * 'stride' 2- or 4-byte words. + * + * 'format' indicates the type of compression and must be one of the following: + * + * GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + * The output is written as 5/6/5 opaque RGB (16 bit words). + * 8 bytes are read from 'data' for each block. + * + * GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + * The output is written as 5/5/5/1 RGBA (16 bit words) + * 8 bytes are read from 'data' for each block. + * + * GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + * GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + * The output is written as 8/8/8/8 ARGB (32 bit words) + * 16 bytes are read from 'data' for each block. + */ +void +decodeDXT(const GLvoid *data, int width, int height, + void *surface, int stride, int format) +{ +#if TIMING + struct timeval start_t, end_t; + struct timezone tz; + + gettimeofday(&start_t, &tz); +#endif + + switch (format) { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + decodeDXT1(data, width, height, surface, stride, false); + break; + + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + decodeDXT1(data, width, height, surface, stride, true); + break; + + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + decodeDXT3(data, width, height, surface, stride); + break; + + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + decodeDXT5(data, width, height, surface, stride); + break; + } + +#if TIMING + gettimeofday(&end_t, &tz); + long usec = (end_t.tv_sec - start_t.tv_sec)*1000000 + + (end_t.tv_usec - start_t.tv_usec); + + printf("Loaded w=%d h=%d in %ld usec\n", width, height, usec); +#endif +} + +} // namespace android diff --git a/opengl/libagl/dxt.h b/opengl/libagl/dxt.h new file mode 100644 index 000000000..d95a36cd1 --- /dev/null +++ b/opengl/libagl/dxt.h @@ -0,0 +1,33 @@ +/* libs/opengles/dxt.h +** +** Copyright 2007, 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. +*/ + +#ifndef ANDROID_OPENGLES_TEXTURE_H +#define ANDROID_OPENGLES_TEXTURE_H + +#include + +#include + +namespace android { + + bool DXT1HasAlpha(const GLvoid *data, int width, int height); + void decodeDXT(const GLvoid *data, int width, int height, + void *surface, int stride, int format); + +} // namespace android + +#endif // ANDROID_OPENGLES_TEXTURE_H diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp new file mode 100644 index 000000000..5b90bf03f --- /dev/null +++ b/opengl/libagl/egl.cpp @@ -0,0 +1,1543 @@ +/* +** +** Copyright 2007 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. +*/ + +#define LOG_TAG "EGL" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include "context.h" +#include "state.h" +#include "texture.h" +#include "matrix.h" + +#undef NELEM +#define NELEM(x) (sizeof(x)/sizeof(*(x))) + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +const unsigned int NUM_DISPLAYS = 1; + +static pthread_mutex_t gInitMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t gErrorKeyMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_key_t gEGLErrorKey = -1; +#ifndef HAVE_ANDROID_OS +namespace gl { +pthread_key_t gGLKey = -1; +}; // namespace gl +#endif + +template +static T setError(GLint error, T returnValue) { + if (ggl_unlikely(gEGLErrorKey == -1)) { + pthread_mutex_lock(&gErrorKeyMutex); + if (gEGLErrorKey == -1) + pthread_key_create(&gEGLErrorKey, NULL); + pthread_mutex_unlock(&gErrorKeyMutex); + } + pthread_setspecific(gEGLErrorKey, (void*)error); + return returnValue; +} + +static GLint getError() { + if (ggl_unlikely(gEGLErrorKey == -1)) + return EGL_SUCCESS; + GLint error = (GLint)pthread_getspecific(gEGLErrorKey); + pthread_setspecific(gEGLErrorKey, (void*)EGL_SUCCESS); + return error; +} + +// ---------------------------------------------------------------------------- + +struct egl_display_t +{ + egl_display_t() : type(0), initialized(0) { } + + static egl_display_t& get_display(EGLDisplay dpy); + + static EGLBoolean is_valid(EGLDisplay dpy) { + return ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS) ? EGL_FALSE : EGL_TRUE; + } + + NativeDisplayType type; + volatile int32_t initialized; +}; + +static egl_display_t gDisplays[NUM_DISPLAYS]; + +egl_display_t& egl_display_t::get_display(EGLDisplay dpy) { + return gDisplays[uintptr_t(dpy)-1U]; +} + +struct egl_context_t { + enum { + IS_CURRENT = 0x00010000, + NEVER_CURRENT = 0x00020000 + }; + uint32_t flags; + EGLDisplay dpy; + EGLConfig config; + EGLSurface read; + EGLSurface draw; + + static inline egl_context_t* context(EGLContext ctx) { + ogles_context_t* const gl = static_cast(ctx); + return static_cast(gl->rasterizer.base); + } +}; + +// ---------------------------------------------------------------------------- + +struct egl_surface_t +{ + enum { + PAGE_FLIP = 0x00000001, + MAGIC = 0x31415265 + }; + + uint32_t magic; + EGLDisplay dpy; + EGLConfig config; + EGLContext ctx; + + egl_surface_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat); + virtual ~egl_surface_t(); + virtual bool isValid() const = 0; + + virtual EGLBoolean bindDrawSurface(ogles_context_t* gl) = 0; + virtual EGLBoolean bindReadSurface(ogles_context_t* gl) = 0; + virtual EGLint getWidth() const = 0; + virtual EGLint getHeight() const = 0; + virtual void* getBits() const = 0; + + virtual EGLint getHorizontalResolution() const; + virtual EGLint getVerticalResolution() const; + virtual EGLint getRefreshRate() const; + virtual EGLint getSwapBehavior() const; + virtual EGLBoolean swapBuffers(); +protected: + GGLSurface depth; +}; + +egl_surface_t::egl_surface_t(EGLDisplay dpy, + EGLConfig config, + int32_t depthFormat) + : magic(MAGIC), dpy(dpy), config(config), ctx(0) +{ + depth.version = sizeof(GGLSurface); + depth.data = 0; + depth.format = depthFormat; +} +egl_surface_t::~egl_surface_t() +{ + magic = 0; + free(depth.data); +} +EGLBoolean egl_surface_t::swapBuffers() { + return EGL_FALSE; +} +EGLint egl_surface_t::getHorizontalResolution() const { + return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); +} +EGLint egl_surface_t::getVerticalResolution() const { + return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); +} +EGLint egl_surface_t::getRefreshRate() const { + return (60 * EGL_DISPLAY_SCALING); +} +EGLint egl_surface_t::getSwapBehavior() const { + return EGL_BUFFER_PRESERVED; +} + +// ---------------------------------------------------------------------------- + +struct egl_window_surface_t : public egl_surface_t +{ + egl_window_surface_t( + EGLDisplay dpy, EGLConfig config, + int32_t depthFormat, + egl_native_window_t* window); + + ~egl_window_surface_t(); + + virtual bool isValid() const { return nativeWindow->magic == 0x600913; } + virtual EGLBoolean swapBuffers(); + virtual EGLBoolean bindDrawSurface(ogles_context_t* gl); + virtual EGLBoolean bindReadSurface(ogles_context_t* gl); + virtual EGLint getWidth() const { return nativeWindow->width; } + virtual EGLint getHeight() const { return nativeWindow->height; } + virtual void* getBits() const; + virtual EGLint getHorizontalResolution() const; + virtual EGLint getVerticalResolution() const; + virtual EGLint getRefreshRate() const; + virtual EGLint getSwapBehavior() const; +private: + egl_native_window_t* nativeWindow; +}; + +egl_window_surface_t::egl_window_surface_t(EGLDisplay dpy, + EGLConfig config, + int32_t depthFormat, + egl_native_window_t* window) + : egl_surface_t(dpy, config, depthFormat), nativeWindow(window) +{ + if (depthFormat) { + depth.width = window->width; + depth.height = window->height; + depth.stride = depth.width; // use the width here + depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); + if (depth.data == 0) { + setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + return; + } + } + nativeWindow->incRef(nativeWindow); +} +egl_window_surface_t::~egl_window_surface_t() { + nativeWindow->decRef(nativeWindow); +} + +EGLBoolean egl_window_surface_t::swapBuffers() +{ + uint32_t flags = nativeWindow->swapBuffers(nativeWindow); + if (flags & EGL_NATIVES_FLAG_SIZE_CHANGED) { + // TODO: we probably should reset the swap rect here + // if the window size has changed + if (depth.data) { + free(depth.data); + depth.width = nativeWindow->width; + depth.height = nativeWindow->height; + depth.stride = nativeWindow->stride; + depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); + if (depth.data == 0) { + setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + return EGL_FALSE; + } + } + } + return EGL_TRUE; +} + +EGLBoolean egl_window_surface_t::bindDrawSurface(ogles_context_t* gl) +{ + GGLSurface buffer; + buffer.version = sizeof(GGLSurface); + buffer.width = nativeWindow->width; + buffer.height = nativeWindow->height; + buffer.stride = nativeWindow->stride; + buffer.data = (GGLubyte*)nativeWindow->base + nativeWindow->offset; + buffer.format = nativeWindow->format; + gl->rasterizer.procs.colorBuffer(gl, &buffer); + if (depth.data != gl->rasterizer.state.buffers.depth.data) + gl->rasterizer.procs.depthBuffer(gl, &depth); + return EGL_TRUE; +} +EGLBoolean egl_window_surface_t::bindReadSurface(ogles_context_t* gl) +{ + GGLSurface buffer; + buffer.version = sizeof(GGLSurface); + buffer.width = nativeWindow->width; + buffer.height = nativeWindow->height; + buffer.stride = nativeWindow->stride; + buffer.data = (GGLubyte*)nativeWindow->base + nativeWindow->offset; + buffer.format = nativeWindow->format; + gl->rasterizer.procs.readBuffer(gl, &buffer); + return EGL_TRUE; +} +void* egl_window_surface_t::getBits() const { + return (GGLubyte*)nativeWindow->base + nativeWindow->offset; +} +EGLint egl_window_surface_t::getHorizontalResolution() const { + return (nativeWindow->xdpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); +} +EGLint egl_window_surface_t::getVerticalResolution() const { + return (nativeWindow->ydpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); +} +EGLint egl_window_surface_t::getRefreshRate() const { + return (nativeWindow->fps * EGL_DISPLAY_SCALING); +} +EGLint egl_window_surface_t::getSwapBehavior() const { + uint32_t flags = nativeWindow->flags; + if (flags & EGL_NATIVES_FLAG_DESTROY_BACKBUFFER) + return EGL_BUFFER_DESTROYED; + return EGL_BUFFER_PRESERVED; +} + +// ---------------------------------------------------------------------------- + +struct egl_pixmap_surface_t : public egl_surface_t +{ + egl_pixmap_surface_t( + EGLDisplay dpy, EGLConfig config, + int32_t depthFormat, + egl_native_pixmap_t const * pixmap); + + virtual ~egl_pixmap_surface_t() { } + + virtual bool isValid() const { return nativePixmap.version == sizeof(egl_native_pixmap_t); } + virtual EGLBoolean bindDrawSurface(ogles_context_t* gl); + virtual EGLBoolean bindReadSurface(ogles_context_t* gl); + virtual EGLint getWidth() const { return nativePixmap.width; } + virtual EGLint getHeight() const { return nativePixmap.height; } + virtual void* getBits() const { return nativePixmap.data; } +private: + egl_native_pixmap_t nativePixmap; +}; + +egl_pixmap_surface_t::egl_pixmap_surface_t(EGLDisplay dpy, + EGLConfig config, + int32_t depthFormat, + egl_native_pixmap_t const * pixmap) + : egl_surface_t(dpy, config, depthFormat), nativePixmap(*pixmap) +{ + if (depthFormat) { + depth.width = pixmap->width; + depth.height = pixmap->height; + depth.stride = depth.width; // use the width here + depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); + if (depth.data == 0) { + setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + return; + } + } +} +EGLBoolean egl_pixmap_surface_t::bindDrawSurface(ogles_context_t* gl) +{ + GGLSurface buffer; + buffer.version = sizeof(GGLSurface); + buffer.width = nativePixmap.width; + buffer.height = nativePixmap.height; + buffer.stride = nativePixmap.stride; + buffer.data = nativePixmap.data; + buffer.format = nativePixmap.format; + + gl->rasterizer.procs.colorBuffer(gl, &buffer); + if (depth.data != gl->rasterizer.state.buffers.depth.data) + gl->rasterizer.procs.depthBuffer(gl, &depth); + return EGL_TRUE; +} +EGLBoolean egl_pixmap_surface_t::bindReadSurface(ogles_context_t* gl) +{ + GGLSurface buffer; + buffer.version = sizeof(GGLSurface); + buffer.width = nativePixmap.width; + buffer.height = nativePixmap.height; + buffer.stride = nativePixmap.stride; + buffer.data = nativePixmap.data; + buffer.format = nativePixmap.format; + gl->rasterizer.procs.readBuffer(gl, &buffer); + return EGL_TRUE; +} + +// ---------------------------------------------------------------------------- + +struct egl_pbuffer_surface_t : public egl_surface_t +{ + egl_pbuffer_surface_t( + EGLDisplay dpy, EGLConfig config, int32_t depthFormat, + int32_t w, int32_t h, int32_t f); + + virtual ~egl_pbuffer_surface_t(); + + virtual bool isValid() const { return pbuffer.data != 0; } + virtual EGLBoolean bindDrawSurface(ogles_context_t* gl); + virtual EGLBoolean bindReadSurface(ogles_context_t* gl); + virtual EGLint getWidth() const { return pbuffer.width; } + virtual EGLint getHeight() const { return pbuffer.height; } + virtual void* getBits() const { return pbuffer.data; } +private: + GGLSurface pbuffer; +}; + +egl_pbuffer_surface_t::egl_pbuffer_surface_t(EGLDisplay dpy, + EGLConfig config, int32_t depthFormat, + int32_t w, int32_t h, int32_t f) + : egl_surface_t(dpy, config, depthFormat) +{ + size_t size = w*h; + switch (f) { + case GGL_PIXEL_FORMAT_A_8: size *= 1; break; + case GGL_PIXEL_FORMAT_RGB_565: size *= 2; break; + case GGL_PIXEL_FORMAT_RGBA_8888: size *= 4; break; + default: + LOGE("incompatible pixel format for pbuffer (format=%d)", f); + pbuffer.data = 0; + break; + } + pbuffer.version = sizeof(GGLSurface); + pbuffer.width = w; + pbuffer.height = h; + pbuffer.stride = w; + pbuffer.data = (GGLubyte*)malloc(size); + pbuffer.format = f; + + if (depthFormat) { + depth.width = pbuffer.width; + depth.height = pbuffer.height; + depth.stride = depth.width; // use the width here + depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); + if (depth.data == 0) { + setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + return; + } + } +} +egl_pbuffer_surface_t::~egl_pbuffer_surface_t() { + free(pbuffer.data); +} +EGLBoolean egl_pbuffer_surface_t::bindDrawSurface(ogles_context_t* gl) +{ + gl->rasterizer.procs.colorBuffer(gl, &pbuffer); + if (depth.data != gl->rasterizer.state.buffers.depth.data) + gl->rasterizer.procs.depthBuffer(gl, &depth); + return EGL_TRUE; +} +EGLBoolean egl_pbuffer_surface_t::bindReadSurface(ogles_context_t* gl) +{ + gl->rasterizer.procs.readBuffer(gl, &pbuffer); + return EGL_TRUE; +} + +// ---------------------------------------------------------------------------- + +struct config_pair_t { + GLint key; + GLint value; +}; + +struct configs_t { + const config_pair_t* array; + int size; +}; + +struct config_management_t { + GLint key; + bool (*match)(GLint reqValue, GLint confValue); + static bool atLeast(GLint reqValue, GLint confValue) { + return (reqValue == EGL_DONT_CARE) || (confValue >= reqValue); + } + static bool exact(GLint reqValue, GLint confValue) { + return (reqValue == EGL_DONT_CARE) || (confValue == reqValue); + } + static bool mask(GLint reqValue, GLint confValue) { + return (confValue & reqValue) == reqValue; + } +}; + +// ---------------------------------------------------------------------------- + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 2 +static char const * const gVendorString = "Google Inc."; +static char const * const gVersionString = "1.2 Android Driver"; +static char const * const gClientApiString = "OpenGL ES"; +static char const * const gExtensionsString = ""; + +// ---------------------------------------------------------------------------- + +struct extention_map_t { + const char * const name; + __eglMustCastToProperFunctionPointerType address; +}; + +static const extention_map_t gExtentionMap[] = { + { "glDrawTexsOES", (void(*)())&glDrawTexsOES }, + { "glDrawTexiOES", (void(*)())&glDrawTexiOES }, + { "glDrawTexfOES", (void(*)())&glDrawTexfOES }, + { "glDrawTexxOES", (void(*)())&glDrawTexxOES }, + { "glDrawTexsvOES", (void(*)())&glDrawTexsvOES }, + { "glDrawTexivOES", (void(*)())&glDrawTexivOES }, + { "glDrawTexfvOES", (void(*)())&glDrawTexfvOES }, + { "glDrawTexxvOES", (void(*)())&glDrawTexxvOES }, + { "glQueryMatrixxOES", (void(*)())&glQueryMatrixxOES }, + { "glClipPlanef", (void(*)())&glClipPlanef }, + { "glClipPlanex", (void(*)())&glClipPlanex }, + { "glBindBuffer", (void(*)())&glBindBuffer }, + { "glBufferData", (void(*)())&glBufferData }, + { "glBufferSubData", (void(*)())&glBufferSubData }, + { "glDeleteBuffers", (void(*)())&glDeleteBuffers }, + { "glGenBuffers", (void(*)())&glGenBuffers }, +}; + +/* + * In the lists below, attributes names MUST be sorted. + * Additionally, all configs must be sorted according to + * the EGL specification. + */ + +static config_pair_t const config_base_attribute_list[] = { + { EGL_STENCIL_SIZE, 0 }, + { EGL_CONFIG_CAVEAT, EGL_SLOW_CONFIG }, + { EGL_LEVEL, 0 }, + { EGL_MAX_PBUFFER_HEIGHT, GGL_MAX_VIEWPORT_DIMS }, + { EGL_MAX_PBUFFER_PIXELS, + GGL_MAX_VIEWPORT_DIMS*GGL_MAX_VIEWPORT_DIMS }, + { EGL_MAX_PBUFFER_WIDTH, GGL_MAX_VIEWPORT_DIMS }, + { EGL_NATIVE_RENDERABLE, EGL_TRUE }, + { EGL_NATIVE_VISUAL_ID, 0 }, + { EGL_NATIVE_VISUAL_TYPE, GGL_PIXEL_FORMAT_RGB_565 }, + { EGL_SAMPLES, 0 }, + { EGL_SAMPLE_BUFFERS, 0 }, + { EGL_TRANSPARENT_TYPE, EGL_NONE }, + { EGL_TRANSPARENT_BLUE_VALUE, 0 }, + { EGL_TRANSPARENT_GREEN_VALUE, 0 }, + { EGL_TRANSPARENT_RED_VALUE, 0 }, + { EGL_BIND_TO_TEXTURE_RGBA, EGL_FALSE }, + { EGL_BIND_TO_TEXTURE_RGB, EGL_FALSE }, + { EGL_MIN_SWAP_INTERVAL, 1 }, + { EGL_MAX_SWAP_INTERVAL, 4 }, +}; + +// These configs can override the base attribute list +// NOTE: when adding a config here, don't forget to update eglCreate*Surface() + +static config_pair_t const config_0_attribute_list[] = { + { EGL_BUFFER_SIZE, 16 }, + { EGL_ALPHA_SIZE, 0 }, + { EGL_BLUE_SIZE, 5 }, + { EGL_GREEN_SIZE, 6 }, + { EGL_RED_SIZE, 5 }, + { EGL_DEPTH_SIZE, 0 }, + { EGL_CONFIG_ID, 0 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static config_pair_t const config_1_attribute_list[] = { + { EGL_BUFFER_SIZE, 16 }, + { EGL_ALPHA_SIZE, 0 }, + { EGL_BLUE_SIZE, 5 }, + { EGL_GREEN_SIZE, 6 }, + { EGL_RED_SIZE, 5 }, + { EGL_DEPTH_SIZE, 16 }, + { EGL_CONFIG_ID, 1 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static config_pair_t const config_2_attribute_list[] = { + { EGL_BUFFER_SIZE, 32 }, + { EGL_ALPHA_SIZE, 8 }, + { EGL_BLUE_SIZE, 8 }, + { EGL_GREEN_SIZE, 8 }, + { EGL_RED_SIZE, 8 }, + { EGL_DEPTH_SIZE, 0 }, + { EGL_CONFIG_ID, 2 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static config_pair_t const config_3_attribute_list[] = { + { EGL_BUFFER_SIZE, 32 }, + { EGL_ALPHA_SIZE, 8 }, + { EGL_BLUE_SIZE, 8 }, + { EGL_GREEN_SIZE, 8 }, + { EGL_RED_SIZE, 8 }, + { EGL_DEPTH_SIZE, 16 }, + { EGL_CONFIG_ID, 3 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static config_pair_t const config_4_attribute_list[] = { + { EGL_BUFFER_SIZE, 8 }, + { EGL_ALPHA_SIZE, 8 }, + { EGL_BLUE_SIZE, 0 }, + { EGL_GREEN_SIZE, 0 }, + { EGL_RED_SIZE, 0 }, + { EGL_DEPTH_SIZE, 0 }, + { EGL_CONFIG_ID, 4 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static config_pair_t const config_5_attribute_list[] = { + { EGL_BUFFER_SIZE, 8 }, + { EGL_ALPHA_SIZE, 8 }, + { EGL_BLUE_SIZE, 0 }, + { EGL_GREEN_SIZE, 0 }, + { EGL_RED_SIZE, 0 }, + { EGL_DEPTH_SIZE, 16 }, + { EGL_CONFIG_ID, 5 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static configs_t const gConfigs[] = { + { config_0_attribute_list, NELEM(config_0_attribute_list) }, + { config_1_attribute_list, NELEM(config_1_attribute_list) }, + { config_2_attribute_list, NELEM(config_2_attribute_list) }, + { config_3_attribute_list, NELEM(config_3_attribute_list) }, + { config_4_attribute_list, NELEM(config_4_attribute_list) }, + { config_5_attribute_list, NELEM(config_5_attribute_list) }, +}; + +static config_management_t const gConfigManagement[] = { + { EGL_BUFFER_SIZE, config_management_t::atLeast }, + { EGL_ALPHA_SIZE, config_management_t::atLeast }, + { EGL_BLUE_SIZE, config_management_t::atLeast }, + { EGL_GREEN_SIZE, config_management_t::atLeast }, + { EGL_RED_SIZE, config_management_t::atLeast }, + { EGL_DEPTH_SIZE, config_management_t::atLeast }, + { EGL_STENCIL_SIZE, config_management_t::atLeast }, + { EGL_CONFIG_CAVEAT, config_management_t::exact }, + { EGL_CONFIG_ID, config_management_t::exact }, + { EGL_LEVEL, config_management_t::exact }, + { EGL_MAX_PBUFFER_HEIGHT, config_management_t::exact }, + { EGL_MAX_PBUFFER_PIXELS, config_management_t::exact }, + { EGL_MAX_PBUFFER_WIDTH, config_management_t::exact }, + { EGL_NATIVE_RENDERABLE, config_management_t::exact }, + { EGL_NATIVE_VISUAL_ID, config_management_t::exact }, + { EGL_NATIVE_VISUAL_TYPE, config_management_t::exact }, + { EGL_SAMPLES, config_management_t::exact }, + { EGL_SAMPLE_BUFFERS, config_management_t::exact }, + { EGL_SURFACE_TYPE, config_management_t::mask }, + { EGL_TRANSPARENT_TYPE, config_management_t::exact }, + { EGL_TRANSPARENT_BLUE_VALUE, config_management_t::exact }, + { EGL_TRANSPARENT_GREEN_VALUE, config_management_t::exact }, + { EGL_TRANSPARENT_RED_VALUE, config_management_t::exact }, + { EGL_BIND_TO_TEXTURE_RGBA, config_management_t::exact }, + { EGL_BIND_TO_TEXTURE_RGB, config_management_t::exact }, + { EGL_MIN_SWAP_INTERVAL, config_management_t::exact }, + { EGL_MAX_SWAP_INTERVAL, config_management_t::exact }, +}; + +static config_pair_t const config_defaults[] = { + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT }, +}; + +// ---------------------------------------------------------------------------- + +template +static int binarySearch(T const sortedArray[], int first, int last, EGLint key) +{ + while (first <= last) { + int mid = (first + last) / 2; + if (key > sortedArray[mid].key) { + first = mid + 1; + } else if (key < sortedArray[mid].key) { + last = mid - 1; + } else { + return mid; + } + } + return -1; +} + +static int isAttributeMatching(int i, EGLint attr, EGLint val) +{ + // look for the attribute in all of our configs + config_pair_t const* configFound = gConfigs[i].array; + int index = binarySearch( + gConfigs[i].array, + 0, gConfigs[i].size-1, + attr); + if (index < 0) { + configFound = config_base_attribute_list; + index = binarySearch( + config_base_attribute_list, + 0, NELEM(config_base_attribute_list)-1, + attr); + } + if (index >= 0) { + // attribute found, check if this config could match + int cfgMgtIndex = binarySearch( + gConfigManagement, + 0, NELEM(gConfigManagement)-1, + attr); + if (index >= 0) { + bool match = gConfigManagement[cfgMgtIndex].match( + val, configFound[index].value); + if (match) { + // this config matches + return 1; + } + } else { + // attribute not found. this should NEVER happen. + } + } else { + // error, this attribute doesn't exist + } + return 0; +} + +static int makeCurrent(ogles_context_t* gl) +{ + ogles_context_t* current = (ogles_context_t*)getGlThreadSpecific(); + if (gl) { + egl_context_t* c = egl_context_t::context(gl); + if (c->flags & egl_context_t::IS_CURRENT) { + if (current != gl) { + // it is an error to set a context current, if it's already + // current to another thread + return -1; + } + } else { + if (current) { + // mark the current context as not current, and flush + glFlush(); + egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT; + } + } + if (!(c->flags & egl_context_t::IS_CURRENT)) { + // The context is not current, make it current! + setGlThreadSpecific(gl); + c->flags |= egl_context_t::IS_CURRENT; + } + } else { + if (current) { + // mark the current context as not current, and flush + glFlush(); + egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT; + } + // this thread has no context attached to it + setGlThreadSpecific(0); + } + return 0; +} + +static EGLBoolean getConfigAttrib(EGLDisplay dpy, EGLConfig config, + EGLint attribute, EGLint *value) +{ + size_t numConfigs = NELEM(gConfigs); + int index = (int)config; + if (uint32_t(index) >= numConfigs) + return setError(EGL_BAD_CONFIG, EGL_FALSE); + + int attrIndex; + attrIndex = binarySearch( + gConfigs[index].array, + 0, gConfigs[index].size-1, + attribute); + if (attrIndex>=0) { + *value = gConfigs[index].array[attrIndex].value; + return EGL_TRUE; + } + + attrIndex = binarySearch( + config_base_attribute_list, + 0, NELEM(config_base_attribute_list)-1, + attribute); + if (attrIndex>=0) { + *value = config_base_attribute_list[attrIndex].value; + return EGL_TRUE; + } + return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); +} + +static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config, + NativeWindowType window, const EGLint *attrib_list) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + if (window == 0) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + EGLint surfaceType; + if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) + return EGL_FALSE; + + if (!(surfaceType & EGL_WINDOW_BIT)) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + EGLint configID; + if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) + return EGL_FALSE; + + int32_t depthFormat; + int32_t pixelFormat; + switch(configID) { + case 0: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = 0; + break; + case 1: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 2: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = 0; + break; + case 3: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 4: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = 0; + break; + case 5: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + default: + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + } + + // FIXME: we don't have access to the pixelFormat here just yet. + // (it's possible that the surface is not fully initialized) + // maybe this should be done after the page-flip + //if (EGLint(info.format) != pixelFormat) + // return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + egl_surface_t* surface = + new egl_window_surface_t(dpy, config, depthFormat, + static_cast(window)); + + if (!surface->isValid()) { + // there was a problem in the ctor, the error + // flag has been set. + delete surface; + surface = 0; + } + return surface; +} + +static EGLSurface createPixmapSurface(EGLDisplay dpy, EGLConfig config, + NativePixmapType pixmap, const EGLint *attrib_list) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + if (pixmap == 0) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + EGLint surfaceType; + if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) + return EGL_FALSE; + + if (!(surfaceType & EGL_PIXMAP_BIT)) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + EGLint configID; + if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) + return EGL_FALSE; + + int32_t depthFormat; + int32_t pixelFormat; + switch(configID) { + case 0: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = 0; + break; + case 1: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 2: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = 0; + break; + case 3: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 4: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = 0; + break; + case 5: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + default: + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + } + + if (pixmap->format != pixelFormat) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + egl_surface_t* surface = + new egl_pixmap_surface_t(dpy, config, depthFormat, + static_cast(pixmap)); + + if (!surface->isValid()) { + // there was a problem in the ctor, the error + // flag has been set. + delete surface; + surface = 0; + } + return surface; +} + +static EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config, + const EGLint *attrib_list) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + + EGLint surfaceType; + if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) + return EGL_FALSE; + + if (!(surfaceType & EGL_PBUFFER_BIT)) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + EGLint configID; + if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) + return EGL_FALSE; + + int32_t depthFormat; + int32_t pixelFormat; + switch(configID) { + case 0: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = 0; + break; + case 1: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 2: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = 0; + break; + case 3: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 4: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = 0; + break; + case 5: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + default: + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + } + + int32_t w = 0; + int32_t h = 0; + while (attrib_list[0]) { + if (attrib_list[0] == EGL_WIDTH) w = attrib_list[1]; + if (attrib_list[0] == EGL_HEIGHT) h = attrib_list[1]; + attrib_list+=2; + } + + egl_surface_t* surface = + new egl_pbuffer_surface_t(dpy, config, depthFormat, w, h, pixelFormat); + + if (!surface->isValid()) { + // there was a problem in the ctor, the error + // flag has been set. + delete surface; + surface = 0; + } + return surface; +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + +// ---------------------------------------------------------------------------- +// Initialization +// ---------------------------------------------------------------------------- + +EGLDisplay eglGetDisplay(NativeDisplayType display) +{ +#ifndef HAVE_ANDROID_OS + // this just needs to be done once + if (gGLKey == -1) { + pthread_mutex_lock(&gInitMutex); + if (gGLKey == -1) + pthread_key_create(&gGLKey, NULL); + pthread_mutex_unlock(&gInitMutex); + } +#endif + if (display == EGL_DEFAULT_DISPLAY) { + EGLDisplay dpy = (EGLDisplay)1; + egl_display_t& d = egl_display_t::get_display(dpy); + d.type = display; + return dpy; + } + return EGL_NO_DISPLAY; +} + +EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + EGLBoolean res = EGL_TRUE; + egl_display_t& d = egl_display_t::get_display(dpy); + + if (android_atomic_inc(&d.initialized) == 0) { + // initialize stuff here if needed + //pthread_mutex_lock(&gInitMutex); + //pthread_mutex_unlock(&gInitMutex); + } + + if (res == EGL_TRUE) { + if (major != NULL) *major = VERSION_MAJOR; + if (minor != NULL) *minor = VERSION_MINOR; + } + return res; +} + +EGLBoolean eglTerminate(EGLDisplay dpy) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + EGLBoolean res = EGL_TRUE; + egl_display_t& d = egl_display_t::get_display(dpy); + if (android_atomic_dec(&d.initialized) == 1) { + // TODO: destroy all resources (surfaces, contexts, etc...) + //pthread_mutex_lock(&gInitMutex); + //pthread_mutex_unlock(&gInitMutex); + } + return res; +} + +// ---------------------------------------------------------------------------- +// configuration +// ---------------------------------------------------------------------------- + +EGLBoolean eglGetConfigs( EGLDisplay dpy, + EGLConfig *configs, + EGLint config_size, EGLint *num_config) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + GLint numConfigs = NELEM(gConfigs); + if (!configs) { + *num_config = numConfigs; + return EGL_TRUE; + } + GLint i; + for (i=0 ; i( + (config_pair_t const*)attrib_list, + 0, numAttributes, + config_defaults[j].key) < 0) + { + for (int i=0 ; i(eglSurface) ); + if (surface->magic != egl_surface_t::MAGIC) + return setError(EGL_BAD_SURFACE, EGL_FALSE); + if (surface->dpy != dpy) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + delete surface; + } + return EGL_TRUE; +} + +EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface eglSurface, + EGLint attribute, EGLint *value) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + egl_surface_t* surface = static_cast(eglSurface); + if (surface->dpy != dpy) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + EGLBoolean ret = EGL_TRUE; + switch (attribute) { + case EGL_CONFIG_ID: + ret = getConfigAttrib(dpy, surface->config, EGL_CONFIG_ID, value); + break; + case EGL_WIDTH: + *value = surface->getWidth(); + break; + case EGL_HEIGHT: + *value = surface->getHeight(); + break; + case EGL_LARGEST_PBUFFER: + // not modified for a window or pixmap surface + break; + case EGL_TEXTURE_FORMAT: + *value = EGL_NO_TEXTURE; + break; + case EGL_TEXTURE_TARGET: + *value = EGL_NO_TEXTURE; + break; + case EGL_MIPMAP_TEXTURE: + *value = EGL_FALSE; + break; + case EGL_MIPMAP_LEVEL: + *value = 0; + break; + case EGL_RENDER_BUFFER: + // TODO: return the real RENDER_BUFFER here + *value = EGL_BACK_BUFFER; + break; + case EGL_HORIZONTAL_RESOLUTION: + // pixel/mm * EGL_DISPLAY_SCALING + *value = surface->getHorizontalResolution(); + break; + case EGL_VERTICAL_RESOLUTION: + // pixel/mm * EGL_DISPLAY_SCALING + *value = surface->getVerticalResolution(); + break; + case EGL_PIXEL_ASPECT_RATIO: { + // w/h * EGL_DISPLAY_SCALING + int wr = surface->getHorizontalResolution(); + int hr = surface->getVerticalResolution(); + *value = (wr * EGL_DISPLAY_SCALING) / hr; + } break; + case EGL_SWAP_BEHAVIOR: + *value = surface->getSwapBehavior(); + break; + default: + ret = setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); + } + return ret; +} + +EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, + EGLContext share_list, const EGLint *attrib_list) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + + ogles_context_t* gl = ogles_init(sizeof(egl_context_t)); + if (!gl) return setError(EGL_BAD_ALLOC, EGL_NO_CONTEXT); + + egl_context_t* c = static_cast(gl->rasterizer.base); + c->flags = egl_context_t::NEVER_CURRENT; + c->dpy = dpy; + c->config = config; + c->read = 0; + c->draw = 0; + return (EGLContext)gl; +} + +EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + egl_context_t* c = egl_context_t::context(ctx); + if (c->flags & egl_context_t::IS_CURRENT) + setGlThreadSpecific(0); + ogles_uninit((ogles_context_t*)ctx); + return EGL_TRUE; +} + +EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, + EGLSurface read, EGLContext ctx) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + if (draw) { + egl_surface_t* s = (egl_surface_t*)draw; + if (s->dpy != dpy) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: check that draw and read are compatible with the context + } + + EGLContext current_ctx = EGL_NO_CONTEXT; + + if ((read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE) && (ctx != EGL_NO_CONTEXT)) + return setError(EGL_BAD_MATCH, EGL_FALSE); + + if ((read != EGL_NO_SURFACE || draw != EGL_NO_SURFACE) && (ctx == EGL_NO_CONTEXT)) + return setError(EGL_BAD_MATCH, EGL_FALSE); + + if (ctx == EGL_NO_CONTEXT) { + // if we're detaching, we need the current context + current_ctx = (EGLContext)getGlThreadSpecific(); + } else { + egl_context_t* c = egl_context_t::context(ctx); + egl_surface_t* d = (egl_surface_t*)draw; + egl_surface_t* r = (egl_surface_t*)read; + if ((d && d->ctx && d->ctx != ctx) || + (r && r->ctx && r->ctx != ctx)) { + // once of the surface is bound to a context in another thread + return setError(EGL_BAD_ACCESS, EGL_FALSE); + } + } + + ogles_context_t* gl = (ogles_context_t*)ctx; + if (makeCurrent(gl) == 0) { + if (ctx) { + egl_context_t* c = egl_context_t::context(ctx); + egl_surface_t* d = (egl_surface_t*)draw; + egl_surface_t* r = (egl_surface_t*)read; + c->read = read; + c->draw = draw; + if (c->flags & egl_context_t::NEVER_CURRENT) { + c->flags &= ~egl_context_t::NEVER_CURRENT; + GLint w = 0; + GLint h = 0; + if (draw) { + w = d->getWidth(); + h = d->getHeight(); + } + ogles_surfaceport(gl, 0, 0); + ogles_viewport(gl, 0, 0, w, h); + ogles_scissor(gl, 0, 0, w, h); + } + if (d) { + d->ctx = ctx; + d->bindDrawSurface(gl); + } + if (r) { + r->ctx = ctx; + r->bindReadSurface(gl); + } + } else { + // if surfaces were bound to the context bound to this thread + // mark then as unbound. + if (current_ctx) { + egl_context_t* c = egl_context_t::context(current_ctx); + egl_surface_t* d = (egl_surface_t*)c->draw; + egl_surface_t* r = (egl_surface_t*)c->read; + if (d) d->ctx = EGL_NO_CONTEXT; + if (r) r->ctx = EGL_NO_CONTEXT; + } + } + return EGL_TRUE; + } + return setError(EGL_BAD_ACCESS, EGL_FALSE); +} + +EGLContext eglGetCurrentContext(void) +{ + // eglGetCurrentContext returns the current EGL rendering context, + // as specified by eglMakeCurrent. If no context is current, + // EGL_NO_CONTEXT is returned. + return (EGLContext)getGlThreadSpecific(); +} + +EGLSurface eglGetCurrentSurface(EGLint readdraw) +{ + // eglGetCurrentSurface returns the read or draw surface attached + // to the current EGL rendering context, as specified by eglMakeCurrent. + // If no context is current, EGL_NO_SURFACE is returned. + EGLContext ctx = (EGLContext)getGlThreadSpecific(); + if (ctx == EGL_NO_CONTEXT) return EGL_NO_SURFACE; + egl_context_t* c = egl_context_t::context(ctx); + if (readdraw == EGL_READ) { + return c->read; + } else if (readdraw == EGL_DRAW) { + return c->draw; + } + return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); +} + +EGLDisplay eglGetCurrentDisplay(void) +{ + // eglGetCurrentDisplay returns the current EGL display connection + // for the current EGL rendering context, as specified by eglMakeCurrent. + // If no context is current, EGL_NO_DISPLAY is returned. + EGLContext ctx = (EGLContext)getGlThreadSpecific(); + if (ctx == EGL_NO_CONTEXT) return EGL_NO_DISPLAY; + egl_context_t* c = egl_context_t::context(ctx); + return c->dpy; +} + +EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, + EGLint attribute, EGLint *value) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + egl_context_t* c = egl_context_t::context(ctx); + switch (attribute) { + case EGL_CONFIG_ID: + // Returns the ID of the EGL frame buffer configuration with + // respect to which the context was created + return getConfigAttrib(dpy, c->config, EGL_CONFIG_ID, value); + } + return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); +} + +EGLBoolean eglWaitGL(void) +{ + return EGL_TRUE; +} + +EGLBoolean eglWaitNative(EGLint engine) +{ + return EGL_TRUE; +} + +EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + egl_surface_t* d = static_cast(draw); + if (d->dpy != dpy) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + // post the surface + d->swapBuffers(); + + // if it's bound to a context, update the buffer + if (d->ctx != EGL_NO_CONTEXT) { + d->bindDrawSurface((ogles_context_t*)d->ctx); + // if this surface is also the read surface of the context + // it is bound to, make sure to update the read buffer as well. + // The EGL spec is a little unclear about this. + egl_context_t* c = egl_context_t::context(d->ctx); + if (c->read == draw) { + d->bindReadSurface((ogles_context_t*)d->ctx); + } + } + + return EGL_TRUE; +} + +EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, + NativePixmapType target) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: eglCopyBuffers() + return EGL_FALSE; +} + +EGLint eglGetError(void) +{ + return getError(); +} + +const char* eglQueryString(EGLDisplay dpy, EGLint name) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, (const char*)0); + + switch (name) { + case EGL_VENDOR: + return gVendorString; + case EGL_VERSION: + return gVersionString; + case EGL_EXTENSIONS: + return gExtensionsString; + case EGL_CLIENT_APIS: + return gClientApiString; + } + return setError(EGL_BAD_PARAMETER, (const char *)0); +} + +// ---------------------------------------------------------------------------- +// EGL 1.1 +// ---------------------------------------------------------------------------- + +EGLBoolean eglSurfaceAttrib( + EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: eglSurfaceAttrib() + return setError(EGL_BAD_PARAMETER, EGL_FALSE); +} + +EGLBoolean eglBindTexImage( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: eglBindTexImage() + return setError(EGL_BAD_PARAMETER, EGL_FALSE); +} + +EGLBoolean eglReleaseTexImage( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: eglReleaseTexImage() + return setError(EGL_BAD_PARAMETER, EGL_FALSE); +} + +EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: eglSwapInterval() + return setError(EGL_BAD_PARAMETER, EGL_FALSE); +} + +// ---------------------------------------------------------------------------- +// EGL 1.2 +// ---------------------------------------------------------------------------- + +EGLBoolean eglBindAPI(EGLenum api) +{ + if (api != EGL_OPENGL_ES_API) + return setError(EGL_BAD_PARAMETER, EGL_FALSE); + return EGL_TRUE; +} + +EGLenum eglQueryAPI(void) +{ + return EGL_OPENGL_ES_API; +} + +EGLBoolean eglWaitClient(void) +{ + glFinish(); + return EGL_TRUE; +} + +EGLBoolean eglReleaseThread(void) +{ + // TODO: eglReleaseThread() + return EGL_TRUE; +} + +EGLSurface eglCreatePbufferFromClientBuffer( + EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, + EGLConfig config, const EGLint *attrib_list) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + // TODO: eglCreatePbufferFromClientBuffer() + return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); +} + +// ---------------------------------------------------------------------------- +// Android extensions +// ---------------------------------------------------------------------------- + +void (*eglGetProcAddress (const char *procname))() +{ + extention_map_t const * const map = gExtentionMap; + for (uint32_t i=0 ; i=0) ? (i<>-c); + const uint32_t e = 134 - c; + i &= ~0x800000; + i |= e<<23; + i |= s; + return f; +#endif +} + +float sinef(float x) +{ + const float A = 1.0f / (2.0f*M_PI); + const float B = -16.0f; + const float C = 8.0f; + + // scale angle for easy argument reduction + x *= A; + + if (fabsf(x) >= 0.5f) { + // Argument reduction + x = x - ceilf(x + 0.5f) + 1.0f; + } + + const float y = B*x*fabsf(x) + C*x; + return 0.2215f * (y*fabsf(y) - y) + y; +} + +float cosinef(float x) +{ + return sinef(x + float(M_PI/2)); +} + +void sincosf(GLfloat angle, GLfloat* s, GLfloat* c) { + *s = sinef(angle); + *c = cosinef(angle); +} + +}; // namespace fp_utils + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/opengl/libagl/fp.h b/opengl/libagl/fp.h new file mode 100644 index 000000000..6d0c18339 --- /dev/null +++ b/opengl/libagl/fp.h @@ -0,0 +1,243 @@ +/* libs/opengles/fp.h +** +** Copyright 2006, 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. +*/ + +#ifndef ANDROID_OPENGLES_FP_H +#define ANDROID_OPENGLES_FP_H + +#include +#include +#include +#include + +#include + +#include + +#define DEBUG_USE_FLOATS 0 + +// ---------------------------------------------------------------------------- + +extern "C" GLfixed gglFloatToFixed(float f) __attribute__((const)); + +// ---------------------------------------------------------------------------- +namespace android { + +namespace gl { + + GLfloat fixedToFloat(GLfixed) CONST; + + void sincosf(GLfloat angle, GLfloat* s, GLfloat* c); + float sinef(GLfloat x) CONST; + float cosinef(GLfloat x) CONST; + +inline bool cmpf(GLfloat a, GLfloat b) CONST; +inline bool isZerof(GLfloat) CONST; +inline bool isOnef(GLfloat) CONST; + +inline int isZeroOrNegativef(GLfloat) CONST; + +inline int exponent(GLfloat) CONST; +inline int32_t mantissa(GLfloat) CONST; +inline GLfloat clampToZerof(GLfloat) CONST; +inline GLfloat reciprocalf(GLfloat) CONST; +inline GLfloat rsqrtf(GLfloat) CONST; +inline GLfloat sqrf(GLfloat) CONST; +inline GLfloat addExpf(GLfloat v, int e) CONST; +inline GLfloat mul2f(GLfloat v) CONST; +inline GLfloat div2f(GLfloat v) CONST; +inline GLfloat absf(GLfloat v) CONST; + + +/* + * float fastexpf(float) : a fast approximation of expf(x) + * give somewhat accurate results for -88 <= x <= 88 + * + * exp(x) = 2^(x/ln(2)) + * we use the properties of float encoding + * to get a fast 2^ and linear interpolation + * + */ + +inline float fastexpf(float y) __attribute__((const)); + +inline float fastexpf(float y) +{ + union { + float r; + int32_t i; + } u; + + // 127*ln(2) = 88 + if (y < -88.0f) { + u.r = 0.0f; + } else if (y > 88.0f) { + u.r = INFINITY; + } else { + const float kOneOverLogTwo = (1L<<23) / M_LN2; + const int32_t kExponentBias = 127L<<23; + const int32_t e = int32_t(y*kOneOverLogTwo); + u.i = e + kExponentBias; + } + + return u.r; +} + + +bool cmpf(GLfloat a, GLfloat b) { +#if DEBUG_USE_FLOATS + return a == b; +#else + union { + float f; + uint32_t i; + } ua, ub; + ua.f = a; + ub.f = b; + return ua.i == ub.i; +#endif +} + +bool isZerof(GLfloat v) { +#if DEBUG_USE_FLOATS + return v == 0; +#else + union { + float f; + int32_t i; + }; + f = v; + return (i<<1) == 0; +#endif +} + +bool isOnef(GLfloat v) { + return cmpf(v, 1.0f); +} + +int isZeroOrNegativef(GLfloat v) { +#if DEBUG_USE_FLOATS + return v <= 0; +#else + union { + float f; + int32_t i; + }; + f = v; + return isZerof(v) | (i>>31); +#endif +} + +int exponent(GLfloat v) { + union { + float f; + uint32_t i; + }; + f = v; + return ((i << 1) >> 24) - 127; +} + +int32_t mantissa(GLfloat v) { + union { + float f; + uint32_t i; + }; + f = v; + if (!(i&0x7F800000)) return 0; + const int s = i >> 31; + i |= (1L<<23); + i &= ~0xFF000000; + return s ? -i : i; +} + +GLfloat clampToZerof(GLfloat v) { +#if DEBUG_USE_FLOATS + return v<0 ? 0 : (v>1 ? 1 : v); +#else + union { + float f; + int32_t i; + }; + f = v; + i &= ~(i>>31); + return f; +#endif +} + +GLfloat reciprocalf(GLfloat v) { + // XXX: do better + return 1.0f / v; +} + +GLfloat rsqrtf(GLfloat v) { + // XXX: do better + return 1.0f / sqrtf(v); +} + +GLfloat sqrf(GLfloat v) { + // XXX: do better + return v*v; +} + +GLfloat addExpf(GLfloat v, int e) { + union { + float f; + int32_t i; + }; + f = v; + if (i<<1) { // XXX: deal with over/underflow + i += int32_t(e)<<23; + } + return f; +} + +GLfloat mul2f(GLfloat v) { +#if DEBUG_USE_FLOATS + return v*2; +#else + return addExpf(v, 1); +#endif +} + +GLfloat div2f(GLfloat v) { +#if DEBUG_USE_FLOATS + return v*0.5f; +#else + return addExpf(v, -1); +#endif +} + +GLfloat absf(GLfloat v) { +#if DEBUG_USE_FLOATS + return v<0 ? -v : v; +#else + union { + float f; + int32_t i; + }; + f = v; + i &= ~0x80000000; + return f; +#endif +} + +}; // namespace gl + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_OPENGLES_FP_H + diff --git a/opengl/libagl/iterators.S b/opengl/libagl/iterators.S new file mode 100644 index 000000000..daf2937ba --- /dev/null +++ b/opengl/libagl/iterators.S @@ -0,0 +1,88 @@ +/* libs/opengles/iterators.S +** +** Copyright 2006, 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. +*/ + + + .text + .align + .arm + + .global iterators0032 + +/* + * iterators0032 + * + * MUST BE CALLED FROM ARM CODE + * + * r0: const compute_iterators_t* (this) + * r0 + 0: m_dx01 + * r0 + 4: m_dy10 + * r0 + 8: m_dx20 + * r0 +12: m_dy02 + * r0 +16: m_x0 + * r0 +20: m_y0 + * r0 +24: m_area + * r0 +28: m_scale + * r0 +29: m_area_scale; + * r1: int32_t* (out) + * r1 + 0: c + * r1 + 4: dcdx + * r1 + 8: dcdy + * r2: c0 + * r3: c1 + * [sp]: c2 + */ + +iterators0032: + stmfd sp!, {r4, r5, r6, r7, r8, lr} + ldr r4, [sp, #4*6] + + ldrb r12, [r0, #29] + sub r3, r3, r2 + sub r4, r4, r2 + sub r12, r12, #16 + mov r3, r3, asr r12 + mov r4, r4, asr r12 + + ldr r5, [r0, #0] + ldr r12, [r0, #4] + smull r8, lr, r4, r5 + ldr r5, [r0, #8] + smull r6, r7, r4, r12 + ldr r12, [r0, #12] + smlal r8, lr, r3, r5 + smlal r6, r7, r3, r12 + + ldr r3, [r0, #16] // m_x0 + ldr r4, [r0, #20] // m_x1 + + str r6, [r1, #4] + str r8, [r1, #8] + + umull r6, r5, r3, r6 + umull r8, r0, r4, r8 + mla r7, r3, r7, r5 + mla lr, r4, lr, r0 + adds r6, r6, r8 + adc r7, r7, lr + + movs r6, r6, lsr #4 + adc r6, r6, r7, lsl #28 + rsb r6, r6, r2, lsl #16 + str r6, [r1, #0] + + ldmfd sp!, {r4, r5, r6, r7, r8, pc} + diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp new file mode 100644 index 000000000..25c41d0c5 --- /dev/null +++ b/opengl/libagl/light.cpp @@ -0,0 +1,852 @@ +/* libs/opengles/light.cpp +** +** Copyright 2006, 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 "context.h" +#include "fp.h" +#include "light.h" +#include "state.h" +#include "matrix.h" + + +#if defined(__arm__) && defined(__thumb__) +#warning "light.cpp should not be compiled in thumb on ARM." +#endif + +namespace android { + +// ---------------------------------------------------------------------------- + +static void invalidate_lighting(ogles_context_t* c); +static void lightVertexValidate(ogles_context_t* c, vertex_t* v); +static void lightVertexNop(ogles_context_t* c, vertex_t* v); +static void lightVertex(ogles_context_t* c, vertex_t* v); +static void lightVertexMaterial(ogles_context_t* c, vertex_t* v); + +static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s); +static inline void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b); + +static __attribute__((noinline)) +void vnorm3(GLfixed* d, const GLfixed* a); + +static inline void vsa3(GLfixed* d, + const GLfixed* m, GLfixed s, const GLfixed* a); +static inline void vmla3(GLfixed* d, + const GLfixed* m0, const GLfixed* m1, const GLfixed* a); +static inline void vmul3(GLfixed* d, + const GLfixed* m0, const GLfixed* m1); + +static GLfixed fog_linear(ogles_context_t* c, GLfixed z); +static GLfixed fog_exp(ogles_context_t* c, GLfixed z); +static GLfixed fog_exp2(ogles_context_t* c, GLfixed z); + + +// ---------------------------------------------------------------------------- + +static void init_white(vec4_t& c) { + c.r = c.g = c.b = c.a = 0x10000; +} + +void ogles_init_light(ogles_context_t* c) +{ + for (unsigned int i=0 ; ilighting.lights[i].ambient.a = 0x10000; + c->lighting.lights[i].position.z = 0x10000; + c->lighting.lights[i].spotDir.z = -0x10000; + c->lighting.lights[i].spotCutoff = gglIntToFixed(180); + c->lighting.lights[i].attenuation[0] = 0x10000; + } + init_white(c->lighting.lights[0].diffuse); + init_white(c->lighting.lights[0].specular); + + c->lighting.front.ambient.r = + c->lighting.front.ambient.g = + c->lighting.front.ambient.b = gglFloatToFixed(0.2f); + c->lighting.front.ambient.a = 0x10000; + c->lighting.front.diffuse.r = + c->lighting.front.diffuse.g = + c->lighting.front.diffuse.b = gglFloatToFixed(0.8f); + c->lighting.front.diffuse.a = 0x10000; + c->lighting.front.specular.a = 0x10000; + c->lighting.front.emission.a = 0x10000; + + c->lighting.lightModel.ambient.r = + c->lighting.lightModel.ambient.g = + c->lighting.lightModel.ambient.b = gglFloatToFixed(0.2f); + c->lighting.lightModel.ambient.a = 0x10000; + + c->lighting.colorMaterial.face = GL_FRONT_AND_BACK; + c->lighting.colorMaterial.mode = GL_AMBIENT_AND_DIFFUSE; + + c->fog.mode = GL_EXP; + c->fog.fog = fog_exp; + c->fog.density = 0x10000; + c->fog.end = 0x10000; + c->fog.invEndMinusStart = 0x10000; + + invalidate_lighting(c); + + c->rasterizer.procs.shadeModel(c, GL_SMOOTH); + c->lighting.shadeModel = GL_SMOOTH; +} + +void ogles_uninit_light(ogles_context_t* c) +{ +} + +static inline int32_t clampF(GLfixed f) CONST; +int32_t clampF(GLfixed f) { + f = (f & ~(f>>31)); + if (f >= 0x10000) + f = 0x10000; + return f; +} + +static GLfixed fog_linear(ogles_context_t* c, GLfixed z) { + return clampF(gglMulx((c->fog.end - ((z<0)?-z:z)), c->fog.invEndMinusStart)); +} + +static GLfixed fog_exp(ogles_context_t* c, GLfixed z) { + const float e = fixedToFloat(gglMulx(c->fog.density, ((z<0)?-z:z))); + return clampF(gglFloatToFixed(fastexpf(-e))); +} + +static GLfixed fog_exp2(ogles_context_t* c, GLfixed z) { + const float e = fixedToFloat(gglMulx(c->fog.density, z)); + return clampF(gglFloatToFixed(fastexpf(-e*e))); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark math helpers +#endif + +static inline +void vscale3(GLfixed* d, const GLfixed* m, GLfixed s) { + d[0] = gglMulx(m[0], s); + d[1] = gglMulx(m[1], s); + d[2] = gglMulx(m[2], s); +} + +static inline +void vsa3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) { + d[0] = gglMulAddx(m[0], s, a[0]); + d[1] = gglMulAddx(m[1], s, a[1]); + d[2] = gglMulAddx(m[2], s, a[2]); +} + +static inline +void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b) { + const GLfixed wa = a[3]; + const GLfixed wb = b[3]; + if (ggl_likely(wa == wb)) { + d[0] = a[0] - b[0]; + d[1] = a[1] - b[1]; + d[2] = a[2] - b[2]; + } else { + d[0] = gglMulSubx(a[0], wb, gglMulx(b[0], wa)); + d[1] = gglMulSubx(a[1], wb, gglMulx(b[1], wa)); + d[2] = gglMulSubx(a[2], wb, gglMulx(b[2], wa)); + } +} + +static inline +void vmla3(GLfixed* d, + const GLfixed* m0, const GLfixed* m1, const GLfixed* a) +{ + d[0] = gglMulAddx(m0[0], m1[0], a[0]); + d[1] = gglMulAddx(m0[1], m1[1], a[1]); + d[2] = gglMulAddx(m0[2], m1[2], a[2]); +} + +static inline +void vmul3(GLfixed* d, const GLfixed* m0, const GLfixed* m1) { + d[0] = gglMulx(m0[0], m1[0]); + d[1] = gglMulx(m0[1], m1[1]); + d[2] = gglMulx(m0[2], m1[2]); +} + +void vnorm3(GLfixed* d, const GLfixed* a) +{ + // we must take care of overflows when normalizing a vector + GLfixed n; + int32_t x = a[0]; x = x>=0 ? x : -x; + int32_t y = a[1]; y = y>=0 ? y : -y; + int32_t z = a[2]; z = z>=0 ? z : -z; + if (ggl_likely(x<=0x6800 && y<=0x6800 && z<= 0x6800)) { + // in this case this will all fit on 32 bits + n = x*x + y*y + z*z; + n = gglSqrtRecipx(n); + n <<= 8; + } else { + // here norm^2 is at least 0x7EC00000 (>>32 == 0.495117) + n = vsquare3(x, y, z); + n = gglSqrtRecipx(n); + } + vscale3(d, a, n); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark lighting equations +#endif + +static inline void light_picker(ogles_context_t* c) +{ + if (ggl_likely(!c->lighting.enable)) { + c->lighting.lightVertex = lightVertexNop; + return; + } + if (c->lighting.colorMaterial.enable) { + c->lighting.lightVertex = lightVertexMaterial; + } else { + c->lighting.lightVertex = lightVertex; + } +} + +static inline void validate_light_mvi(ogles_context_t* c) +{ + uint32_t en = c->lighting.enabledLights; + while (en) { + const int i = 31 - gglClz(en); + en &= ~(1<lighting.lights[i]; + c->transforms.mvui.point3(&c->transforms.mvui, + &l.objPosition, &l.position); + vnorm3(l.normalizedObjPosition.v, l.objPosition.v); + } +} + +static inline void validate_light(ogles_context_t* c) +{ + // if colorMaterial is enabled, we get the color from the vertex + if (!c->lighting.colorMaterial.enable) { + material_t& material = c->lighting.front; + uint32_t en = c->lighting.enabledLights; + while (en) { + const int i = 31 - gglClz(en); + en &= ~(1<lighting.lights[i]; + vmul3(l.implicitAmbient.v, material.ambient.v, l.ambient.v); + vmul3(l.implicitDiffuse.v, material.diffuse.v, l.diffuse.v); + vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v); + + // this is just a flag to tell if we have a specular component + l.implicitSpecular.v[3] = + l.implicitSpecular.r | + l.implicitSpecular.g | + l.implicitSpecular.b; + + l.rConstAttenuation = (l.attenuation[1] | l.attenuation[2])==0; + if (l.rConstAttenuation) + l.rConstAttenuation = gglRecipFast(l.attenuation[0]); + } + // emission and ambient for the whole scene + vmla3( c->lighting.implicitSceneEmissionAndAmbient.v, + c->lighting.lightModel.ambient.v, + material.ambient.v, + material.emission.v); + c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a; + } + validate_light_mvi(c); +} + +void invalidate_lighting(ogles_context_t* c) +{ + // TODO: pick lightVertexValidate or lightVertexValidateMVI + // instead of systematically the heavier lightVertexValidate() + c->lighting.lightVertex = lightVertexValidate; +} + +void ogles_invalidate_lighting_mvui(ogles_context_t* c) +{ + invalidate_lighting(c); +} + +void lightVertexNop(ogles_context_t*, vertex_t* v) +{ + // we should never end-up here +} + +void lightVertexValidateMVI(ogles_context_t* c, vertex_t* v) +{ + validate_light_mvi(c); + light_picker(c); + c->lighting.lightVertex(c, v); +} + +void lightVertexValidate(ogles_context_t* c, vertex_t* v) +{ + validate_light(c); + light_picker(c); + c->lighting.lightVertex(c, v); +} + +void lightVertexMaterial(ogles_context_t* c, vertex_t* v) +{ + // fetch the material color + const GLvoid* cp = c->arrays.color.element( + v->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v->color.v, cp); + + // acquire the color-material from the vertex + material_t& material = c->lighting.front; + material.ambient = + material.diffuse = v->color; + // implicit arguments need to be computed per/vertex + uint32_t en = c->lighting.enabledLights; + while (en) { + const int i = 31 - gglClz(en); + en &= ~(1<lighting.lights[i]; + vmul3(l.implicitAmbient.v, material.ambient.v, l.ambient.v); + vmul3(l.implicitDiffuse.v, material.diffuse.v, l.diffuse.v); + vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v); + } + // emission and ambient for the whole scene + vmla3( c->lighting.implicitSceneEmissionAndAmbient.v, + c->lighting.lightModel.ambient.v, + material.ambient.v, + material.emission.v); + c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a; + + // now we can light our vertex as usual + lightVertex(c, v); +} + +void lightVertex(ogles_context_t* c, vertex_t* v) +{ + // emission and ambient for the whole scene + vec4_t r = c->lighting.implicitSceneEmissionAndAmbient; + + uint32_t en = c->lighting.enabledLights; + if (ggl_likely(en)) { + // since we do the lighting in object-space, we don't need to + // transform each normal. However, we might still have to normalize + // it if GL_NORMALIZE is enabled. + vec4_t n; + c->arrays.normal.fetch(c, n.v, + c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK)); + if (c->transforms.rescaleNormals == GL_NORMALIZE) + vnorm3(n.v, n.v); + + const material_t& material = c->lighting.front; + const int twoSide = c->lighting.lightModel.twoSide; + + while (en) { + const int i = 31 - gglClz(en); + en &= ~(1<lighting.lights[i]; + + vec4_t d, t; + GLfixed s; + GLfixed sqDist = 0x10000; + + // compute vertex-to-light vector + if (ggl_unlikely(l.position.w)) { + vsub3w(d.v, l.objPosition.v, v->obj.v); + sqDist = dot3(d.v, d.v); + vscale3(d.v, d.v, gglSqrtRecipx(sqDist)); + } else { + // TODO: avoid copy here + d = l.normalizedObjPosition; + } + + // ambient & diffuse + s = dot3(n.v, d.v); + s = (s<0) ? (twoSide?(-s):0) : s; + vsa3(t.v, l.implicitDiffuse.v, s, l.implicitAmbient.v); + + // specular + if (ggl_unlikely(s && l.implicitSpecular.v[3])) { + vec4_t h; + h.x = d.x; + h.y = d.y; + h.z = d.z + 0x10000; + vnorm3(h.v, h.v); + s = dot3(n.v, h.v); + s = (s<0) ? (twoSide?(-s):0) : s; + if (s > 0) { + s = gglPowx(s, material.shininess); + vsa3(t.v, l.implicitSpecular.v, s, t.v); + } + } + + // spot + if (ggl_unlikely(l.spotCutoff != gglIntToFixed(180))) { + GLfixed spotAtt = -dot3(l.normalizedSpotDir.v, d.v); + if (spotAtt >= l.spotCutoffCosine) { + vscale3(t.v, t.v, gglPowx(spotAtt, l.spotExp)); + } + } + + // attenuation + if (ggl_unlikely(l.position.w)) { + if (l.rConstAttenuation) { + s = l.rConstAttenuation; + } else { + s = gglMulAddx(sqDist, l.attenuation[2], l.attenuation[0]); + if (l.attenuation[1]) + s = gglMulAddx(gglSqrtx(sqDist), l.attenuation[1], s); + s = gglRecipFast(s); + } + vscale3(t.v, t.v, s); + } + + r.r += t.r; + r.g += t.g; + r.b += t.b; + } + } + v->color.r = gglClampx(r.r); + v->color.g = gglClampx(r.g); + v->color.b = gglClampx(r.b); + v->color.a = gglClampx(r.a); + v->flags |= vertex_t::LIT; +} + +static void lightModelx(GLenum pname, GLfixed param, ogles_context_t* c) +{ + if (ggl_unlikely(pname != GL_LIGHT_MODEL_TWO_SIDE)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->lighting.lightModel.twoSide = param ? GL_TRUE : GL_FALSE; + invalidate_lighting(c); +} + +static void lightx(GLenum i, GLenum pname, GLfixed param, ogles_context_t* c) +{ + if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + light_t& light = c->lighting.lights[i-GL_LIGHT0]; + const GLfixed kDegToRad = GLfixed((M_PI * gglIntToFixed(1)) / 180.0f); + switch (pname) { + case GL_SPOT_EXPONENT: + if (GGLfixed(param) >= gglIntToFixed(128)) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + light.spotExp = param; + break; + case GL_SPOT_CUTOFF: + if (param!=gglIntToFixed(180) && GGLfixed(param)>=gglIntToFixed(90)) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + light.spotCutoff = param; + light.spotCutoffCosine = + gglFloatToFixed(cosinef((M_PI/(180.0f*65536.0f))*param)); + break; + case GL_CONSTANT_ATTENUATION: + if (param < 0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + light.attenuation[0] = param; + break; + case GL_LINEAR_ATTENUATION: + if (param < 0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + light.attenuation[1] = param; + break; + case GL_QUADRATIC_ATTENUATION: + if (param < 0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + light.attenuation[2] = param; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + invalidate_lighting(c); +} + +static void lightxv(GLenum i, GLenum pname, const GLfixed *params, ogles_context_t* c) +{ + if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + GLfixed* what; + light_t& light = c->lighting.lights[i-GL_LIGHT0]; + switch (pname) { + case GL_AMBIENT: + what = light.ambient.v; + break; + case GL_DIFFUSE: + what = light.diffuse.v; + break; + case GL_SPECULAR: + what = light.specular.v; + break; + case GL_POSITION: { + ogles_validate_transform(c, transform_state_t::MODELVIEW); + transform_t& mv = c->transforms.modelview.transform; + memcpy(light.position.v, params, sizeof(light.position.v)); + mv.point4(&mv, &light.position, &light.position); + invalidate_lighting(c); + return; + } + case GL_SPOT_DIRECTION: { + ogles_validate_transform(c, transform_state_t::MVUI); + transform_t& mvui = c->transforms.mvui; + mvui.point3(&mvui, &light.spotDir, (vec4_t*)params); + vnorm3(light.normalizedSpotDir.v, light.spotDir.v); + invalidate_lighting(c); + return; + } + default: + lightx(i, pname, params[0], c); + return; + } + what[0] = params[0]; + what[1] = params[1]; + what[2] = params[2]; + what[3] = params[3]; + invalidate_lighting(c); +} + +static void materialx(GLenum face, GLenum pname, GLfixed param, ogles_context_t* c) +{ + if (ggl_unlikely(face != GL_FRONT_AND_BACK)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (ggl_unlikely(pname != GL_SHININESS)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->lighting.front.shininess = param; + invalidate_lighting(c); +} + +static void fogx(GLenum pname, GLfixed param, ogles_context_t* c) +{ + switch (pname) { + case GL_FOG_DENSITY: + if (param >= 0) { + c->fog.density = param; + break; + } + ogles_error(c, GL_INVALID_VALUE); + break; + case GL_FOG_START: + c->fog.start = param; + c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start); + break; + case GL_FOG_END: + c->fog.end = param; + c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start); + break; + case GL_FOG_MODE: + switch (param) { + case GL_LINEAR: + c->fog.mode = param; + c->fog.fog = fog_linear; + break; + case GL_EXP: + c->fog.mode = param; + c->fog.fog = fog_exp; + break; + case GL_EXP2: + c->fog.mode = param; + c->fog.fog = fog_exp2; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + break; + } + break; + default: + ogles_error(c, GL_INVALID_ENUM); + break; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + +#if 0 +#pragma mark - +#pragma mark lighting APIs +#endif + +void glShadeModel(GLenum mode) +{ + ogles_context_t* c = ogles_context_t::get(); + if (ggl_unlikely(mode != GL_SMOOTH && mode != GL_FLAT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->lighting.shadeModel = mode; +} + +void glLightModelf(GLenum pname, GLfloat param) +{ + ogles_context_t* c = ogles_context_t::get(); + lightModelx(pname, gglFloatToFixed(param), c); +} + +void glLightModelx(GLenum pname, GLfixed param) +{ + ogles_context_t* c = ogles_context_t::get(); + lightModelx(pname, param, c); +} + +void glLightModelfv(GLenum pname, const GLfloat *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (pname == GL_LIGHT_MODEL_TWO_SIDE) { + lightModelx(pname, gglFloatToFixed(params[0]), c); + return; + } + + if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + c->lighting.lightModel.ambient.r = gglFloatToFixed(params[0]); + c->lighting.lightModel.ambient.g = gglFloatToFixed(params[1]); + c->lighting.lightModel.ambient.b = gglFloatToFixed(params[2]); + c->lighting.lightModel.ambient.a = gglFloatToFixed(params[3]); + invalidate_lighting(c); +} + +void glLightModelxv(GLenum pname, const GLfixed *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (pname == GL_LIGHT_MODEL_TWO_SIDE) { + lightModelx(pname, params[0], c); + return; + } + + if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + c->lighting.lightModel.ambient.r = params[0]; + c->lighting.lightModel.ambient.g = params[1]; + c->lighting.lightModel.ambient.b = params[2]; + c->lighting.lightModel.ambient.a = params[3]; + invalidate_lighting(c); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void glLightf(GLenum i, GLenum pname, GLfloat param) +{ + ogles_context_t* c = ogles_context_t::get(); + lightx(i, pname, gglFloatToFixed(param), c); +} + +void glLightx(GLenum i, GLenum pname, GLfixed param) +{ + ogles_context_t* c = ogles_context_t::get(); + lightx(i, pname, param, c); +} + +void glLightfv(GLenum i, GLenum pname, const GLfloat *params) +{ + ogles_context_t* c = ogles_context_t::get(); + switch (pname) { + case GL_SPOT_EXPONENT: + case GL_SPOT_CUTOFF: + case GL_CONSTANT_ATTENUATION: + case GL_LINEAR_ATTENUATION: + case GL_QUADRATIC_ATTENUATION: + lightx(i, pname, gglFloatToFixed(params[0]), c); + return; + } + + GLfixed paramsx[4]; + paramsx[0] = gglFloatToFixed(params[0]); + paramsx[1] = gglFloatToFixed(params[1]); + paramsx[2] = gglFloatToFixed(params[2]); + if (pname != GL_SPOT_DIRECTION) + paramsx[3] = gglFloatToFixed(params[3]); + + lightxv(i, pname, paramsx, c); +} + +void glLightxv(GLenum i, GLenum pname, const GLfixed *params) +{ + ogles_context_t* c = ogles_context_t::get(); + lightxv(i, pname, params, c); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void glMaterialf(GLenum face, GLenum pname, GLfloat param) +{ + ogles_context_t* c = ogles_context_t::get(); + materialx(face, pname, gglFloatToFixed(param), c); +} + +void glMaterialx(GLenum face, GLenum pname, GLfixed param) +{ + ogles_context_t* c = ogles_context_t::get(); + materialx(face, pname, param, c); +} + +void glMaterialfv( + GLenum face, GLenum pname, const GLfloat *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (ggl_unlikely(face != GL_FRONT_AND_BACK)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + GLfixed* what=0; + GLfixed* other=0; + switch (pname) { + case GL_AMBIENT: what = c->lighting.front.ambient.v; break; + case GL_DIFFUSE: what = c->lighting.front.diffuse.v; break; + case GL_SPECULAR: what = c->lighting.front.specular.v; break; + case GL_EMISSION: what = c->lighting.front.emission.v; break; + case GL_AMBIENT_AND_DIFFUSE: + what = c->lighting.front.ambient.v; break; + other = c->lighting.front.diffuse.v; break; + break; + case GL_SHININESS: + c->lighting.front.shininess = gglFloatToFixed(params[0]); + invalidate_lighting(c); + return; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + what[0] = gglFloatToFixed(params[0]); + what[1] = gglFloatToFixed(params[1]); + what[2] = gglFloatToFixed(params[2]); + what[3] = gglFloatToFixed(params[3]); + if (other) { + other[0] = what[0]; + other[1] = what[1]; + other[2] = what[2]; + other[3] = what[3]; + } + invalidate_lighting(c); +} + +void glMaterialxv( + GLenum face, GLenum pname, const GLfixed *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (ggl_unlikely(face != GL_FRONT_AND_BACK)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + GLfixed* what=0; + GLfixed* other=0; + switch (pname) { + case GL_AMBIENT: what = c->lighting.front.ambient.v; break; + case GL_DIFFUSE: what = c->lighting.front.diffuse.v; break; + case GL_SPECULAR: what = c->lighting.front.specular.v; break; + case GL_EMISSION: what = c->lighting.front.emission.v; break; + case GL_AMBIENT_AND_DIFFUSE: + what = c->lighting.front.ambient.v; break; + other= c->lighting.front.diffuse.v; break; + break; + case GL_SHININESS: + c->lighting.front.shininess = gglFloatToFixed(params[0]); + invalidate_lighting(c); + return; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + what[0] = params[0]; + what[1] = params[1]; + what[2] = params[2]; + what[3] = params[3]; + if (other) { + other[0] = what[0]; + other[1] = what[1]; + other[2] = what[2]; + other[3] = what[3]; + } + invalidate_lighting(c); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark fog +#endif + +void glFogf(GLenum pname, GLfloat param) { + ogles_context_t* c = ogles_context_t::get(); + GLfixed paramx = (GLfixed)param; + if (pname != GL_FOG_MODE) + paramx = gglFloatToFixed(param); + fogx(pname, paramx, c); +} + +void glFogx(GLenum pname, GLfixed param) { + ogles_context_t* c = ogles_context_t::get(); + fogx(pname, param, c); +} + +void glFogfv(GLenum pname, const GLfloat *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (pname != GL_FOG_COLOR) { + GLfixed paramx = (GLfixed)params[0]; + if (pname != GL_FOG_MODE) + paramx = gglFloatToFixed(params[0]); + fogx(pname, paramx, c); + return; + } + GLfixed paramsx[4]; + paramsx[0] = gglFloatToFixed(params[0]); + paramsx[1] = gglFloatToFixed(params[1]); + paramsx[2] = gglFloatToFixed(params[2]); + paramsx[3] = gglFloatToFixed(params[3]); + c->rasterizer.procs.fogColor3xv(c, paramsx); +} + +void glFogxv(GLenum pname, const GLfixed *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (pname != GL_FOG_COLOR) { + fogx(pname, params[0], c); + return; + } + c->rasterizer.procs.fogColor3xv(c, params); +} diff --git a/opengl/libagl/light.h b/opengl/libagl/light.h new file mode 100644 index 000000000..6dae25f53 --- /dev/null +++ b/opengl/libagl/light.h @@ -0,0 +1,38 @@ +/* libs/opengles/light.h +** +** Copyright 2006, 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. +*/ + +#ifndef ANDROID_OPENGLES_LIGHT_H +#define ANDROID_OPENGLES_LIGHT_H + +#include +#include +#include + +namespace android { + +namespace gl { +struct ogles_context_t; +}; + +void ogles_init_light(ogles_context_t* c); +void ogles_uninit_light(ogles_context_t* c); +void ogles_invalidate_lighting_mvui(ogles_context_t* c); + +}; // namespace android + +#endif // ANDROID_OPENGLES_LIGHT_H + diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp new file mode 100644 index 000000000..f175cdad6 --- /dev/null +++ b/opengl/libagl/matrix.cpp @@ -0,0 +1,1145 @@ +/* libs/opengles/matrix.cpp +** +** Copyright 2006, 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 "context.h" +#include "fp.h" +#include "state.h" +#include "matrix.h" +#include "vertex.h" +#include "light.h" + +#if defined(__arm__) && defined(__thumb__) +#warning "matrix.cpp should not be compiled in thumb on ARM." +#endif + +#define I(_i, _j) ((_j)+ 4*(_i)) + +namespace android { + +// ---------------------------------------------------------------------------- + +static const GLfloat gIdentityf[16] = { 1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1 }; + +static const matrixx_t gIdentityx = { + { 0x10000,0,0,0, + 0,0x10000,0,0, + 0,0,0x10000,0, + 0,0,0,0x10000 + } + }; + +static void point2__nop(transform_t const*, vec4_t* c, vec4_t const* o); +static void point3__nop(transform_t const*, vec4_t* c, vec4_t const* o); +static void point4__nop(transform_t const*, vec4_t* c, vec4_t const* o); +static void normal__nop(transform_t const*, vec4_t* c, vec4_t const* o); +static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o); +static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o); +static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o); +static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o); + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void ogles_init_matrix(ogles_context_t* c) +{ + c->transforms.modelview.init(OGLES_MODELVIEW_STACK_DEPTH); + c->transforms.projection.init(OGLES_PROJECTION_STACK_DEPTH); + for (int i=0; itransforms.texture[i].init(OGLES_TEXTURE_STACK_DEPTH); + + c->transforms.current = &c->transforms.modelview; + c->transforms.matrixMode = GL_MODELVIEW; + c->transforms.dirty = transform_state_t::VIEWPORT | + transform_state_t::MVUI | + transform_state_t::MVIT | + transform_state_t::MVP; + c->transforms.mvp.loadIdentity(); + c->transforms.mvp4.loadIdentity(); + c->transforms.mvit4.loadIdentity(); + c->transforms.mvui.loadIdentity(); + c->transforms.vpt.loadIdentity(); + c->transforms.vpt.zNear = 0.0f; + c->transforms.vpt.zFar = 1.0f; +} + +void ogles_uninit_matrix(ogles_context_t* c) +{ + c->transforms.modelview.uninit(); + c->transforms.projection.uninit(); + for (int i=0; itransforms.texture[i].uninit(); +} + +static void validate_perspective(ogles_context_t* c, vertex_t* v) +{ + const uint32_t enables = c->rasterizer.state.enables; + c->arrays.perspective = (c->clipPlanes.enable) ? + ogles_vertex_clipAllPerspective3D : ogles_vertex_perspective3D; + if (enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) { + c->arrays.perspective = ogles_vertex_perspective3DZ; + if (c->clipPlanes.enable || (enables&GGL_ENABLE_FOG)) + c->arrays.perspective = ogles_vertex_clipAllPerspective3DZ; + } + if ((c->arrays.vertex.size != 4) && + (c->transforms.mvp4.flags & transform_t::FLAGS_2D_PROJECTION)) { + c->arrays.perspective = ogles_vertex_perspective2D; + } + c->arrays.perspective(c, v); +} + +void ogles_invalidate_perspective(ogles_context_t* c) +{ + c->arrays.perspective = validate_perspective; +} + +void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want) +{ + int dirty = c->transforms.dirty & want; + + // Validate the modelview + if (dirty & transform_state_t::MODELVIEW) { + c->transforms.modelview.validate(); + } + + // Validate the projection stack (in fact, it's never needed) + if (dirty & transform_state_t::PROJECTION) { + c->transforms.projection.validate(); + } + + // Validate the viewport transformation + if (dirty & transform_state_t::VIEWPORT) { + vp_transform_t& vpt = c->transforms.vpt; + vpt.transform.matrix.load(vpt.matrix); + vpt.transform.picker(); + } + + // We need to update the mvp (used to transform each vertex) + if (dirty & transform_state_t::MVP) { + c->transforms.update_mvp(); + // invalidate perspective (divide by W) and view volume clipping + ogles_invalidate_perspective(c); + } + + // Validate the mvui (for normal transformation) + if (dirty & transform_state_t::MVUI) { + c->transforms.update_mvui(); + ogles_invalidate_lighting_mvui(c); + } + + // Validate the texture stack + if (dirty & transform_state_t::TEXTURE) { + for (int i=0; itransforms.texture[i].validate(); + } + + // Validate the mvit4 (user-clip planes) + if (dirty & transform_state_t::MVIT) { + c->transforms.update_mvit(); + } + + c->transforms.dirty &= ~want; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark transform_t +#endif + +void transform_t::loadIdentity() { + matrix = gIdentityx; + flags = 0; + ops = OP_IDENTITY; + point2 = point2__nop; + point3 = point3__nop; + point4 = point4__nop; +} + + +static inline +int notZero(GLfixed v) { + return abs(v) & ~0x3; +} + +static inline +int notOne(GLfixed v) { + return notZero(v - 0x10000); +} + +void transform_t::picker() +{ + const GLfixed* const m = matrix.m; + + // XXX: picker needs to be smarter + flags = 0; + ops = OP_ALL; + point2 = point2__generic; + point3 = point3__generic; + point4 = point4__generic; + + // find out if this is a 2D projection + if (!(notZero(m[3]) | notZero(m[7]) | notZero(m[11]) | notOne(m[15]))) { + flags |= FLAGS_2D_PROJECTION; + } +} + +void mvui_transform_t::picker() +{ + flags = 0; + ops = OP_ALL; + point3 = normal__generic; +} + +void transform_t::dump(const char* what) +{ + GLfixed const * const m = matrix.m; + LOGD("%s:", what); + for (int i=0 ; i<4 ; i++) + LOGD("[%08x %08x %08x %08x] [%f %f %f %f]\n", + m[I(0,i)], m[I(1,i)], m[I(2,i)], m[I(3,i)], + fixedToFloat(m[I(0,i)]), + fixedToFloat(m[I(1,i)]), + fixedToFloat(m[I(2,i)]), + fixedToFloat(m[I(3,i)])); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark matrixx_t +#endif + +void matrixx_t::load(const matrixf_t& rhs) { + GLfixed* xp = m; + GLfloat const* fp = rhs.elements(); + unsigned int i = 16; + do { + const GLfloat f = *fp++; + *xp++ = isZerof(f) ? 0 : gglFloatToFixed(f); + } while (--i); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark matrixf_t +#endif + +void matrixf_t::multiply(matrixf_t& r, const matrixf_t& lhs, const matrixf_t& rhs) +{ + GLfloat const* const m = lhs.m; + for (int i=0 ; i<4 ; i++) { + register const float rhs_i0 = rhs.m[ I(i,0) ]; + register float ri0 = m[ I(0,0) ] * rhs_i0; + register float ri1 = m[ I(0,1) ] * rhs_i0; + register float ri2 = m[ I(0,2) ] * rhs_i0; + register float ri3 = m[ I(0,3) ] * rhs_i0; + for (int j=1 ; j<4 ; j++) { + register const float rhs_ij = rhs.m[ I(i,j) ]; + ri0 += m[ I(j,0) ] * rhs_ij; + ri1 += m[ I(j,1) ] * rhs_ij; + ri2 += m[ I(j,2) ] * rhs_ij; + ri3 += m[ I(j,3) ] * rhs_ij; + } + r.m[ I(i,0) ] = ri0; + r.m[ I(i,1) ] = ri1; + r.m[ I(i,2) ] = ri2; + r.m[ I(i,3) ] = ri3; + } +} + +void matrixf_t::dump(const char* what) { + LOGD("%s", what); + LOGD("[ %9f %9f %9f %9f ]", m[I(0,0)], m[I(1,0)], m[I(2,0)], m[I(3,0)]); + LOGD("[ %9f %9f %9f %9f ]", m[I(0,1)], m[I(1,1)], m[I(2,1)], m[I(3,1)]); + LOGD("[ %9f %9f %9f %9f ]", m[I(0,2)], m[I(1,2)], m[I(2,2)], m[I(3,2)]); + LOGD("[ %9f %9f %9f %9f ]", m[I(0,3)], m[I(1,3)], m[I(2,3)], m[I(3,3)]); +} + +void matrixf_t::loadIdentity() { + memcpy(m, gIdentityf, sizeof(m)); +} + +void matrixf_t::set(const GLfixed* rhs) { + load(rhs); +} + +void matrixf_t::set(const GLfloat* rhs) { + load(rhs); +} + +void matrixf_t::load(const GLfixed* rhs) { + GLfloat* fp = m; + unsigned int i = 16; + do { + *fp++ = fixedToFloat(*rhs++); + } while (--i); +} + +void matrixf_t::load(const GLfloat* rhs) { + memcpy(m, rhs, sizeof(m)); +} + +void matrixf_t::load(const matrixf_t& rhs) { + operator = (rhs); +} + +void matrixf_t::multiply(const matrixf_t& rhs) { + matrixf_t r; + multiply(r, *this, rhs); + operator = (r); +} + +void matrixf_t::translate(GLfloat x, GLfloat y, GLfloat z) { + for (int i=0 ; i<4 ; i++) { + m[12+i] += m[i]*x + m[4+i]*y + m[8+i]*z; + } +} + +void matrixf_t::scale(GLfloat x, GLfloat y, GLfloat z) { + for (int i=0 ; i<4 ; i++) { + m[ i] *= x; + m[4+i] *= y; + m[8+i] *= z; + } +} + +void matrixf_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z) +{ + matrixf_t rotation; + GLfloat* r = rotation.m; + GLfloat c, s; + r[3] = 0; r[7] = 0; r[11]= 0; + r[12]= 0; r[13]= 0; r[14]= 0; r[15]= 1; + a *= GLfloat(M_PI / 180.0f); + sincosf(a, &s, &c); + if (isOnef(x) && isZerof(y) && isZerof(z)) { + r[5] = c; r[10]= c; + r[6] = s; r[9] = -s; + r[1] = 0; r[2] = 0; + r[4] = 0; r[8] = 0; + r[0] = 1; + } else if (isZerof(x) && isOnef(y) && isZerof(z)) { + r[0] = c; r[10]= c; + r[8] = s; r[2] = -s; + r[1] = 0; r[4] = 0; + r[6] = 0; r[9] = 0; + r[5] = 1; + } else if (isZerof(x) && isZerof(y) && isOnef(z)) { + r[0] = c; r[5] = c; + r[1] = s; r[4] = -s; + r[2] = 0; r[6] = 0; + r[8] = 0; r[9] = 0; + r[10]= 1; + } else { + const GLfloat len = sqrtf(x*x + y*y + z*z); + if (!isOnef(len)) { + const GLfloat recipLen = reciprocalf(len); + x *= recipLen; + y *= recipLen; + z *= recipLen; + } + const GLfloat nc = 1.0f - c; + const GLfloat xy = x * y; + const GLfloat yz = y * z; + const GLfloat zx = z * x; + const GLfloat xs = x * s; + const GLfloat ys = y * s; + const GLfloat zs = z * s; + r[ 0] = x*x*nc + c; r[ 4] = xy*nc - zs; r[ 8] = zx*nc + ys; + r[ 1] = xy*nc + zs; r[ 5] = y*y*nc + c; r[ 9] = yz*nc - xs; + r[ 2] = zx*nc - ys; r[ 6] = yz*nc + xs; r[10] = z*z*nc + c; + } + multiply(rotation); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark matrix_stack_t +#endif + +void matrix_stack_t::init(int depth) { + stack = new matrixf_t[depth]; + ops = new uint8_t[depth]; + maxDepth = depth; + depth = 0; + dirty = 0; + loadIdentity(); +} + +void matrix_stack_t::uninit() { + delete [] stack; + delete [] ops; +} + +void matrix_stack_t::loadIdentity() { + transform.loadIdentity(); + stack[depth].loadIdentity(); + ops[depth] = OP_IDENTITY; +} + +void matrix_stack_t::load(const GLfixed* rhs) +{ + memcpy(transform.matrix.m, rhs, sizeof(transform.matrix.m)); + stack[depth].load(rhs); + ops[depth] = OP_ALL; // TODO: we should look at the matrix +} + +void matrix_stack_t::load(const GLfloat* rhs) +{ + stack[depth].load(rhs); + ops[depth] = OP_ALL; // TODO: we should look at the matrix +} + +void matrix_stack_t::multiply(const matrixf_t& rhs) +{ + stack[depth].multiply(rhs); + ops[depth] = OP_ALL; // TODO: we should look at the matrix +} + +void matrix_stack_t::translate(GLfloat x, GLfloat y, GLfloat z) +{ + stack[depth].translate(x,y,z); + ops[depth] |= OP_TRANSLATE; +} + +void matrix_stack_t::scale(GLfloat x, GLfloat y, GLfloat z) +{ + stack[depth].scale(x,y,z); + if (x==y && y==z) { + ops[depth] |= OP_UNIFORM_SCALE; + } else { + ops[depth] |= OP_SCALE; + } +} + +void matrix_stack_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z) +{ + stack[depth].rotate(a,x,y,z); + ops[depth] |= OP_ROTATE; +} + +void matrix_stack_t::validate() +{ + if (dirty & DO_FLOAT_TO_FIXED) { + transform.matrix.load(top()); + } + if (dirty & DO_PICKER) { + transform.picker(); + } + dirty = 0; +} + +GLint matrix_stack_t::push() +{ + if (depth >= (maxDepth-1)) { + return GL_STACK_OVERFLOW; + } + stack[depth+1] = stack[depth]; + ops[depth+1] = ops[depth]; + depth++; + return 0; +} + +GLint matrix_stack_t::pop() +{ + if (depth == 0) { + return GL_STACK_UNDERFLOW; + } + depth--; + return 0; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark vp_transform_t +#endif + +void vp_transform_t::loadIdentity() { + transform.loadIdentity(); + matrix.loadIdentity(); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark transform_state_t +#endif + +void transform_state_t::invalidate() +{ + switch (matrixMode) { + case GL_MODELVIEW: dirty |= MODELVIEW | MVP | MVUI | MVIT; break; + case GL_PROJECTION: dirty |= PROJECTION | MVP; break; + case GL_TEXTURE: dirty |= TEXTURE | MVP; break; + } + current->dirty = matrix_stack_t::DO_PICKER | + matrix_stack_t::DO_FLOAT_TO_FIXED; +} + +void transform_state_t::update_mvp() +{ + matrixf_t temp_mvp; + matrixf_t::multiply(temp_mvp, projection.top(), modelview.top()); + mvp4.matrix.load(temp_mvp); + mvp4.picker(); + + if (mvp4.flags & transform_t::FLAGS_2D_PROJECTION) { + // the mvp matrix doesn't transform W, in this case we can + // premultiply it with the viewport transformation. In addition to + // being more efficient, this is also much more accurate and in fact + // is needed for 2D drawing with a resulting 1:1 mapping. + matrixf_t mvpv; + matrixf_t::multiply(mvpv, vpt.matrix, temp_mvp); + mvp.matrix.load(mvpv); + mvp.picker(); + } else { + mvp = mvp4; + } +} + +static inline +GLfloat det22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) { + return a*d - b*c; +} + +static inline +GLfloat ndet22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) { + return b*c - a*d; +} + +static __attribute__((noinline)) +void invert(GLfloat* inverse, const GLfloat* src) +{ + double t; + int i, j, k, swap; + GLfloat tmp[4][4]; + + memcpy(inverse, gIdentityf, sizeof(gIdentityf)); + memcpy(tmp, src, sizeof(GLfloat)*16); + + for (i = 0; i < 4; i++) { + // look for largest element in column + swap = i; + for (j = i + 1; j < 4; j++) { + if (fabs(tmp[j][i]) > fabs(tmp[i][i])) { + swap = j; + } + } + + if (swap != i) { + /* swap rows. */ + for (k = 0; k < 4; k++) { + t = tmp[i][k]; + tmp[i][k] = tmp[swap][k]; + tmp[swap][k] = t; + + t = inverse[i*4+k]; + inverse[i*4+k] = inverse[swap*4+k]; + inverse[swap*4+k] = t; + } + } + + t = 1.0f / tmp[i][i]; + for (k = 0; k < 4; k++) { + tmp[i][k] *= t; + inverse[i*4+k] *= t; + } + for (j = 0; j < 4; j++) { + if (j != i) { + t = tmp[j][i]; + for (k = 0; k < 4; k++) { + tmp[j][k] -= tmp[i][k]*t; + inverse[j*4+k] -= inverse[i*4+k]*t; + } + } + } + } +} + +void transform_state_t::update_mvit() +{ + GLfloat r[16]; + const GLfloat* const mv = modelview.top().elements(); + invert(r, mv); + // convert to fixed-point and transpose + GLfixed* const x = mvit4.matrix.m; + for (int i=0 ; i<4 ; i++) + for (int j=0 ; j<4 ; j++) + x[I(i,j)] = gglFloatToFixed(r[I(j,i)]); + mvit4.picker(); +} + +void transform_state_t::update_mvui() +{ + const GLfloat* const mv = modelview.top().elements(); + + /* + When transforming normals, we can use the upper 3x3 matrix, see: + http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html + */ + + // Also note that: + // l(obj) = tr(M).l(eye) for infinite light + // l(obj) = inv(M).l(eye) for local light + + const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE; + if (ggl_likely((!(ops & ~OP_ROTATE)) || + (rescaleNormals && modelview.isRigidBody()))) { + // if the modelview matrix is a rigid body transformation + // (translation, rotation, uniform scaling), then we can bypass + // the inverse by transposing the matrix. + GLfloat rescale = 1.0f; + if (rescaleNormals == GL_RESCALE_NORMAL) { + if (!(ops & ~OP_UNIFORM_SCALE)) { + rescale = reciprocalf(mv[I(0,0)]); + } else { + rescale = rsqrtf( + sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)])); + } + } + GLfixed* const x = mvui.matrix.m; + for (int i=0 ; i<3 ; i++) { + x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale); + x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale); + x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale); + } + mvui.picker(); + return; + } + + GLfloat r[3][3]; + r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]); + r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]); + r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]); + r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]); + r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]); + r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]); + r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]); + r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]); + r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]); + + GLfloat rdet; + if (rescaleNormals == GL_RESCALE_NORMAL) { + rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2])); + } else { + rdet = reciprocalf( + r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]); + } + + GLfixed* const x = mvui.matrix.m; + for (int i=0 ; i<3 ; i++) { + x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet); + x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet); + x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet); + } + mvui.picker(); +} + + +// ---------------------------------------------------------------------------- +// transformation and matrices API +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark transformation and matrices API +#endif + +int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y) +{ + c->viewport.surfaceport.x = x; + c->viewport.surfaceport.y = y; + + ogles_viewport(c, + c->viewport.x, + c->viewport.y, + c->viewport.w, + c->viewport.h); + + ogles_scissor(c, + c->viewport.scissor.x, + c->viewport.scissor.y, + c->viewport.scissor.w, + c->viewport.scissor.h); + + return 0; +} + +void ogles_scissor(ogles_context_t* c, + GLint x, GLint y, GLsizei w, GLsizei h) +{ + if ((w|h) < 0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + c->viewport.scissor.x = x; + c->viewport.scissor.y = y; + c->viewport.scissor.w = w; + c->viewport.scissor.h = h; + + x += c->viewport.surfaceport.x; + y += c->viewport.surfaceport.y; + + y = c->rasterizer.state.buffers.color.height - (y + h); + c->rasterizer.procs.scissor(c, x, y, w, h); +} + +void ogles_viewport(ogles_context_t* c, + GLint x, GLint y, GLsizei w, GLsizei h) +{ + if ((w|h)<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + c->viewport.x = x; + c->viewport.y = y; + c->viewport.w = w; + c->viewport.h = h; + + x += c->viewport.surfaceport.x; + y += c->viewport.surfaceport.y; + + GLint H = c->rasterizer.state.buffers.color.height; + GLfloat sx = div2f(w); + GLfloat ox = sx + x; + GLfloat sy = div2f(h); + GLfloat oy = sy - y + (H - h); + + GLfloat near = c->transforms.vpt.zNear; + GLfloat far = c->transforms.vpt.zFar; + GLfloat A = div2f(far - near); + GLfloat B = div2f(far + near); + + // compute viewport matrix + GLfloat* const f = c->transforms.vpt.matrix.editElements(); + f[0] = sx; f[4] = 0; f[ 8] = 0; f[12] = ox; + f[1] = 0; f[5] =-sy; f[ 9] = 0; f[13] = oy; + f[2] = 0; f[6] = 0; f[10] = A; f[14] = B; + f[3] = 0; f[7] = 0; f[11] = 0; f[15] = 1; + c->transforms.dirty |= transform_state_t::VIEWPORT; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark matrix * vertex +#endif + +void point2__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { + const GLfixed* const m = mx->matrix.m; + const GLfixed rx = rhs->x; + const GLfixed ry = rhs->y; + lhs->x = mla2a(rx, m[ 0], ry, m[ 4], m[12]); + lhs->y = mla2a(rx, m[ 1], ry, m[ 5], m[13]); + lhs->z = mla2a(rx, m[ 2], ry, m[ 6], m[14]); + lhs->w = mla2a(rx, m[ 3], ry, m[ 7], m[15]); +} + +void point3__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { + const GLfixed* const m = mx->matrix.m; + const GLfixed rx = rhs->x; + const GLfixed ry = rhs->y; + const GLfixed rz = rhs->z; + lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); + lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]); + lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]); + lhs->w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]); +} + +void point4__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { + const GLfixed* const m = mx->matrix.m; + const GLfixed rx = rhs->x; + const GLfixed ry = rhs->y; + const GLfixed rz = rhs->z; + const GLfixed rw = rhs->w; + lhs->x = mla4(rx, m[ 0], ry, m[ 4], rz, m[ 8], rw, m[12]); + lhs->y = mla4(rx, m[ 1], ry, m[ 5], rz, m[ 9], rw, m[13]); + lhs->z = mla4(rx, m[ 2], ry, m[ 6], rz, m[10], rw, m[14]); + lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]); +} + +void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { + const GLfixed* const m = mx->matrix.m; + const GLfixed rx = rhs->x; + const GLfixed ry = rhs->y; + const GLfixed rz = rhs->z; + lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]); + lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]); + lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]); +} + + +void point2__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { + lhs->z = 0; + lhs->w = 0x10000; + if (lhs != rhs) { + lhs->x = rhs->x; + lhs->y = rhs->y; + } +} + +void point3__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { + lhs->w = 0x10000; + if (lhs != rhs) { + lhs->x = rhs->x; + lhs->y = rhs->y; + lhs->z = rhs->z; + } +} + +void point4__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { + if (lhs != rhs) + *lhs = *rhs; +} + + +static void frustumf( + GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar, + ogles_context_t* c) + { + if (cmpf(left,right) || + cmpf(top, bottom) || + cmpf(zNear, zFar) || + isZeroOrNegativef(zNear) || + isZeroOrNegativef(zFar)) + { + ogles_error(c, GL_INVALID_VALUE); + return; + } + const GLfloat r_width = reciprocalf(right - left); + const GLfloat r_height = reciprocalf(top - bottom); + const GLfloat r_depth = reciprocalf(zNear - zFar); + const GLfloat x = mul2f(zNear * r_width); + const GLfloat y = mul2f(zNear * r_height); + const GLfloat A = mul2f((right + left) * r_width); + const GLfloat B = (top + bottom) * r_height; + const GLfloat C = (zFar + zNear) * r_depth; + const GLfloat D = mul2f(zFar * zNear * r_depth); + GLfloat f[16]; + f[ 0] = x; + f[ 5] = y; + f[ 8] = A; + f[ 9] = B; + f[10] = C; + f[14] = D; + f[11] = -1.0f; + f[ 1] = f[ 2] = f[ 3] = + f[ 4] = f[ 6] = f[ 7] = + f[12] = f[13] = f[15] = 0.0f; + + matrixf_t rhs; + rhs.set(f); + c->transforms.current->multiply(rhs); + c->transforms.invalidate(); +} + +static void orthof( + GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar, + ogles_context_t* c) +{ + if (cmpf(left,right) || + cmpf(top, bottom) || + cmpf(zNear, zFar)) + { + ogles_error(c, GL_INVALID_VALUE); + return; + } + const GLfloat r_width = reciprocalf(right - left); + const GLfloat r_height = reciprocalf(top - bottom); + const GLfloat r_depth = reciprocalf(zFar - zNear); + const GLfloat x = mul2f(r_width); + const GLfloat y = mul2f(r_height); + const GLfloat z = -mul2f(r_depth); + const GLfloat tx = -(right + left) * r_width; + const GLfloat ty = -(top + bottom) * r_height; + const GLfloat tz = -(zFar + zNear) * r_depth; + GLfloat f[16]; + f[ 0] = x; + f[ 5] = y; + f[10] = z; + f[12] = tx; + f[13] = ty; + f[14] = tz; + f[15] = 1.0f; + f[ 1] = f[ 2] = f[ 3] = + f[ 4] = f[ 6] = f[ 7] = + f[ 8] = f[ 9] = f[11] = 0.0f; + matrixf_t rhs; + rhs.set(f); + c->transforms.current->multiply(rhs); + c->transforms.invalidate(); +} + +static void depthRangef(GLclampf zNear, GLclampf zFar, ogles_context_t* c) +{ + zNear = clampToZerof(zNear > 1 ? 1 : zNear); + zFar = clampToZerof(zFar > 1 ? 1 : zFar); + GLfloat* const f = c->transforms.vpt.matrix.editElements(); + f[10] = div2f(zFar - zNear); + f[14] = div2f(zFar + zNear); + c->transforms.dirty |= transform_state_t::VIEWPORT; + c->transforms.vpt.zNear = zNear; + c->transforms.vpt.zFar = zFar; +} + + +// ---------------------------------------------------------------------------- +}; // namespace android + +using namespace android; + +void glMatrixMode(GLenum mode) +{ + ogles_context_t* c = ogles_context_t::get(); + matrix_stack_t* stack = 0; + switch (mode) { + case GL_MODELVIEW: + stack = &c->transforms.modelview; + break; + case GL_PROJECTION: + stack = &c->transforms.projection; + break; + case GL_TEXTURE: + stack = &c->transforms.texture[c->textures.active]; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->transforms.matrixMode = mode; + c->transforms.current = stack; +} + +void glLoadIdentity() +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->loadIdentity(); // also loads the GLfixed transform + c->transforms.invalidate(); + c->transforms.current->dirty = 0; +} + +void glLoadMatrixf(const GLfloat* m) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->load(m); + c->transforms.invalidate(); +} + +void glLoadMatrixx(const GLfixed* m) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->load(m); // also loads the GLfixed transform + c->transforms.invalidate(); + c->transforms.current->dirty &= ~matrix_stack_t::DO_FLOAT_TO_FIXED; +} + +void glMultMatrixf(const GLfloat* m) +{ + ogles_context_t* c = ogles_context_t::get(); + matrixf_t rhs; + rhs.set(m); + c->transforms.current->multiply(rhs); + c->transforms.invalidate(); +} + +void glMultMatrixx(const GLfixed* m) +{ + ogles_context_t* c = ogles_context_t::get(); + matrixf_t rhs; + rhs.set(m); + c->transforms.current->multiply(rhs); + c->transforms.invalidate(); +} + +void glPopMatrix() +{ + ogles_context_t* c = ogles_context_t::get(); + GLint err = c->transforms.current->pop(); + if (ggl_unlikely(err)) { + ogles_error(c, err); + return; + } + c->transforms.invalidate(); +} + +void glPushMatrix() +{ + ogles_context_t* c = ogles_context_t::get(); + GLint err = c->transforms.current->push(); + if (ggl_unlikely(err)) { + ogles_error(c, err); + return; + } + c->transforms.invalidate(); +} + +void glFrustumf( + GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + frustumf(left, right, bottom, top, zNear, zFar, c); +} + +void glFrustumx( + GLfixed left, GLfixed right, + GLfixed bottom, GLfixed top, + GLfixed zNear, GLfixed zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + frustumf( fixedToFloat(left), fixedToFloat(right), + fixedToFloat(bottom), fixedToFloat(top), + fixedToFloat(zNear), fixedToFloat(zFar), + c); +} + +void glOrthof( + GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + orthof(left, right, bottom, top, zNear, zFar, c); +} + +void glOrthox( + GLfixed left, GLfixed right, + GLfixed bottom, GLfixed top, + GLfixed zNear, GLfixed zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + orthof( fixedToFloat(left), fixedToFloat(right), + fixedToFloat(bottom), fixedToFloat(top), + fixedToFloat(zNear), fixedToFloat(zFar), + c); +} + +void glRotatef(GLfloat a, GLfloat x, GLfloat y, GLfloat z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->rotate(a, x, y, z); + c->transforms.invalidate(); +} + +void glRotatex(GLfixed a, GLfixed x, GLfixed y, GLfixed z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->rotate( + fixedToFloat(a), fixedToFloat(x), + fixedToFloat(y), fixedToFloat(z)); + c->transforms.invalidate(); +} + +void glScalef(GLfloat x, GLfloat y, GLfloat z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->scale(x, y, z); + c->transforms.invalidate(); +} + +void glScalex(GLfixed x, GLfixed y, GLfixed z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->scale( + fixedToFloat(x), fixedToFloat(y), fixedToFloat(z)); + c->transforms.invalidate(); +} + +void glTranslatef(GLfloat x, GLfloat y, GLfloat z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->translate(x, y, z); + c->transforms.invalidate(); +} + +void glTranslatex(GLfixed x, GLfixed y, GLfixed z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->translate( + fixedToFloat(x), fixedToFloat(y), fixedToFloat(z)); + c->transforms.invalidate(); +} + +void glScissor(GLint x, GLint y, GLsizei w, GLsizei h) +{ + ogles_context_t* c = ogles_context_t::get(); + ogles_scissor(c, x, y, w, h); +} + +void glViewport(GLint x, GLint y, GLsizei w, GLsizei h) +{ + ogles_context_t* c = ogles_context_t::get(); + ogles_viewport(c, x, y, w, h); +} + +void glDepthRangef(GLclampf zNear, GLclampf zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + depthRangef(zNear, zFar, c); +} + +void glDepthRangex(GLclampx zNear, GLclampx zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + depthRangef(fixedToFloat(zNear), fixedToFloat(zFar), c); +} + +void glPolygonOffsetx(GLfixed factor, GLfixed units) +{ + ogles_context_t* c = ogles_context_t::get(); + c->polygonOffset.factor = factor; + c->polygonOffset.units = units; +} + +void glPolygonOffset(GLfloat factor, GLfloat units) +{ + ogles_context_t* c = ogles_context_t::get(); + c->polygonOffset.factor = gglFloatToFixed(factor); + c->polygonOffset.units = gglFloatToFixed(units); +} + +GLbitfield glQueryMatrixxOES(GLfixed* m, GLint* e) +{ + ogles_context_t* c = ogles_context_t::get(); + GLbitfield status = 0; + GLfloat const* f = c->transforms.current->top().elements(); + for (int i=0 ; i<16 ; i++) { + if (isnan(f[i]) || isinf(f[i])) { + status |= 1< +#include +#include +#include + +#include + +#include + +namespace android { + +const int OGLES_MODELVIEW_STACK_DEPTH = 16; +const int OGLES_PROJECTION_STACK_DEPTH = 2; +const int OGLES_TEXTURE_STACK_DEPTH = 2; + +void ogles_init_matrix(ogles_context_t*); +void ogles_uninit_matrix(ogles_context_t*); +void ogles_invalidate_perspective(ogles_context_t* c); +void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want); + +int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y); + +void ogles_scissor(ogles_context_t* c, + GLint x, GLint y, GLsizei w, GLsizei h); + +void ogles_viewport(ogles_context_t* c, + GLint x, GLint y, GLsizei w, GLsizei h); + +inline void ogles_validate_transform( + ogles_context_t* c, uint32_t want) +{ + if (c->transforms.dirty & want) + ogles_validate_transform_impl(c, want); +} + +// ---------------------------------------------------------------------------- + +inline +GLfixed vsquare3(GLfixed a, GLfixed b, GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + int32_t t; + asm( + "smull %0, %1, %2, %2 \n" + "smlal %0, %1, %3, %3 \n" + "smlal %0, %1, %4, %4 \n" + "movs %0, %0, lsr #16 \n" + "adc %0, %0, %1, lsl #16 \n" + : "=&r"(r), "=&r"(t) + : "%r"(a), "r"(b), "r"(c) + : "cc" + ); + return r; + +#else + + return (( int64_t(a)*a + + int64_t(b)*b + + int64_t(c)*c + 0x8000)>>16); + +#endif +} + +static inline GLfixed mla2a( GLfixed a0, GLfixed b0, + GLfixed a1, GLfixed b1, + GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + int32_t t; + asm( + "smull %0, %1, %2, %3 \n" + "smlal %0, %1, %4, %5 \n" + "add %0, %6, %0, lsr #16 \n" + "add %0, %0, %1, lsl #16 \n" + : "=&r"(r), "=&r"(t) + : "%r"(a0), "r"(b0), + "%r"(a1), "r"(b1), + "r"(c) + : + ); + return r; + +#else + + return (( int64_t(a0)*b0 + + int64_t(a1)*b1)>>16) + c; + +#endif +} + +static inline GLfixed mla3a( GLfixed a0, GLfixed b0, + GLfixed a1, GLfixed b1, + GLfixed a2, GLfixed b2, + GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + int32_t t; + asm( + "smull %0, %1, %2, %3 \n" + "smlal %0, %1, %4, %5 \n" + "smlal %0, %1, %6, %7 \n" + "add %0, %8, %0, lsr #16 \n" + "add %0, %0, %1, lsl #16 \n" + : "=&r"(r), "=&r"(t) + : "%r"(a0), "r"(b0), + "%r"(a1), "r"(b1), + "%r"(a2), "r"(b2), + "r"(c) + : + ); + return r; + +#else + + return (( int64_t(a0)*b0 + + int64_t(a1)*b1 + + int64_t(a2)*b2)>>16) + c; + +#endif +} + +// b0, b1, b2 are signed 16-bit quanities +// that have been shifted right by 'shift' bits relative to normal +// S16.16 fixed point +static inline GLfixed mla3a16( GLfixed a0, int32_t b1b0, + GLfixed a1, + GLfixed a2, int32_t b2, + GLint shift, + GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + asm( + "smulwb %0, %1, %2 \n" + "smlawt %0, %3, %2, %0 \n" + "smlawb %0, %4, %5, %0 \n" + "add %0, %7, %0, lsl %6 \n" + : "=&r"(r) + : "r"(a0), "r"(b1b0), + "r"(a1), + "r"(a2), "r"(b2), + "r"(shift), + "r"(c) + : + ); + return r; + +#else + + int32_t accum; + int16_t b0 = b1b0 & 0xffff; + int16_t b1 = (b1b0 >> 16) & 0xffff; + accum = int64_t(a0)*int16_t(b0) >> 16; + accum += int64_t(a1)*int16_t(b1) >> 16; + accum += int64_t(a2)*int16_t(b2) >> 16; + accum = (accum << shift) + c; + return accum; + +#endif +} + + +static inline GLfixed mla3a16_btb( GLfixed a0, + GLfixed a1, + GLfixed a2, + int32_t b1b0, int32_t xxb2, + GLint shift, + GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + asm( + "smulwb %0, %1, %4 \n" + "smlawt %0, %2, %4, %0 \n" + "smlawb %0, %3, %5, %0 \n" + "add %0, %7, %0, lsl %6 \n" + : "=&r"(r) + : "r"(a0), + "r"(a1), + "r"(a2), + "r"(b1b0), "r"(xxb2), + "r"(shift), + "r"(c) + : + ); + return r; + +#else + + int32_t accum; + int16_t b0 = b1b0 & 0xffff; + int16_t b1 = (b1b0 >> 16) & 0xffff; + int16_t b2 = xxb2 & 0xffff; + accum = int64_t(a0)*int16_t(b0) >> 16; + accum += int64_t(a1)*int16_t(b1) >> 16; + accum += int64_t(a2)*int16_t(b2) >> 16; + accum = (accum << shift) + c; + return accum; + +#endif +} + +static inline GLfixed mla3a16_btt( GLfixed a0, + GLfixed a1, + GLfixed a2, + int32_t b1b0, int32_t b2xx, + GLint shift, + GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + asm( + "smulwb %0, %1, %4 \n" + "smlawt %0, %2, %4, %0 \n" + "smlawt %0, %3, %5, %0 \n" + "add %0, %7, %0, lsl %6 \n" + : "=&r"(r) + : "r"(a0), + "r"(a1), + "r"(a2), + "r"(b1b0), "r"(b2xx), + "r"(shift), + "r"(c) + : + ); + return r; + +#else + + int32_t accum; + int16_t b0 = b1b0 & 0xffff; + int16_t b1 = (b1b0 >> 16) & 0xffff; + int16_t b2 = (b2xx >> 16) & 0xffff; + accum = int64_t(a0)*int16_t(b0) >> 16; + accum += int64_t(a1)*int16_t(b1) >> 16; + accum += int64_t(a2)*int16_t(b2) >> 16; + accum = (accum << shift) + c; + return accum; + +#endif +} + +static inline GLfixed mla3( GLfixed a0, GLfixed b0, + GLfixed a1, GLfixed b1, + GLfixed a2, GLfixed b2) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + int32_t t; + asm( + "smull %0, %1, %2, %3 \n" + "smlal %0, %1, %4, %5 \n" + "smlal %0, %1, %6, %7 \n" + "movs %0, %0, lsr #16 \n" + "adc %0, %0, %1, lsl #16 \n" + : "=&r"(r), "=&r"(t) + : "%r"(a0), "r"(b0), + "%r"(a1), "r"(b1), + "%r"(a2), "r"(b2) + : "cc" + ); + return r; + +#else + + return (( int64_t(a0)*b0 + + int64_t(a1)*b1 + + int64_t(a2)*b2 + 0x8000)>>16); + +#endif +} + +static inline GLfixed mla4( GLfixed a0, GLfixed b0, + GLfixed a1, GLfixed b1, + GLfixed a2, GLfixed b2, + GLfixed a3, GLfixed b3) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + int32_t t; + asm( + "smull %0, %1, %2, %3 \n" + "smlal %0, %1, %4, %5 \n" + "smlal %0, %1, %6, %7 \n" + "smlal %0, %1, %8, %9 \n" + "movs %0, %0, lsr #16 \n" + "adc %0, %0, %1, lsl #16 \n" + : "=&r"(r), "=&r"(t) + : "%r"(a0), "r"(b0), + "%r"(a1), "r"(b1), + "%r"(a2), "r"(b2), + "%r"(a3), "r"(b3) + : "cc" + ); + return r; + +#else + + return (( int64_t(a0)*b0 + + int64_t(a1)*b1 + + int64_t(a2)*b2 + + int64_t(a3)*b3 + 0x8000)>>16); + +#endif +} + +inline +GLfixed dot4(const GLfixed* a, const GLfixed* b) +{ + return mla4(a[0], b[0], a[1], b[1], a[2], b[2], a[3], b[3]); +} + + +inline +GLfixed dot3(const GLfixed* a, const GLfixed* b) +{ + return mla3(a[0], b[0], a[1], b[1], a[2], b[2]); +} + + +}; // namespace android + +#endif // ANDROID_OPENGLES_MATRIX_H + diff --git a/opengl/libagl/mipmap.cpp b/opengl/libagl/mipmap.cpp new file mode 100644 index 000000000..ccd77b7dc --- /dev/null +++ b/opengl/libagl/mipmap.cpp @@ -0,0 +1,192 @@ +/* libs/opengles/mipmap.cpp +** +** Copyright 2006, 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 "context.h" +#include "state.h" +#include "texture.h" +#include "TextureObjectManager.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex) +{ + int level = 0; + const GGLSurface* base = &tex->surface; + const GGLFormat& pixelFormat(c->rasterizer.formats[base->format]); + + int w = base->width; + int h = base->height; + if ((w&h) == 1) + return NO_ERROR; + + w = (w>>1) ? : 1; + h = (h>>1) ? : 1; + + while(true) { + ++level; + const int bpr = w * pixelFormat.size; + if (tex->reallocate(level, w, h, w, + base->format, base->compressedFormat, bpr) != NO_ERROR) { + return NO_MEMORY; + } + + int stride = w; + int bs = base->stride; + GGLSurface& cur = tex->editMip(level); + + if (base->format == GGL_PIXEL_FORMAT_RGB_565) + { + uint16_t const * src = (uint16_t const *)base->data; + uint16_t* dst = (uint16_t*)cur.data; + const uint32_t mask = 0x07E0F81F; + for (int y=0 ; y> 2) & mask; + uint32_t rgb = (grb & 0xFFFF) | (grb >> 16); + dst[x + y*stride] = rgb; + offset += 2; + } + } + } + else if (base->format == GGL_PIXEL_FORMAT_RGBA_5551) + { + uint16_t const * src = (uint16_t const *)base->data; + uint16_t* dst = (uint16_t*)cur.data; + for (int y=0 ; y>11)+(p10>>11)+(p01>>11)+(p11>>11)+2)>>2; + uint32_t g = (((p00>>6)+(p10>>6)+(p01>>6)+(p11>>6)+2)>>2)&0x3F; + uint32_t b = ((p00&0x3E)+(p10&0x3E)+(p01&0x3E)+(p11&0x3E)+4)>>3; + uint32_t a = ((p00&1)+(p10&1)+(p01&1)+(p11&1)+2)>>2; + dst[x + y*stride] = (r<<11)|(g<<6)|(b<<1)|a; + offset += 2; + } + } + } + else if (base->format == GGL_PIXEL_FORMAT_RGBA_8888) + { + uint32_t const * src = (uint32_t const *)base->data; + uint32_t* dst = (uint32_t*)cur.data; + for (int y=0 ; y> 8) & 0x00FF00FF; + uint32_t ga01 = (p01 >> 8) & 0x00FF00FF; + uint32_t ga10 = (p10 >> 8) & 0x00FF00FF; + uint32_t ga11 = (p11 >> 8) & 0x00FF00FF; + uint32_t rb = (rb00 + rb01 + rb10 + rb11)>>2; + uint32_t ga = (ga00 + ga01 + ga10 + ga11)>>2; + uint32_t rgba = (rb & 0x00FF00FF) | ((ga & 0x00FF00FF)<<8); + dst[x + y*stride] = rgba; + offset += 2; + } + } + } + else if ((base->format == GGL_PIXEL_FORMAT_RGB_888) || + (base->format == GGL_PIXEL_FORMAT_LA_88) || + (base->format == GGL_PIXEL_FORMAT_A_8) || + (base->format == GGL_PIXEL_FORMAT_L_8)) + { + int skip; + switch (base->format) { + case GGL_PIXEL_FORMAT_RGB_888: skip = 3; break; + case GGL_PIXEL_FORMAT_LA_88: skip = 2; break; + default: skip = 1; break; + } + uint8_t const * src = (uint8_t const *)base->data; + uint8_t* dst = (uint8_t*)cur.data; + bs *= skip; + stride *= skip; + for (int y=0 ; y> 2; + } + offset += 2*skip; + } + } + } + else if (base->format == GGL_PIXEL_FORMAT_RGBA_4444) + { + uint16_t const * src = (uint16_t const *)base->data; + uint16_t* dst = (uint16_t*)cur.data; + for (int y=0 ; y> 2; + uint32_t rgba = (rbga & 0x0F0F) | ((rbga>>12) & 0xF0F0); + dst[x + y*stride] = rgba; + offset += 2; + } + } + } else { + LOGE("Unsupported format (%d)", base->format); + return BAD_TYPE; + } + + // exit condition: we just processed the 1x1 LODs + if ((w&h) == 1) + break; + + base = &cur; + w = (w>>1) ? : 1; + h = (h>>1) ? : 1; + } + return NO_ERROR; +} + +}; // namespace android diff --git a/opengl/libagl/primitives.cpp b/opengl/libagl/primitives.cpp new file mode 100644 index 000000000..f164c02ee --- /dev/null +++ b/opengl/libagl/primitives.cpp @@ -0,0 +1,1111 @@ +/* libs/opengles/primitives.cpp +** +** Copyright 2006, 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 "context.h" +#include "primitives.h" +#include "light.h" +#include "matrix.h" +#include "vertex.h" +#include "fp.h" +#include "TextureObjectManager.h" + +extern "C" void iterators0032(const void* that, + int32_t* it, int32_t c0, int32_t c1, int32_t c2); + +namespace android { + +// ---------------------------------------------------------------------------- + +static void primitive_point(ogles_context_t* c, vertex_t* v); +static void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1); +static void primitive_clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void primitive_nop_point(ogles_context_t* c, vertex_t* v); +static void primitive_nop_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1); +static void primitive_nop_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static inline bool cull_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void lerp_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void lerp_texcoords(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void lerp_texcoords_w(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static unsigned int clip_line(ogles_context_t* c, + vertex_t* s, vertex_t* p); + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +static void lightTriangleDarkSmooth(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v0->flags & vertex_t::LIT)) { + v0->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v0->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v0->color.v, cp); + } + if (!(v1->flags & vertex_t::LIT)) { + v1->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v1->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v1->color.v, cp); + } + if(!(v2->flags & vertex_t::LIT)) { + v2->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v2->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v2->color.v, cp); + } +} + +static void lightTriangleDarkFlat(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v2->flags & vertex_t::LIT)) { + v2->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v2->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v2->color.v, cp); + } + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + +static void lightTriangleSmooth(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v0->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v0); + if (!(v1->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v1); + if(!(v2->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v2); +} + +static void lightTriangleFlat(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v2->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v2); + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + +// The fog versions... + +static inline +void lightVertexDarkSmoothFog(ogles_context_t* c, vertex_t* v) +{ + if (!(v->flags & vertex_t::LIT)) { + v->flags |= vertex_t::LIT; + v->fog = c->fog.fog(c, v->eye.z); + const GLvoid* cp = c->arrays.color.element( + v->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v->color.v, cp); + } +} +static inline +void lightVertexDarkFlatFog(ogles_context_t* c, vertex_t* v) +{ + if (!(v->flags & vertex_t::LIT)) { + v->flags |= vertex_t::LIT; + v->fog = c->fog.fog(c, v->eye.z); + } +} +static inline +void lightVertexSmoothFog(ogles_context_t* c, vertex_t* v) +{ + if (!(v->flags & vertex_t::LIT)) { + v->fog = c->fog.fog(c, v->eye.z); + c->lighting.lightVertex(c, v); + } +} + +static void lightTriangleDarkSmoothFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexDarkSmoothFog(c, v0); + lightVertexDarkSmoothFog(c, v1); + lightVertexDarkSmoothFog(c, v2); +} + +static void lightTriangleDarkFlatFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexDarkFlatFog(c, v0); + lightVertexDarkFlatFog(c, v1); + lightVertexDarkSmoothFog(c, v2); + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + +static void lightTriangleSmoothFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexSmoothFog(c, v0); + lightVertexSmoothFog(c, v1); + lightVertexSmoothFog(c, v2); +} + +static void lightTriangleFlatFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexDarkFlatFog(c, v0); + lightVertexDarkFlatFog(c, v1); + lightVertexSmoothFog(c, v2); + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + + + +typedef void (*light_primitive_t)(ogles_context_t*, + vertex_t*, vertex_t*, vertex_t*); + +// fog 0x4, light 0x2, smooth 0x1 +static const light_primitive_t lightPrimitive[8] = { + lightTriangleDarkFlat, // no fog | dark | flat + lightTriangleDarkSmooth, // no fog | dark | smooth + lightTriangleFlat, // no fog | light | flat + lightTriangleSmooth, // no fog | light | smooth + lightTriangleDarkFlatFog, // fog | dark | flat + lightTriangleDarkSmoothFog, // fog | dark | smooth + lightTriangleFlatFog, // fog | light | flat + lightTriangleSmoothFog // fog | light | smooth +}; + +void ogles_validate_primitives(ogles_context_t* c) +{ + const uint32_t enables = c->rasterizer.state.enables; + + // set up the lighting/shading/smoothing/fogging function + int index = enables & GGL_ENABLE_SMOOTH ? 0x1 : 0; + index |= c->lighting.enable ? 0x2 : 0; + index |= enables & GGL_ENABLE_FOG ? 0x4 : 0; + c->lighting.lightTriangle = lightPrimitive[index]; + + // set up the primitive renderers + if (ggl_likely(c->arrays.vertex.enable)) { + c->prims.renderPoint = primitive_point; + c->prims.renderLine = primitive_line; + c->prims.renderTriangle = primitive_clip_triangle; + } else { + c->prims.renderPoint = primitive_nop_point; + c->prims.renderLine = primitive_nop_line; + c->prims.renderTriangle = primitive_nop_triangle; + } +} + +// ---------------------------------------------------------------------------- + +void compute_iterators_t::initTriangle( + vertex_t const* v0, vertex_t const* v1, vertex_t const* v2) +{ + m_dx01 = v1->window.x - v0->window.x; + m_dy10 = v0->window.y - v1->window.y; + m_dx20 = v0->window.x - v2->window.x; + m_dy02 = v2->window.y - v0->window.y; + m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20; +} + +void compute_iterators_t::initLine( + vertex_t const* v0, vertex_t const* v1) +{ + m_dx01 = m_dy02 = v1->window.x - v0->window.x; + m_dy10 = m_dx20 = v0->window.y - v1->window.y; + m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20; +} + +void compute_iterators_t::initLerp(vertex_t const* v0, uint32_t enables) +{ + m_x0 = v0->window.x; + m_y0 = v0->window.y; + const GGLcoord area = (m_area + TRI_HALF) >> TRI_FRACTION_BITS; + const GGLcoord minArea = 2; // cannot be inverted + // triangles with an area smaller than 1.0 are not smooth-shaded + + int q=0, s=0, d=0; + if (abs(area) >= minArea) { + // Here we do some voodoo magic, to compute a suitable scale + // factor for deltas/area: + + // First compute the 1/area with full 32-bits precision, + // gglRecipQNormalized returns a number [-0.5, 0.5[ and an exponent. + d = gglRecipQNormalized(area, &q); + + // Then compute the minimum left-shift to not overflow the muls + // below. + s = 32 - gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20)); + + // We'll keep 16-bits of precision for deltas/area. So we need + // to shift everything left an extra 15 bits. + s += 15; + + // make sure all final shifts are not > 32, because gglMulx + // can't handle it. + if (s < q) s = q; + if (s > 32) { + d >>= 32-s; + s = 32; + } + } + + m_dx01 = gglMulx(m_dx01, d, s); + m_dy10 = gglMulx(m_dy10, d, s); + m_dx20 = gglMulx(m_dx20, d, s); + m_dy02 = gglMulx(m_dy02, d, s); + m_area_scale = 32 + q - s; + m_scale = 0; + + if (enables & GGL_ENABLE_TMUS) { + const int A = gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20)); + const int B = gglClz(abs(m_x0)|abs(m_y0)); + m_scale = max(0, 32 - (A + 16)) + + max(0, 32 - (B + TRI_FRACTION_BITS)) + 1; + } +} + +int compute_iterators_t::iteratorsScale(GGLfixed* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + int32_t dc01 = c1 - c0; + int32_t dc02 = c2 - c0; + const int A = gglClz(abs(c0)); + const int B = gglClz(abs(dc01)|abs(dc02)); + const int scale = min(A, B - m_scale) - 2; + if (scale >= 0) { + c0 <<= scale; + dc01 <<= scale; + dc02 <<= scale; + } else { + c0 >>= -scale; + dc01 >>= -scale; + dc02 >>= -scale; + } + const int s = m_area_scale; + int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s); + int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s); + int32_t c = c0 - (gglMulAddx(dcdx, m_x0, + gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS)); + it[0] = c; + it[1] = dcdx; + it[2] = dcdy; + return scale; +} + +void compute_iterators_t::iterators1616(GGLfixed* it, + GGLfixed c0, GGLfixed c1, GGLfixed c2) const +{ + const GGLfixed dc01 = c1 - c0; + const GGLfixed dc02 = c2 - c0; + // 16.16 x 16.16 == 32.32 --> 16.16 + const int s = m_area_scale; + int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s); + int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s); + int32_t c = c0 - (gglMulAddx(dcdx, m_x0, + gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS)); + it[0] = c; + it[1] = dcdx; + it[2] = dcdy; +} + +void compute_iterators_t::iterators0032(int64_t* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + const int s = m_area_scale - 16; + int32_t dc01 = (c1 - c0)>>s; + int32_t dc02 = (c2 - c0)>>s; + // 16.16 x 16.16 == 32.32 + int64_t dcdx = gglMulii(dc01, m_dy02) + gglMulii(dc02, m_dy10); + int64_t dcdy = gglMulii(dc02, m_dx01) + gglMulii(dc01, m_dx20); + it[ 0] = (c0<<16) - ((dcdx*m_x0 + dcdy*m_y0)>>4); + it[ 1] = dcdx; + it[ 2] = dcdy; +} + +#if defined(__arm__) && !defined(__thumb__) +inline void compute_iterators_t::iterators0032(int32_t* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + ::iterators0032(this, it, c0, c1, c2); +} +#else +void compute_iterators_t::iterators0032(int32_t* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + int64_t it64[3]; + iterators0032(it, c0, c1, c2); + it[0] = it64[0]; + it[1] = it64[1]; + it[2] = it64[2]; +} +#endif + +// ---------------------------------------------------------------------------- + +static inline int32_t clampZ(GLfixed z) CONST; +int32_t clampZ(GLfixed z) { + z = (z & ~(z>>31)); + if (z >= 0x10000) + z = 0xFFFF; + return z; +} + +static __attribute__((noinline)) +void fetch_texcoord_impl(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + vertex_t* const vtx[3] = { v0, v1, v2 }; + array_t const * const texcoordArray = c->arrays.texture; + + for (int i=0 ; irasterizer.state.texture[i].enable)) + continue; + + for (int j=0 ; j<3 ; j++) { + vertex_t* const v = vtx[j]; + if (v->flags & vertex_t::TT) + continue; + + // NOTE: here we could compute automatic texgen + // such as sphere/cube maps, instead of fetching them + // from the textcoord array. + + vec4_t& coords = v->texture[i]; + const GLubyte* tp = texcoordArray[i].element( + v->index & vertex_cache_t::INDEX_MASK); + texcoordArray[i].fetch(c, coords.v, tp); + + // transform texture coordinates... + coords.Q = 0x10000; + const transform_t& tr = c->transforms.texture[i].transform; + if (ggl_unlikely(tr.ops)) { + c->arrays.tex_transform[i](&tr, &coords, &coords); + } + + // divide by Q + const GGLfixed q = coords.Q; + if (ggl_unlikely(q != 0x10000)) { + const int32_t qinv = gglRecip28(q); + coords.S = gglMulx(coords.S, qinv, 28); + coords.T = gglMulx(coords.T, qinv, 28); + } + } + } + v0->flags |= vertex_t::TT; + v1->flags |= vertex_t::TT; + v2->flags |= vertex_t::TT; +} + +inline void fetch_texcoord(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const uint32_t enables = c->rasterizer.state.enables; + if (!(enables & GGL_ENABLE_TMUS)) + return; + + // Fetch & transform texture coordinates... + if (ggl_likely(v0->flags & v1->flags & v2->flags & vertex_t::TT)) { + // already done for all three vertices, bail... + return; + } + fetch_texcoord_impl(c, v0, v1, v2); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Point +#endif + +void primitive_nop_point(ogles_context_t*, vertex_t*) { +} + +void primitive_point(ogles_context_t* c, vertex_t* v) +{ + // lighting & clamping... + const uint32_t enables = c->rasterizer.state.enables; + + if (ggl_unlikely(!(v->flags & vertex_t::LIT))) { + if (c->lighting.enable) { + c->lighting.lightVertex(c, v); + } else { + v->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v->color.v, cp); + } + if (enables & GGL_ENABLE_FOG) { + v->fog = c->fog.fog(c, v->eye.z); + } + } + + // XXX: we don't need to do that each-time + // if color array and lighting not enabled + c->rasterizer.procs.color4xv(c, v->color.v); + + // XXX: look into ES point-sprite extension + if (enables & GGL_ENABLE_TMUS) { + fetch_texcoord(c, v,v,v); + for (int i=0 ; irasterizer.state.texture[i].enable) + continue; + int32_t itt[8]; + itt[1] = itt[2] = itt[4] = itt[5] = 0; + itt[6] = itt[7] = 16; // XXX: check that + if (c->rasterizer.state.texture[i].s_wrap == GGL_CLAMP) { + int width = c->textures.tmu[i].texture->surface.width; + itt[0] = v->texture[i].S * width; + itt[6] = 0; + } + if (c->rasterizer.state.texture[i].t_wrap == GGL_CLAMP) { + int height = c->textures.tmu[i].texture->surface.height; + itt[3] = v->texture[i].T * height; + itt[7] = 0; + } + c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); + } + } + + if (enables & GGL_ENABLE_DEPTH_TEST) { + int32_t itz[3]; + itz[0] = clampZ(v->window.z) * 0x00010001; + itz[1] = itz[2] = 0; + c->rasterizer.procs.zGrad3xv(c, itz); + } + + if (enables & GGL_ENABLE_FOG) { + GLfixed itf[3]; + itf[0] = v->fog; + itf[1] = itf[2] = 0; + c->rasterizer.procs.fogGrad3xv(c, itf); + } + + // Render our point... + c->rasterizer.procs.pointx(c, v->window.v, c->point.size); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Line +#endif + +void primitive_nop_line(ogles_context_t*, vertex_t*, vertex_t*) { +} + +void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1) +{ + // get texture coordinates + fetch_texcoord(c, v0, v1, v1); + + // light/shade the vertices first (they're copied below) + c->lighting.lightTriangle(c, v0, v1, v1); + + // clip the line if needed + if (ggl_unlikely((v0->flags | v1->flags) & vertex_t::CLIP_ALL)) { + unsigned int count = clip_line(c, v0, v1); + if (ggl_unlikely(count == 0)) + return; + } + + // compute iterators... + const uint32_t enables = c->rasterizer.state.enables; + const uint32_t mask = GGL_ENABLE_TMUS | + GGL_ENABLE_SMOOTH | + GGL_ENABLE_W | + GGL_ENABLE_FOG | + GGL_ENABLE_DEPTH_TEST; + + if (ggl_unlikely(enables & mask)) { + c->lerp.initLine(v0, v1); + lerp_triangle(c, v0, v1, v0); + } + + // render our line + c->rasterizer.procs.linex(c, v0->window.v, v1->window.v, c->line.width); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Triangle +#endif + +void primitive_nop_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) { +} + +void primitive_clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + uint32_t cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL; + if (ggl_likely(!cc)) { + // code below must be as optimized as possible, this is the + // common code path. + + // This triangle is not clipped, test if it's culled + // unclipped triangle... + c->lerp.initTriangle(v0, v1, v2); + if (cull_triangle(c, v0, v1, v2)) + return; // culled! + + // Fetch all texture coordinates if needed + fetch_texcoord(c, v0, v1, v2); + + // light (or shade) our triangle! + c->lighting.lightTriangle(c, v0, v1, v2); + + triangle(c, v0, v1, v2); + return; + } + + // The assumption here is that we're not going to clip very often, + // and even more rarely will we clip a triangle that ends up + // being culled out. So it's okay to light the vertices here, even though + // in a few cases we won't render the triangle (if culled). + + // Fetch texture coordinates... + fetch_texcoord(c, v0, v1, v2); + + // light (or shade) our triangle! + c->lighting.lightTriangle(c, v0, v1, v2); + + clip_triangle(c, v0, v1, v2); +} + +// ----------------------------------------------------------------------- + +void triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + // compute iterators... + const uint32_t enables = c->rasterizer.state.enables; + const uint32_t mask = GGL_ENABLE_TMUS | + GGL_ENABLE_SMOOTH | + GGL_ENABLE_W | + GGL_ENABLE_FOG | + GGL_ENABLE_DEPTH_TEST; + + if (ggl_likely(enables & mask)) + lerp_triangle(c, v0, v1, v2); + + c->rasterizer.procs.trianglex(c, v0->window.v, v1->window.v, v2->window.v); +} + +void lerp_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const uint32_t enables = c->rasterizer.state.enables; + c->lerp.initLerp(v0, enables); + + // set up texture iterators + if (enables & GGL_ENABLE_TMUS) { + if (enables & GGL_ENABLE_W) { + lerp_texcoords_w(c, v0, v1, v2); + } else { + lerp_texcoords(c, v0, v1, v2); + } + } + + // set up the color iterators + const compute_iterators_t& lerp = c->lerp; + if (enables & GGL_ENABLE_SMOOTH) { + GLfixed itc[12]; + for (int i=0 ; i<4 ; i++) { + const GGLcolor c0 = v0->color.v[i] * 255; + const GGLcolor c1 = v1->color.v[i] * 255; + const GGLcolor c2 = v2->color.v[i] * 255; + lerp.iterators1616(&itc[i*3], c0, c1, c2); + } + c->rasterizer.procs.colorGrad12xv(c, itc); + } + + if (enables & GGL_ENABLE_DEPTH_TEST) { + int32_t itz[3]; + const int32_t v0z = clampZ(v0->window.z); + const int32_t v1z = clampZ(v1->window.z); + const int32_t v2z = clampZ(v2->window.z); + if (ggl_unlikely(c->polygonOffset.enable)) { + const int32_t units = (c->polygonOffset.units << 16); + const GLfixed factor = c->polygonOffset.factor; + if (factor) { + int64_t itz64[3]; + lerp.iterators0032(itz64, v0z, v1z, v2z); + int64_t maxDepthSlope = max(itz64[1], itz64[2]); + itz[0] = uint32_t(itz64[0]) + + uint32_t((maxDepthSlope*factor)>>16) + units; + itz[1] = uint32_t(itz64[1]); + itz[2] = uint32_t(itz64[2]); + } else { + lerp.iterators0032(itz, v0z, v1z, v2z); + itz[0] += units; + } + } else { + lerp.iterators0032(itz, v0z, v1z, v2z); + } + c->rasterizer.procs.zGrad3xv(c, itz); + } + + if (ggl_unlikely(enables & GGL_ENABLE_FOG)) { + GLfixed itf[3]; + lerp.iterators1616(itf, v0->fog, v1->fog, v2->fog); + c->rasterizer.procs.fogGrad3xv(c, itf); + } +} + + +static inline +int compute_lod(ogles_context_t* c, int i, + int32_t s0, int32_t t0, int32_t s1, int32_t t1, int32_t s2, int32_t t2) +{ + // Compute mipmap level / primitive + // rho = sqrt( texelArea / area ) + // lod = log2( rho ) + // lod = log2( texelArea / area ) / 2 + // lod = (log2( texelArea ) - log2( area )) / 2 + const compute_iterators_t& lerp = c->lerp; + const GGLcoord area = abs(lerp.area()); + const int w = c->textures.tmu[i].texture->surface.width; + const int h = c->textures.tmu[i].texture->surface.height; + const int shift = 16 + (16 - TRI_FRACTION_BITS); + int32_t texelArea = abs( gglMulx(s1-s0, t2-t0, shift) - + gglMulx(s2-s0, t1-t0, shift) )*w*h; + int log2TArea = (32-TRI_FRACTION_BITS -1) - gglClz(texelArea); + int log2Area = (32-TRI_FRACTION_BITS*2-1) - gglClz(area); + int lod = (log2TArea - log2Area + 1) >> 1; + return lod; +} + +void lerp_texcoords(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const compute_iterators_t& lerp = c->lerp; + int32_t itt[8] __attribute__((aligned(16))); + for (int i=0 ; irasterizer.state.texture[i]; + if (!tmu.enable) + continue; + + // compute the jacobians using block floating-point + int32_t s0 = v0->texture[i].S; + int32_t t0 = v0->texture[i].T; + int32_t s1 = v1->texture[i].S; + int32_t t1 = v1->texture[i].T; + int32_t s2 = v2->texture[i].S; + int32_t t2 = v2->texture[i].T; + + const GLenum min_filter = c->textures.tmu[i].texture->min_filter; + if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) { + int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2); + c->rasterizer.procs.bindTextureLod(c, i, + &c->textures.tmu[i].texture->mip(lod)); + } + + // premultiply (s,t) when clampling + if (tmu.s_wrap == GGL_CLAMP) { + const int width = tmu.surface.width; + s0 *= width; + s1 *= width; + s2 *= width; + } + if (tmu.t_wrap == GGL_CLAMP) { + const int height = tmu.surface.height; + t0 *= height; + t1 *= height; + t2 *= height; + } + itt[6] = -lerp.iteratorsScale(itt+0, s0, s1, s2); + itt[7] = -lerp.iteratorsScale(itt+3, t0, t1, t2); + c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); + } +} + +void lerp_texcoords_w(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const compute_iterators_t& lerp = c->lerp; + int32_t itt[8] __attribute__((aligned(16))); + int32_t itw[3]; + + // compute W's scale to 2.30 + int32_t w0 = v0->window.w; + int32_t w1 = v1->window.w; + int32_t w2 = v2->window.w; + int wscale = 32 - gglClz(w0|w1|w2); + + // compute the jacobian using block floating-point + int sc = lerp.iteratorsScale(itw, w0, w1, w2); + sc += wscale - 16; + c->rasterizer.procs.wGrad3xv(c, itw); + + for (int i=0 ; irasterizer.state.texture[i]; + if (!tmu.enable) + continue; + + // compute the jacobians using block floating-point + int32_t s0 = v0->texture[i].S; + int32_t t0 = v0->texture[i].T; + int32_t s1 = v1->texture[i].S; + int32_t t1 = v1->texture[i].T; + int32_t s2 = v2->texture[i].S; + int32_t t2 = v2->texture[i].T; + + const GLenum min_filter = c->textures.tmu[i].texture->min_filter; + if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) { + int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2); + c->rasterizer.procs.bindTextureLod(c, i, + &c->textures.tmu[i].texture->mip(lod)); + } + + // premultiply (s,t) when clampling + if (tmu.s_wrap == GGL_CLAMP) { + const int width = tmu.surface.width; + s0 *= width; + s1 *= width; + s2 *= width; + } + if (tmu.t_wrap == GGL_CLAMP) { + const int height = tmu.surface.height; + t0 *= height; + t1 *= height; + t2 *= height; + } + + s0 = gglMulx(s0, w0, wscale); + t0 = gglMulx(t0, w0, wscale); + s1 = gglMulx(s1, w1, wscale); + t1 = gglMulx(t1, w1, wscale); + s2 = gglMulx(s2, w2, wscale); + t2 = gglMulx(t2, w2, wscale); + + itt[6] = sc - lerp.iteratorsScale(itt+0, s0, s1, s2); + itt[7] = sc - lerp.iteratorsScale(itt+3, t0, t1, t2); + c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); + } +} + + +static inline +bool cull_triangle(ogles_context_t* c, vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (ggl_likely(c->cull.enable)) { + const GLenum winding = (c->lerp.area() > 0) ? GL_CW : GL_CCW; + const GLenum face = (winding == c->cull.frontFace) ? GL_FRONT : GL_BACK; + if (face == c->cull.cullFace) + return true; // culled! + } + return false; +} + +static inline +GLfixed frustumPlaneDist(int plane, const vec4_t& s) +{ + const GLfixed d = s.v[ plane >> 1 ]; + return ((plane & 1) ? (s.w - d) : (s.w + d)); +} + +static inline +int32_t clipDivide(GLfixed a, GLfixed b) { + // returns a 4.28 fixed-point + return gglMulDivi(1LU<<28, a, b); +} + +void clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + uint32_t all_cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL; + + vertex_t *p0, *p1, *p2; + const int MAX_CLIPPING_PLANES = 6 + OGLES_MAX_CLIP_PLANES; + const int MAX_VERTICES = 3; + + // Temporary buffer to hold the new vertices. Each plane can add up to + // two new vertices (because the polygon is convex). + // We need one extra element, to handle an overflow case when + // the polygon degenerates into something non convex. + vertex_t buffer[MAX_CLIPPING_PLANES * 2 + 1]; // ~3KB + vertex_t* buf = buffer; + + // original list of vertices (polygon to clip, in fact this + // function works with an arbitrary polygon). + vertex_t* in[3] = { v0, v1, v2 }; + + // output lists (we need 2, which we use back and forth) + // (maximum outpout list's size is MAX_CLIPPING_PLANES + MAX_VERTICES) + // 2 more elements for overflow when non convex polygons. + vertex_t* out[2][MAX_CLIPPING_PLANES + MAX_VERTICES + 2]; + unsigned int outi = 0; + + // current input list + vertex_t** ivl = in; + + // 3 input vertices, 0 in the output list, first plane + unsigned int ic = 3; + + // User clip-planes first, the clipping is always done in eye-coordinate + // this is basically the same algorithm than for the view-volume + // clipping, except for the computation of the distance (vertex, plane) + // and the fact that we need to compute the eye-coordinates of each + // new vertex we create. + + if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL)) + { + unsigned int plane = 0; + uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8; + do { + if (cc & 1) { + // pointers to our output list (head and current) + vertex_t** const ovl = &out[outi][0]; + vertex_t** output = ovl; + unsigned int oc = 0; + unsigned int sentinel = 0; + // previous vertex, compute distance to the plane + vertex_t* s = ivl[ic-1]; + const vec4_t& equation = c->clipPlanes.plane[plane].equation; + GLfixed sd = dot4(equation.v, s->eye.v); + // clip each vertex against this plane... + for (unsigned int i=0 ; ieye.v); + if (sd >= 0) { + if (pd >= 0) { + // both inside + *output++ = p; + oc++; + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipEye(c, buf, t, p, s); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipEye(c, buf, t, s, p); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + *output++ = p; + oc++; + } else { + // both outside + } + } + s = p; + sd = pd; + } + // output list become the new input list + if (oc<3) + return; // less than 3 vertices left? we're done! + ivl = ovl; + ic = oc; + outi = 1-outi; + } + cc >>= 1; + plane++; + } while (cc); + } + + // frustum clip-planes + if (all_cc & vertex_t::FRUSTUM_CLIP_ALL) + { + unsigned int plane = 0; + uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL; + do { + if (cc & 1) { + // pointers to our output list (head and current) + vertex_t** const ovl = &out[outi][0]; + vertex_t** output = ovl; + unsigned int oc = 0; + unsigned int sentinel = 0; + // previous vertex, compute distance to the plane + vertex_t* s = ivl[ic-1]; + GLfixed sd = frustumPlaneDist(plane, s->clip); + // clip each vertex against this plane... + for (unsigned int i=0 ; iclip); + if (sd >= 0) { + if (pd >= 0) { + // both inside + *output++ = p; + oc++; + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipVertex(c, buf, t, p, s); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipVertex(c, buf, t, s, p); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + *output++ = p; + oc++; + } else { + // both outside + } + } + s = p; + sd = pd; + } + // output list become the new input list + if (oc<3) + return; // less than 3 vertices left? we're done! + ivl = ovl; + ic = oc; + outi = 1-outi; + } + cc >>= 1; + plane++; + } while (cc); + } + + // finally we can render our triangles... + p0 = ivl[0]; + p1 = ivl[1]; + for (unsigned int i=2 ; ilerp.initTriangle(p0, p1, p2); + if (cull_triangle(c, p0, p1, p2)) { + p1 = p2; + continue; // culled! + } + triangle(c, p0, p1, p2); + p1 = p2; + } +} + +unsigned int clip_line(ogles_context_t* c, vertex_t* s, vertex_t* p) +{ + const uint32_t all_cc = (s->flags | p->flags) & vertex_t::CLIP_ALL; + + if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL)) + { + unsigned int plane = 0; + uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8; + do { + if (cc & 1) { + const vec4_t& equation = c->clipPlanes.plane[plane].equation; + const GLfixed sd = dot4(equation.v, s->eye.v); + const GLfixed pd = dot4(equation.v, p->eye.v); + if (sd >= 0) { + if (pd >= 0) { + // both inside + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipEye(c, p, t, p, s); + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipEye(c, s, t, s, p); + } + } else { + // both outside + return 0; + } + } + } + cc >>= 1; + plane++; + } while (cc); + } + + // frustum clip-planes + if (all_cc & vertex_t::FRUSTUM_CLIP_ALL) + { + unsigned int plane = 0; + uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL; + do { + if (cc & 1) { + const GLfixed sd = frustumPlaneDist(plane, s->clip); + const GLfixed pd = frustumPlaneDist(plane, p->clip); + if (sd >= 0) { + if (pd >= 0) { + // both inside + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipVertex(c, p, t, p, s); + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipVertex(c, s, t, s, p); + } + } else { + // both outside + return 0; + } + } + } + cc >>= 1; + plane++; + } while (cc); + } + + return 2; +} + + +}; // namespace android diff --git a/opengl/libagl/primitives.h b/opengl/libagl/primitives.h new file mode 100644 index 000000000..1bef604eb --- /dev/null +++ b/opengl/libagl/primitives.h @@ -0,0 +1,37 @@ +/* libs/opengles/primitives.h +** +** Copyright 2006, 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. +*/ + +#ifndef ANDROID_OPENGLES_PRIMITIVES_H +#define ANDROID_OPENGLES_PRIMITIVES_H + +#include +#include +#include + + +namespace android { + +namespace gl { +struct ogles_context_t; +}; + +void ogles_validate_primitives(ogles_context_t* c); + +}; // namespace android + +#endif // ANDROID_OPENGLES_PRIMITIVES_H + diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp new file mode 100644 index 000000000..5cbabea1f --- /dev/null +++ b/opengl/libagl/state.cpp @@ -0,0 +1,586 @@ +/* libs/opengles/state.cpp +** +** Copyright 2006, 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 "context.h" +#include "fp.h" +#include "state.h" +#include "array.h" +#include "matrix.h" +#include "vertex.h" +#include "light.h" +#include "texture.h" +#include "BufferObjectManager.h" +#include "TextureObjectManager.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static char const * const gVendorString = "Android"; +static char const * const gRendererString = "Android PixelFlinger 1.0"; +static char const * const gVersionString = "OpenGL ES-CM 1.0"; +static char const * const gExtensionsString = + "GL_OES_byte_coordinates " // OK + "GL_OES_fixed_point " // OK + "GL_OES_single_precision " // OK + "GL_OES_read_format " // OK + "GL_OES_compressed_paletted_texture " // OK + "GL_OES_draw_texture " // OK + "GL_OES_matrix_get " // OK + "GL_OES_query_matrix " // OK + // "GL_OES_point_size_array " // TODO + // "GL_OES_point_sprite " // TODO + "GL_ARB_texture_compression " // OK + "GL_ARB_texture_non_power_of_two " // OK + "GL_ANDROID_direct_texture " // OK + "GL_ANDROID_user_clip_plane " // OK + "GL_ANDROID_vertex_buffer_object " // OK + "GL_ANDROID_generate_mipmap " // OK + ; + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +ogles_context_t *ogles_init(size_t extra) +{ + void* const base = malloc(extra + sizeof(ogles_context_t) + 32); + if (!base) return 0; + + ogles_context_t *c = + (ogles_context_t *)((ptrdiff_t(base) + extra + 31) & ~0x1FL); + memset(c, 0, sizeof(ogles_context_t)); + ggl_init_context(&(c->rasterizer)); + + // XXX: this should be passed as an argument + sp smgr(new EGLSurfaceManager()); + c->surfaceManager = smgr.get(); + c->surfaceManager->incStrong(c); + + sp bomgr(new EGLBufferObjectManager()); + c->bufferObjectManager = bomgr.get(); + c->bufferObjectManager->incStrong(c); + + ogles_init_array(c); + ogles_init_matrix(c); + ogles_init_vertex(c); + ogles_init_light(c); + ogles_init_texture(c); + + c->rasterizer.base = base; + c->point.size = TRI_ONE; + c->line.width = TRI_ONE; + + // in OpenGL, writing to the depth buffer is enabled by default. + c->rasterizer.procs.depthMask(c, 1); + + // OpenGL enables dithering by default + c->rasterizer.procs.enable(c, GL_DITHER); + + return c; +} + +void ogles_uninit(ogles_context_t* c) +{ + ogles_uninit_array(c); + ogles_uninit_matrix(c); + ogles_uninit_vertex(c); + ogles_uninit_light(c); + ogles_uninit_texture(c); + c->surfaceManager->decStrong(c); + c->bufferObjectManager->decStrong(c); + ggl_uninit_context(&(c->rasterizer)); + free(c->rasterizer.base); +} + +void _ogles_error(ogles_context_t* c, GLenum error) +{ + if (c->error == GL_NO_ERROR) + c->error = error; +} + +static bool stencilop_valid(GLenum op) { + switch (op) { + case GL_KEEP: + case GL_ZERO: + case GL_REPLACE: + case GL_INCR: + case GL_DECR: + case GL_INVERT: + return true; + } + return false; +} + +static void enable_disable(ogles_context_t* c, GLenum cap, int enabled) +{ + if ((cap >= GL_LIGHT0) && (caplighting.lights[cap-GL_LIGHT0].enable = enabled; + c->lighting.enabledLights &= ~(1<<(cap-GL_LIGHT0)); + c->lighting.enabledLights |= (enabled<<(cap-GL_LIGHT0)); + return; + } + + switch (cap) { + case GL_POINT_SMOOTH: + c->point.smooth = enabled; + break; + case GL_LINE_SMOOTH: + c->line.smooth = enabled; + break; + case GL_POLYGON_OFFSET_FILL: + c->polygonOffset.enable = enabled; + break; + case GL_CULL_FACE: + c->cull.enable = enabled; + break; + case GL_LIGHTING: + c->lighting.enable = enabled; + break; + case GL_COLOR_MATERIAL: + c->lighting.colorMaterial.enable = enabled; + break; + case GL_NORMALIZE: + case GL_RESCALE_NORMAL: + c->transforms.rescaleNormals = enabled ? cap : 0; + // XXX: invalidate mvit + break; + + case GL_CLIP_PLANE0: + case GL_CLIP_PLANE1: + case GL_CLIP_PLANE2: + case GL_CLIP_PLANE3: + case GL_CLIP_PLANE4: + case GL_CLIP_PLANE5: + c->clipPlanes.enable &= ~(1<<(cap-GL_CLIP_PLANE0)); + c->clipPlanes.enable |= (enabled<<(cap-GL_CLIP_PLANE0)); + ogles_invalidate_perspective(c); + break; + + case GL_FOG: + case GL_DEPTH_TEST: + ogles_invalidate_perspective(c); + // fall-through... + case GL_BLEND: + case GL_SCISSOR_TEST: + case GL_ALPHA_TEST: + case GL_COLOR_LOGIC_OP: + case GL_DITHER: + case GL_STENCIL_TEST: + case GL_TEXTURE_2D: + // these need to fall through into the rasterizer + c->rasterizer.procs.enableDisable(c, cap, enabled); + break; + + case GL_MULTISAMPLE: + case GL_SAMPLE_ALPHA_TO_COVERAGE: + case GL_SAMPLE_ALPHA_TO_ONE: + case GL_SAMPLE_COVERAGE: + // not supported in this implementation + break; + + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- +using namespace android; + +#if 0 +#pragma mark - +#endif + +// These ones are super-easy, we're not supporting those features! +void glSampleCoverage(GLclampf value, GLboolean invert) { +} +void glSampleCoveragex(GLclampx value, GLboolean invert) { +} +void glStencilFunc(GLenum func, GLint ref, GLuint mask) { + ogles_context_t* c = ogles_context_t::get(); + if (func < GL_NEVER || func > GL_ALWAYS) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + // from OpenGL|ES 1.0 sepcification: + // If there is no stencil buffer, no stencil modification can occur + // and it is as if the stencil test always passes. +} + +void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) { + ogles_context_t* c = ogles_context_t::get(); + if ((stencilop_valid(fail) & + stencilop_valid(zfail) & + stencilop_valid(zpass)) == 0) { + ogles_error(c, GL_INVALID_ENUM); + return; + } +} + +// ---------------------------------------------------------------------------- + +void glAlphaFunc(GLenum func, GLclampf ref) +{ + glAlphaFuncx(func, gglFloatToFixed(ref)); +} + +void glCullFace(GLenum mode) +{ + ogles_context_t* c = ogles_context_t::get(); + switch (mode) { + case GL_FRONT: + case GL_BACK: + case GL_FRONT_AND_BACK: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + } + c->cull.cullFace = mode; +} + +void glFrontFace(GLenum mode) +{ + ogles_context_t* c = ogles_context_t::get(); + switch (mode) { + case GL_CW: + case GL_CCW: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->cull.frontFace = mode; +} + +void glHint(GLenum target, GLenum mode) +{ + ogles_context_t* c = ogles_context_t::get(); + switch (target) { + case GL_FOG_HINT: + case GL_GENERATE_MIPMAP_HINT: + case GL_LINE_SMOOTH_HINT: + break; + case GL_POINT_SMOOTH_HINT: + c->rasterizer.procs.enableDisable(c, + GGL_POINT_SMOOTH_NICE, mode==GL_NICEST); + break; + case GL_PERSPECTIVE_CORRECTION_HINT: + c->perspective = (mode == GL_NICEST) ? 1 : 0; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + } +} + +void glEnable(GLenum cap) { + ogles_context_t* c = ogles_context_t::get(); + enable_disable(c, cap, 1); +} +void glDisable(GLenum cap) { + ogles_context_t* c = ogles_context_t::get(); + enable_disable(c, cap, 0); +} + +void glFinish() +{ // nothing to do for our software implementation +} + +void glFlush() +{ // nothing to do for our software implementation +} + +GLenum glGetError() +{ + // From OpenGL|ES 1.0 specification: + // If more than one flag has recorded an error, glGetError returns + // and clears an arbitrary error flag value. Thus, glGetError should + // always be called in a loop, until it returns GL_NO_ERROR, + // if all error flags are to be reset. + + ogles_context_t* c = ogles_context_t::get(); + if (c->error) { + const GLenum ret(c->error); + c->error = 0; + return ret; + } + + if (c->rasterizer.error) { + const GLenum ret(c->rasterizer.error); + c->rasterizer.error = 0; + return ret; + } + + return GL_NO_ERROR; +} + +const GLubyte* glGetString(GLenum string) +{ + switch (string) { + case GL_VENDOR: return (const GLubyte*)gVendorString; + case GL_RENDERER: return (const GLubyte*)gRendererString; + case GL_VERSION: return (const GLubyte*)gVersionString; + case GL_EXTENSIONS: return (const GLubyte*)gExtensionsString; + } + ogles_context_t* c = ogles_context_t::get(); + ogles_error(c, GL_INVALID_ENUM); + return 0; +} + +void glGetIntegerv(GLenum pname, GLint *params) +{ + ogles_context_t* c = ogles_context_t::get(); + switch (pname) { + case GL_ALIASED_POINT_SIZE_RANGE: + params[0] = 0; + params[1] = GGL_MAX_ALIASED_POINT_SIZE; + break; + case GL_ALIASED_LINE_WIDTH_RANGE: + params[0] = 0; + params[1] = GGL_MAX_ALIASED_POINT_SIZE; + break; + case GL_ALPHA_BITS: { + int index = c->rasterizer.state.buffers.color.format; + GGLFormat const * formats = gglGetPixelFormatTable(); + params[0] = formats[index].ah - formats[index].al; + break; + } + case GL_RED_BITS: { + int index = c->rasterizer.state.buffers.color.format; + GGLFormat const * formats = gglGetPixelFormatTable(); + params[0] = formats[index].rh - formats[index].rl; + break; + } + case GL_GREEN_BITS: { + int index = c->rasterizer.state.buffers.color.format; + GGLFormat const * formats = gglGetPixelFormatTable(); + params[0] = formats[index].gh - formats[index].gl; + break; + } + case GL_BLUE_BITS: { + int index = c->rasterizer.state.buffers.color.format; + GGLFormat const * formats = gglGetPixelFormatTable(); + params[0] = formats[index].bh - formats[index].bl; + break; + } + case GL_COMPRESSED_TEXTURE_FORMATS: + params[ 0] = GL_PALETTE4_RGB8_OES; + params[ 1] = GL_PALETTE4_RGBA8_OES; + params[ 2] = GL_PALETTE4_R5_G6_B5_OES; + params[ 3] = GL_PALETTE4_RGBA4_OES; + params[ 4] = GL_PALETTE4_RGB5_A1_OES; + params[ 5] = GL_PALETTE8_RGB8_OES; + params[ 6] = GL_PALETTE8_RGBA8_OES; + params[ 7] = GL_PALETTE8_R5_G6_B5_OES; + params[ 8] = GL_PALETTE8_RGBA4_OES; + params[ 9] = GL_PALETTE8_RGB5_A1_OES; + break; + case GL_DEPTH_BITS: + params[0] = c->rasterizer.state.buffers.depth.format ? 0 : 16; + break; + case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES: + params[0] = GL_RGB; + break; + case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES: + params[0] = GL_UNSIGNED_SHORT_5_6_5; + break; + case GL_MAX_LIGHTS: + params[0] = OGLES_MAX_LIGHTS; + break; + case GL_MAX_CLIP_PLANES: + params[0] = OGLES_MAX_CLIP_PLANES; + break; + case GL_MAX_MODELVIEW_STACK_DEPTH: + params[0] = OGLES_MODELVIEW_STACK_DEPTH; + break; + case GL_MAX_PROJECTION_STACK_DEPTH: + params[0] = OGLES_PROJECTION_STACK_DEPTH; + break; + case GL_MAX_TEXTURE_STACK_DEPTH: + params[0] = OGLES_TEXTURE_STACK_DEPTH; + break; + case GL_MAX_TEXTURE_SIZE: + params[0] = GGL_MAX_TEXTURE_SIZE; + break; + case GL_MAX_TEXTURE_UNITS: + params[0] = GGL_TEXTURE_UNIT_COUNT; + break; + case GL_MAX_VIEWPORT_DIMS: + params[0] = GGL_MAX_VIEWPORT_DIMS; + params[1] = GGL_MAX_VIEWPORT_DIMS; + break; + case GL_NUM_COMPRESSED_TEXTURE_FORMATS: + params[0] = OGLES_NUM_COMPRESSED_TEXTURE_FORMATS; + break; + case GL_SMOOTH_LINE_WIDTH_RANGE: + params[0] = 0; + params[1] = GGL_MAX_SMOOTH_LINE_WIDTH; + break; + case GL_SMOOTH_POINT_SIZE_RANGE: + params[0] = 0; + params[1] = GGL_MAX_SMOOTH_POINT_SIZE; + break; + case GL_STENCIL_BITS: + params[0] = 0; + break; + case GL_SUBPIXEL_BITS: + params[0] = GGL_SUBPIXEL_BITS; + break; + + case GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES: + memcpy( params, + c->transforms.modelview.top().elements(), + 16*sizeof(GLint)); + break; + case GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES: + memcpy( params, + c->transforms.projection.top().elements(), + 16*sizeof(GLint)); + break; + case GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES: + memcpy( params, + c->transforms.texture[c->textures.active].top().elements(), + 16*sizeof(GLint)); + break; + + default: + ogles_error(c, GL_INVALID_ENUM); + break; + } +} + +// ---------------------------------------------------------------------------- + +void glPointSize(GLfloat size) +{ + ogles_context_t* c = ogles_context_t::get(); + if (size <= 0) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->point.size = TRI_FROM_FIXED(gglFloatToFixed(size)); +} + +void glPointSizex(GLfixed size) +{ + ogles_context_t* c = ogles_context_t::get(); + if (size <= 0) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->point.size = TRI_FROM_FIXED(size); +} + +// ---------------------------------------------------------------------------- + +void glLineWidth(GLfloat width) +{ + ogles_context_t* c = ogles_context_t::get(); + if (width <= 0) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->line.width = TRI_FROM_FIXED(gglFloatToFixed(width)); +} + +void glLineWidthx(GLfixed width) +{ + ogles_context_t* c = ogles_context_t::get(); + if (width <= 0) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->line.width = TRI_FROM_FIXED(width); +} + +// ---------------------------------------------------------------------------- + +void glColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.colorMask(c, r, g, b, a); +} + +void glDepthMask(GLboolean flag) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.depthMask(c, flag); +} + +void glStencilMask(GLuint mask) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.stencilMask(c, mask); +} + +void glDepthFunc(GLenum func) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.depthFunc(c, func); +} + +void glLogicOp(GLenum opcode) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.logicOp(c, opcode); +} + +void glAlphaFuncx(GLenum func, GLclampx ref) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.alphaFuncx(c, func, ref); +} + +void glBlendFunc(GLenum sfactor, GLenum dfactor) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.blendFunc(c, sfactor, dfactor); +} + +void glClear(GLbitfield mask) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clear(c, mask); +} + +void glClearColorx(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clearColorx(c, red, green, blue, alpha); +} + +void glClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a) +{ + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clearColorx(c, + gglFloatToFixed(r), + gglFloatToFixed(g), + gglFloatToFixed(b), + gglFloatToFixed(a)); +} + +void glClearDepthx(GLclampx depth) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clearDepthx(c, depth); +} + +void glClearDepthf(GLclampf depth) +{ + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clearDepthx(c, gglFloatToFixed(depth)); +} + +void glClearStencil(GLint s) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clearStencil(c, s); +} diff --git a/opengl/libagl/state.h b/opengl/libagl/state.h new file mode 100644 index 000000000..55a5ccbd1 --- /dev/null +++ b/opengl/libagl/state.h @@ -0,0 +1,54 @@ +/* libs/opengles/state.h +** +** Copyright 2006, 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. +*/ + +#ifndef ANDROID_OPENGLES_STATE_H +#define ANDROID_OPENGLES_STATE_H + +#include +#include +#include + +#include + +#include + +#include + +namespace android { + +ogles_context_t *ogles_init(size_t extra); +void ogles_uninit(ogles_context_t* c); +void _ogles_error(ogles_context_t* c, GLenum error); + +#ifndef TRACE_GL_ERRORS +#define TRACE_GL_ERRORS 0 +#endif + +#if TRACE_GL_ERRORS +#define ogles_error(c, error) \ +do { \ + printf("ogles_error at file %s line %d\n", __FILE__, __LINE__); \ + _ogles_error(c, error); \ +} while (0) +#else /* !TRACE_GL_ERRORS */ +#define ogles_error(c, error) _ogles_error((c), (error)) +#endif + +}; // namespace android + +#endif // ANDROID_OPENGLES_STATE_H + diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp new file mode 100644 index 000000000..b6f534b7e --- /dev/null +++ b/opengl/libagl/texture.cpp @@ -0,0 +1,1421 @@ +/* libs/opengles/texture.cpp +** +** Copyright 2006, 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 "context.h" +#include "fp.h" +#include "state.h" +#include "texture.h" +#include "TextureObjectManager.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static void bindTextureTmu( + ogles_context_t* c, int tmu, GLuint texture, const sp& tex); + +static __attribute__((noinline)) +void generateMipmap(ogles_context_t* c, GLint level); + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Init +#endif + +void ogles_init_texture(ogles_context_t* c) +{ + c->textures.packAlignment = 4; + c->textures.unpackAlignment = 4; + + // each context has a default named (0) texture (not shared) + c->textures.defaultTexture = new EGLTextureObject(); + c->textures.defaultTexture->incStrong(c); + + // bind the default texture to each texture unit + for (int i=0; itextures.defaultTexture); + memset(c->current.texture[i].v, 0, sizeof(vec4_t)); + c->current.texture[i].Q = 0x10000; + } +} + +void ogles_uninit_texture(ogles_context_t* c) +{ + if (c->textures.ggl) + gglUninit(c->textures.ggl); + c->textures.defaultTexture->decStrong(c); + for (int i=0; itextures.tmu[i].texture) + c->textures.tmu[i].texture->decStrong(c); + } +} + +static __attribute__((noinline)) +void validate_tmu(ogles_context_t* c, int i) +{ + texture_unit_t& u(c->textures.tmu[i]); + if (u.dirty) { + u.dirty = 0; + c->rasterizer.procs.activeTexture(c, i); + c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); + c->rasterizer.procs.texGeni(c, GGL_S, + GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); + c->rasterizer.procs.texGeni(c, GGL_T, + GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_WRAP_S, u.texture->wraps); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_WRAP_T, u.texture->wrapt); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_MIN_FILTER, u.texture->min_filter); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter); + + // disable this texture unit if it's not complete + if (!u.texture->isComplete()) { + c->rasterizer.procs.disable(c, GGL_TEXTURE_2D); + } + } +} + +void ogles_validate_texture_impl(ogles_context_t* c) +{ + for (int i=0 ; irasterizer.state.texture[i].enable) + validate_tmu(c, i); + } + c->rasterizer.procs.activeTexture(c, c->textures.active); +} + +static +void invalidate_texture(ogles_context_t* c, int tmu, uint8_t flags = 0xFF) { + c->textures.tmu[tmu].dirty = flags; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Format conversion +#endif + +static uint32_t gl2format_table[6][4] = { + // BYTE, 565, 4444, 5551 + { GGL_PIXEL_FORMAT_A_8, + 0, 0, 0 }, // GL_ALPHA + { GGL_PIXEL_FORMAT_RGB_888, + GGL_PIXEL_FORMAT_RGB_565, + 0, 0 }, // GL_RGB + { GGL_PIXEL_FORMAT_RGBA_8888, + 0, + GGL_PIXEL_FORMAT_RGBA_4444, + GGL_PIXEL_FORMAT_RGBA_5551 }, // GL_RGBA + { GGL_PIXEL_FORMAT_L_8, + 0, 0, 0 }, // GL_LUMINANCE + { GGL_PIXEL_FORMAT_LA_88, + 0, 0, 0 }, // GL_LUMINANCE_ALPHA +}; + +static int32_t convertGLPixelFormat(GLint format, GLenum type) +{ + int32_t fi = -1; + int32_t ti = -1; + switch (format) { + case GL_ALPHA: fi = 0; break; + case GL_RGB: fi = 1; break; + case GL_RGBA: fi = 2; break; + case GL_LUMINANCE: fi = 3; break; + case GL_LUMINANCE_ALPHA: fi = 4; break; + } + switch (type) { + case GL_UNSIGNED_BYTE: ti = 0; break; + case GL_UNSIGNED_SHORT_5_6_5: ti = 1; break; + case GL_UNSIGNED_SHORT_4_4_4_4: ti = 2; break; + case GL_UNSIGNED_SHORT_5_5_5_1: ti = 3; break; + } + if (fi==-1 || ti==-1) + return 0; + return gl2format_table[fi][ti]; +} + +// ---------------------------------------------------------------------------- + +static GLenum validFormatType(ogles_context_t* c, GLenum format, GLenum type) +{ + GLenum error = 0; + if (formatGL_LUMINANCE_ALPHA) { + error = GL_INVALID_ENUM; + } + if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT_4_4_4_4 && + type != GL_UNSIGNED_SHORT_5_5_5_1 && type != GL_UNSIGNED_SHORT_5_6_5) { + error = GL_INVALID_ENUM; + } + if (type == GL_UNSIGNED_SHORT_5_6_5 && format != GL_RGB) { + error = GL_INVALID_OPERATION; + } + if ((type == GL_UNSIGNED_SHORT_4_4_4_4 || + type == GL_UNSIGNED_SHORT_5_5_5_1) && format != GL_RGBA) { + error = GL_INVALID_OPERATION; + } + if (error) { + ogles_error(c, error); + } + return error; +} + +// ---------------------------------------------------------------------------- + +GGLContext* getRasterizer(ogles_context_t* c) +{ + GGLContext* ggl = c->textures.ggl; + if (ggl_unlikely(!ggl)) { + // this is quite heavy the first time... + gglInit(&ggl); + if (!ggl) { + return 0; + } + GGLfixed colors[4] = { 0, 0, 0, 0x10000 }; + c->textures.ggl = ggl; + ggl->activeTexture(ggl, 0); + ggl->enable(ggl, GGL_TEXTURE_2D); + ggl->texEnvi(ggl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); + ggl->disable(ggl, GGL_DITHER); + ggl->shadeModel(ggl, GGL_FLAT); + ggl->color4xv(ggl, colors); + } + return ggl; +} + +static __attribute__((noinline)) +int copyPixels( + ogles_context_t* c, + const GGLSurface& dst, + GLint xoffset, GLint yoffset, + const GGLSurface& src, + GLint x, GLint y, GLsizei w, GLsizei h) +{ + if ((dst.format == src.format) && + (dst.stride == src.stride) && + (dst.width == src.width) && + (dst.height == src.height) && + (dst.stride > 0) && + ((x|y) == 0) && + ((xoffset|yoffset) == 0)) + { + // this is a common case... + const GGLFormat& pixelFormat(c->rasterizer.formats[src.format]); + const size_t size = src.height * src.stride * pixelFormat.size; + memcpy(dst.data, src.data, size); + return 0; + } + + // use pixel-flinger to handle all the conversions + GGLContext* ggl = getRasterizer(c); + if (!ggl) { + // the only reason this would fail is because we ran out of memory + return GL_OUT_OF_MEMORY; + } + + ggl->colorBuffer(ggl, &dst); + ggl->bindTexture(ggl, &src); + ggl->texCoord2i(ggl, x-xoffset, y-yoffset); + ggl->recti(ggl, xoffset, yoffset, xoffset+w, yoffset+h); + return 0; +} + +// ---------------------------------------------------------------------------- + +static __attribute__((noinline)) +sp getAndBindActiveTextureObject(ogles_context_t* c) +{ + sp tex; + const int active = c->textures.active; + const GLuint name = c->textures.tmu[active].name; + + // free the reference to the previously bound object + texture_unit_t& u(c->textures.tmu[active]); + if (u.texture) + u.texture->decStrong(c); + + if (name == 0) { + // 0 is our local texture object, not shared with anyone. + // But it affects all bound TMUs immediately. + // (we need to invalidate all units bound to this texture object) + tex = c->textures.defaultTexture; + for (int i=0 ; itextures.tmu[i].texture == tex.get()) + invalidate_texture(c, i); + } + } else { + // get a new texture object for that name + tex = c->surfaceManager->replaceTexture(name); + } + + // bind this texture to the current active texture unit + // and add a reference to this texture object + u.texture = tex.get(); + u.texture->incStrong(c); + u.name = name; + invalidate_texture(c, active); + return tex; +} + +void bindTextureTmu( + ogles_context_t* c, int tmu, GLuint texture, const sp& tex) +{ + if (tex.get() == c->textures.tmu[tmu].texture) + return; + + // free the reference to the previously bound object + texture_unit_t& u(c->textures.tmu[tmu]); + if (u.texture) + u.texture->decStrong(c); + + // bind this texture to the current active texture unit + // and add a reference to this texture object + u.texture = tex.get(); + u.texture->incStrong(c); + u.name = texture; + invalidate_texture(c, tmu); +} + +int createTextureSurface(ogles_context_t* c, + GGLSurface** outSurface, int32_t* outSize, GLint level, + GLenum format, GLenum type, GLsizei width, GLsizei height, + GLenum compressedFormat = 0) +{ + // find out which texture is bound to the current unit + const int active = c->textures.active; + const GLuint name = c->textures.tmu[active].name; + + // convert the pixelformat to one we can handle + const int32_t formatIdx = convertGLPixelFormat(format, type); + if (formatIdx == 0) { // we don't know what to do with this + return GL_INVALID_OPERATION; + } + + // figure out the size we need as well as the stride + const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); + const int32_t align = c->textures.unpackAlignment-1; + const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; + const size_t size = bpr * height; + const int32_t stride = bpr / pixelFormat.size; + + if (level > 0) { + const int active = c->textures.active; + EGLTextureObject* tex = c->textures.tmu[active].texture; + status_t err = tex->reallocate(level, + width, height, stride, formatIdx, compressedFormat, bpr); + if (err != NO_ERROR) + return GL_OUT_OF_MEMORY; + GGLSurface& surface = tex->editMip(level); + *outSurface = &surface; + *outSize = size; + return 0; + } + + sp tex = getAndBindActiveTextureObject(c); + status_t err = tex->reallocate(level, + width, height, stride, formatIdx, compressedFormat, bpr); + if (err != NO_ERROR) + return GL_OUT_OF_MEMORY; + + tex->internalformat = format; + *outSurface = &tex->surface; + *outSize = size; + return 0; +} + +static void decodePalette4(const GLvoid *data, int level, int width, int height, + void *surface, int stride, int format) + +{ + int indexBits = 8; + int entrySize = 0; + switch (format) { + case GL_PALETTE4_RGB8_OES: + indexBits = 4; + /* FALLTHROUGH */ + case GL_PALETTE8_RGB8_OES: + entrySize = 3; + break; + + case GL_PALETTE4_RGBA8_OES: + indexBits = 4; + /* FALLTHROUGH */ + case GL_PALETTE8_RGBA8_OES: + entrySize = 4; + break; + + case GL_PALETTE4_R5_G6_B5_OES: + case GL_PALETTE4_RGBA4_OES: + case GL_PALETTE4_RGB5_A1_OES: + indexBits = 4; + /* FALLTHROUGH */ + case GL_PALETTE8_R5_G6_B5_OES: + case GL_PALETTE8_RGBA4_OES: + case GL_PALETTE8_RGB5_A1_OES: + entrySize = 2; + break; + } + + const int paletteSize = (1 << indexBits) * entrySize; + uint8_t const* pixels = (uint8_t *)data + paletteSize; + for (int i=0 ; i> i) ? : 1; + int h = (height >> i) ? : 1; + pixels += h * ((w * indexBits) / 8); + } + width = (width >> level) ? : 1; + height = (height >> level) ? : 1; + + if (entrySize == 2) { + uint8_t const* const palette = (uint8_t*)data; + for (int y=0 ; y> 4); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + if (x+1 < width) { + index = 2 * (v & 0xF); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + } + } + } + } + } else if (entrySize == 3) { + uint8_t const* const palette = (uint8_t*)data; + for (int y=0 ; y> 4); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + *p++ = palette[index + 2]; + if (x+1 < width) { + index = 3 * (v & 0xF); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + *p++ = palette[index + 2]; + } + } + } + } + } else if (entrySize == 4) { + uint8_t const* const palette = (uint8_t*)data; + for (int y=0 ; y> 4); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + *p++ = palette[index + 2]; + *p++ = palette[index + 3]; + if (x+1 < width) { + index = 4 * (v & 0xF); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + *p++ = palette[index + 2]; + *p++ = palette[index + 3]; + } + } + } + } + } +} + + + +static __attribute__((noinline)) +void set_depth_and_fog(ogles_context_t* c, GLint z) +{ + const uint32_t enables = c->rasterizer.state.enables; + // we need to compute Zw + int32_t iterators[3]; + iterators[1] = iterators[2] = 0; + GGLfixed Zw; + GGLfixed n = gglFloatToFixed(c->transforms.vpt.zNear); + GGLfixed f = gglFloatToFixed(c->transforms.vpt.zFar); + if (z<=0) Zw = n; + else if (z>=1) Zw = f; + else Zw = gglMulAddx(z, (f-n), n); + if (enables & GGL_ENABLE_FOG) { + // set up fog if needed... + iterators[0] = c->fog.fog(c, Zw); + c->rasterizer.procs.fogGrad3xv(c, iterators); + } + if (enables & GGL_ENABLE_DEPTH_TEST) { + // set up z-test if needed... + int32_t z = (Zw & ~(Zw>>31)); + if (z >= 0x10000) + z = 0xFFFF; + iterators[0] = (z << 16) | z; + c->rasterizer.procs.zGrad3xv(c, iterators); + } +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Generate mimaps +#endif + +extern status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex); + +void generateMipmap(ogles_context_t* c, GLint level) +{ + if (level == 0) { + const int active = c->textures.active; + EGLTextureObject* tex = c->textures.tmu[active].texture; + if (tex->generate_mipmap) { + if (buildAPyramid(c, tex) != NO_ERROR) { + ogles_error(c, GL_OUT_OF_MEMORY); + return; + } + } + } +} + + +static void texParameterx( + GLenum target, GLenum pname, GLfixed param, ogles_context_t* c) +{ + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture; + switch (pname) { + case GL_TEXTURE_WRAP_S: + if ((param == GL_REPEAT) || + (param == GL_CLAMP_TO_EDGE)) { + textureObject->wraps = param; + } else { + goto invalid_enum; + } + break; + case GL_TEXTURE_WRAP_T: + if ((param == GL_REPEAT) || + (param == GL_CLAMP_TO_EDGE)) { + textureObject->wrapt = param; + } else { + goto invalid_enum; + } + break; + case GL_TEXTURE_MIN_FILTER: + if ((param == GL_NEAREST) || + (param == GL_LINEAR) || + (param == GL_NEAREST_MIPMAP_NEAREST) || + (param == GL_LINEAR_MIPMAP_NEAREST) || + (param == GL_NEAREST_MIPMAP_LINEAR) || + (param == GL_LINEAR_MIPMAP_LINEAR)) { + textureObject->min_filter = param; + } else { + goto invalid_enum; + } + break; + case GL_TEXTURE_MAG_FILTER: + if ((param == GL_NEAREST) || + (param == GL_LINEAR)) { + textureObject->mag_filter = param; + } else { + goto invalid_enum; + } + break; + case GL_GENERATE_MIPMAP: + textureObject->generate_mipmap = param; + break; + default: +invalid_enum: + ogles_error(c, GL_INVALID_ENUM); + return; + } + invalidate_texture(c, c->textures.active); +} + + +static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h, + ogles_context_t* c) +{ + // quickly reject empty rects + if ((w|h) <= 0) + return; + + const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; + y = gglIntToFixed(cbSurface.height) - (y + h); + w >>= FIXED_BITS; + h >>= FIXED_BITS; + + // set up all texture units + for (int i=0 ; irasterizer.state.texture[i].enable) + continue; + + int32_t texcoords[8]; + texture_unit_t& u(c->textures.tmu[i]); + + // validate this tmu (bind, wrap, filter) + validate_tmu(c, i); + // we CLAMP here, which works with premultiplied (s,t) + c->rasterizer.procs.texParameteri(c, + GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP); + c->rasterizer.procs.texParameteri(c, + GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP); + u.dirty = 0xFF; // XXX: should be more subtle + + EGLTextureObject* textureObject = u.texture; + const GLint Ucr = textureObject->crop_rect[0] << 16; + const GLint Vcr = textureObject->crop_rect[1] << 16; + const GLint Wcr = textureObject->crop_rect[2] << 16; + const GLint Hcr = textureObject->crop_rect[3] << 16; + + // computes texture coordinates (pre-multiplied) + int32_t dsdx = Wcr / w; // dsdx = ((Wcr/w)/Wt)*Wt + int32_t dtdy =-Hcr / h; // dtdy = -((Hcr/h)/Ht)*Ht + int32_t s0 = Ucr - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx + int32_t t0 = (Vcr+Hcr) - gglMulx(dtdy, y); // t0 = (Vcr+Hcr) - y*dtdy + texcoords[0] = s0; + texcoords[1] = dsdx; + texcoords[2] = 0; + texcoords[3] = t0; + texcoords[4] = 0; + texcoords[5] = dtdy; + texcoords[6] = 0; + texcoords[7] = 0; + c->rasterizer.procs.texCoordGradScale8xv(c, i, texcoords); + } + + const uint32_t enables = c->rasterizer.state.enables; + if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG))) + set_depth_and_fog(c, z); + + c->rasterizer.procs.activeTexture(c, c->textures.active); + c->rasterizer.procs.color4xv(c, c->currentColorClamped.v); + c->rasterizer.procs.disable(c, GGL_W_LERP); + c->rasterizer.procs.disable(c, GGL_AA); + c->rasterizer.procs.shadeModel(c, GL_FLAT); + c->rasterizer.procs.recti(c, + gglFixedToIntRound(x), + gglFixedToIntRound(y), + gglFixedToIntRound(x)+w, + gglFixedToIntRound(y)+h); +} + +static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c) +{ + // All coordinates are integer, so if we have only one + // texture unit active and no scaling is required + // THEN, we can use our special 1:1 mapping + // which is a lot faster. + + if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) { + const int tmu = 0; + texture_unit_t& u(c->textures.tmu[tmu]); + EGLTextureObject* textureObject = u.texture; + const GLint Wcr = textureObject->crop_rect[2]; + const GLint Hcr = textureObject->crop_rect[3]; + + if ((w == Wcr) && (h == -Hcr)) { + if ((w|h) <= 0) return; // quickly reject empty rects + + if (u.dirty) { + c->rasterizer.procs.activeTexture(c, tmu); + c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_MIN_FILTER, u.texture->min_filter); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter); + } + c->rasterizer.procs.texGeni(c, GGL_S, + GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + c->rasterizer.procs.texGeni(c, GGL_T, + GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + u.dirty = 0xFF; // XXX: should be more subtle + c->rasterizer.procs.activeTexture(c, c->textures.active); + + const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; + y = cbSurface.height - (y + h); + const GLint Ucr = textureObject->crop_rect[0]; + const GLint Vcr = textureObject->crop_rect[1]; + const GLint s0 = Ucr - x; + const GLint t0 = (Vcr + Hcr) - y; + + const GLuint tw = textureObject->surface.width; + const GLuint th = textureObject->surface.height; + if ((uint32_t(s0+x+w) > tw) || (uint32_t(t0+y+h) > th)) { + // The GL spec is unclear about what should happen + // in this case, so we just use the slow case, which + // at least won't crash + goto slow_case; + } + + c->rasterizer.procs.texCoord2i(c, s0, t0); + const uint32_t enables = c->rasterizer.state.enables; + if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG))) + set_depth_and_fog(c, z); + + c->rasterizer.procs.color4xv(c, c->currentColorClamped.v); + c->rasterizer.procs.disable(c, GGL_W_LERP); + c->rasterizer.procs.disable(c, GGL_AA); + c->rasterizer.procs.shadeModel(c, GL_FLAT); + c->rasterizer.procs.recti(c, x, y, x+w, y+h); + return; + } + } + +slow_case: + drawTexxOES( + gglIntToFixed(x), gglIntToFixed(y), gglIntToFixed(z), + gglIntToFixed(w), gglIntToFixed(h), + c); +} + + +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + + +#if 0 +#pragma mark - +#pragma mark Texture API +#endif + +void glActiveTexture(GLenum texture) +{ + ogles_context_t* c = ogles_context_t::get(); + if (uint32_t(texture-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->textures.active = texture - GL_TEXTURE0; + c->rasterizer.procs.activeTexture(c, c->textures.active); +} + +void glBindTexture(GLenum target, GLuint texture) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + // Bind or create a texture + sp tex; + if (texture == 0) { + // 0 is our local texture object + tex = c->textures.defaultTexture; + } else { + tex = c->surfaceManager->texture(texture); + if (ggl_unlikely(tex == 0)) { + tex = c->surfaceManager->createTexture(texture); + if (tex == 0) { + ogles_error(c, GL_OUT_OF_MEMORY); + return; + } + } + } + bindTextureTmu(c, c->textures.active, texture, tex); +} + +void glGenTextures(GLsizei n, GLuint *textures) +{ + ogles_context_t* c = ogles_context_t::get(); + if (n<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + // generate unique (shared) texture names + c->surfaceManager->getToken(n, textures); +} + +void glDeleteTextures(GLsizei n, const GLuint *textures) +{ + ogles_context_t* c = ogles_context_t::get(); + if (n<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + // If deleting a bound texture, bind this unit to 0 + for (int t=0 ; ttextures.tmu[t].name == 0) + continue; + for (int i=0 ; itextures.tmu[t].name)) { + // bind this tmu to texture 0 + sp tex(c->textures.defaultTexture); + bindTextureTmu(c, t, 0, tex); + } + } + } + c->surfaceManager->deleteTextures(n, textures); + c->surfaceManager->recycleTokens(n, textures); +} + +void glMultiTexCoord4f( + GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) +{ + ogles_context_t* c = ogles_context_t::get(); + if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + const int tmu = target-GL_TEXTURE0; + c->current.texture[tmu].S = gglFloatToFixed(s); + c->current.texture[tmu].T = gglFloatToFixed(t); + c->current.texture[tmu].R = gglFloatToFixed(r); + c->current.texture[tmu].Q = gglFloatToFixed(q); +} + +void glMultiTexCoord4x( + GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) +{ + ogles_context_t* c = ogles_context_t::get(); + if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + const int tmu = target-GL_TEXTURE0; + c->current.texture[tmu].S = s; + c->current.texture[tmu].T = t; + c->current.texture[tmu].R = r; + c->current.texture[tmu].Q = q; +} + +void glPixelStorei(GLenum pname, GLint param) +{ + ogles_context_t* c = ogles_context_t::get(); + if ((pname != GL_PACK_ALIGNMENT) && (pname != GL_UNPACK_ALIGNMENT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if ((param<=0 || param>8) || (param & (param-1))) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (pname == GL_PACK_ALIGNMENT) + c->textures.packAlignment = param; + if (pname == GL_UNPACK_ALIGNMENT) + c->textures.unpackAlignment = param; +} + +void glTexEnvf(GLenum target, GLenum pname, GLfloat param) +{ + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.texEnvi(c, target, pname, GLint(param)); +} + +void glTexEnvfv( + GLenum target, GLenum pname, const GLfloat *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (pname == GL_TEXTURE_ENV_MODE) { + c->rasterizer.procs.texEnvi(c, target, pname, GLint(*params)); + return; + } + if (pname == GL_TEXTURE_ENV_COLOR) { + GGLfixed fixed[4]; + for (int i=0 ; i<4 ; i++) + fixed[i] = gglFloatToFixed(params[i]); + c->rasterizer.procs.texEnvxv(c, target, pname, fixed); + return; + } + ogles_error(c, GL_INVALID_ENUM); +} + +void glTexEnvx(GLenum target, GLenum pname, GLfixed param) +{ + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.texEnvi(c, target, pname, param); +} + +void glTexEnvxv( + GLenum target, GLenum pname, const GLfixed *params) +{ + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.texEnvxv(c, target, pname, params); +} + +void glTexParameteriv( + GLenum target, GLenum pname, const GLint* params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GGL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture; + switch (pname) { + case GL_TEXTURE_CROP_RECT_OES: + memcpy(textureObject->crop_rect, params, 4*sizeof(GLint)); + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } +} + +void glTexParameterf( + GLenum target, GLenum pname, GLfloat param) +{ + ogles_context_t* c = ogles_context_t::get(); + texParameterx(target, pname, GLfixed(param), c); +} + +void glTexParameterx( + GLenum target, GLenum pname, GLfixed param) +{ + ogles_context_t* c = ogles_context_t::get(); + texParameterx(target, pname, param, c); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void glCompressedTexImage2D( + GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid *data) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if ((internalformat < GL_PALETTE4_RGB8_OES || + internalformat > GL_PALETTE8_RGB5_A1_OES)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (width<0 || height<0 || border!=0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + // "uncompress" the texture since pixelflinger doesn't support + // any compressed texture format natively. + GLenum format; + GLenum type; + switch (internalformat) { + case GL_PALETTE8_RGB8_OES: + case GL_PALETTE4_RGB8_OES: + format = GL_RGB; + type = GL_UNSIGNED_BYTE; + break; + case GL_PALETTE8_RGBA8_OES: + case GL_PALETTE4_RGBA8_OES: + format = GL_RGBA; + type = GL_UNSIGNED_BYTE; + break; + case GL_PALETTE8_R5_G6_B5_OES: + case GL_PALETTE4_R5_G6_B5_OES: + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + break; + case GL_PALETTE8_RGBA4_OES: + case GL_PALETTE4_RGBA4_OES: + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case GL_PALETTE8_RGB5_A1_OES: + case GL_PALETTE4_RGB5_A1_OES: + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_5_5_5_1; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + + if (!data || !width || !height) { + // unclear if this is an error or not... + return; + } + + int32_t size; + GGLSurface* surface; + // all mipmap levels are specified at once. + const int numLevels = level<0 ? -level : 1; + for (int i=0 ; i> i) ? : 1; + int lod_h = (height >> i) ? : 1; + int error = createTextureSurface(c, &surface, &size, + i, format, type, lod_w, lod_h); + if (error) { + ogles_error(c, error); + return; + } + decodePalette4(data, i, width, height, + surface->data, surface->stride, internalformat); + } +} + + +void glTexImage2D( + GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, + GLenum format, GLenum type, const GLvoid *pixels) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D && target != GL_DIRECT_TEXTURE_2D_QUALCOMM) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (width<0 || height<0 || border!=0 || level < 0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (format != internalformat) { + ogles_error(c, GL_INVALID_OPERATION); + return; + } + if (validFormatType(c, format, type)) { + return; + } + + int32_t size = 0; + GGLSurface* surface = 0; + if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) { + int error = createTextureSurface(c, &surface, &size, + level, format, type, width, height); + if (error) { + ogles_error(c, error); + return; + } + } else if (pixels == 0 || level != 0) { + // pixel can't be null for direct texture + ogles_error(c, GL_INVALID_OPERATION); + return; + } + + if (pixels) { + const int32_t formatIdx = convertGLPixelFormat(format, type); + const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); + const int32_t align = c->textures.unpackAlignment-1; + const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; + const size_t size = bpr * height; + const int32_t stride = bpr / pixelFormat.size; + + GGLSurface userSurface; + userSurface.version = sizeof(userSurface); + userSurface.width = width; + userSurface.height = height; + userSurface.stride = stride; + userSurface.format = formatIdx; + userSurface.compressedFormat = 0; + userSurface.data = (GLubyte*)pixels; + + if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) { + int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height); + if (err) { + ogles_error(c, err); + return; + } + generateMipmap(c, level); + } else { + // bind it to the texture unit + sp tex = getAndBindActiveTextureObject(c); + tex->setSurface(&userSurface); + } + } +} + +// ---------------------------------------------------------------------------- + +void glCompressedTexSubImage2D( + GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLsizei imageSize, + const GLvoid *data) +{ + ogles_context_t* c = ogles_context_t::get(); + ogles_error(c, GL_INVALID_ENUM); +} + +void glTexSubImage2D( + GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLenum type, const GLvoid *pixels) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (validFormatType(c, format, type)) { + return; + } + + // find out which texture is bound to the current unit + const int active = c->textures.active; + EGLTextureObject* tex = c->textures.tmu[active].texture; + const GGLSurface& surface(tex->mip(level)); + + if (!tex->internalformat || tex->direct) { + ogles_error(c, GL_INVALID_OPERATION); + return; + } + if ((xoffset + width > GLsizei(surface.width)) || + (yoffset + height > GLsizei(surface.height))) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (!width || !height) { + return; // okay, but no-op. + } + + // figure out the size we need as well as the stride + const int32_t formatIdx = convertGLPixelFormat(format, type); + if (formatIdx == 0) { // we don't know what to do with this + ogles_error(c, GL_INVALID_OPERATION); + return; + } + + const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); + const int32_t align = c->textures.unpackAlignment-1; + const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; + const size_t size = bpr * height; + const int32_t stride = bpr / pixelFormat.size; + GGLSurface userSurface; + userSurface.version = sizeof(userSurface); + userSurface.width = width; + userSurface.height = height; + userSurface.stride = stride; + userSurface.format = formatIdx; + userSurface.compressedFormat = 0; + userSurface.data = (GLubyte*)pixels; + + int err = copyPixels(c, + surface, xoffset, yoffset, + userSurface, 0, 0, width, height); + if (err) { + ogles_error(c, err); + return; + } + + generateMipmap(c, level); + + // since we only changed the content of the texture, we don't need + // to call bindTexture on the main rasterizer. +} + +// ---------------------------------------------------------------------------- + +void glCopyTexImage2D( + GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, + GLint border) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (internalformatGL_LUMINANCE_ALPHA) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (width<0 || height<0 || border!=0 || level<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + GLenum format = 0; + GLenum type = GL_UNSIGNED_BYTE; + const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; + const int cbFormatIdx = cbSurface.format; + switch (cbFormatIdx) { + case GGL_PIXEL_FORMAT_RGB_565: + type = GL_UNSIGNED_SHORT_5_6_5; + break; + case GGL_PIXEL_FORMAT_RGBA_5551: + type = GL_UNSIGNED_SHORT_5_5_5_1; + break; + case GGL_PIXEL_FORMAT_RGBA_4444: + type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + } + switch (internalformat) { + case GL_ALPHA: + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE: + type = GL_UNSIGNED_BYTE; + break; + } + + // figure out the format to use for the new texture + switch (cbFormatIdx) { + case GGL_PIXEL_FORMAT_RGBA_8888: + case GGL_PIXEL_FORMAT_A_8: + case GGL_PIXEL_FORMAT_RGBA_5551: + case GGL_PIXEL_FORMAT_RGBA_4444: + format = internalformat; + break; + case GGL_PIXEL_FORMAT_RGBX_8888: + case GGL_PIXEL_FORMAT_RGB_888: + case GGL_PIXEL_FORMAT_RGB_565: + case GGL_PIXEL_FORMAT_L_8: + switch (internalformat) { + case GL_LUMINANCE: + case GL_RGB: + format = internalformat; + break; + } + break; + } + + if (format == 0) { + // invalid combination + ogles_error(c, GL_INVALID_ENUM); + return; + } + + // create the new texture... + int32_t size; + GGLSurface* surface; + int error = createTextureSurface(c, &surface, &size, + level, format, type, width, height); + if (error) { + ogles_error(c, error); + return; + } + + // The bottom row is stored first in textures + GGLSurface txSurface(*surface); + txSurface.stride = -txSurface.stride; + + // (x,y) is the lower-left corner of colorBuffer + y = cbSurface.height - (y + height); + + int err = copyPixels(c, + txSurface, 0, 0, + cbSurface, x, y, cbSurface.width, cbSurface.height); + if (err) { + ogles_error(c, err); + } + + generateMipmap(c, level); +} + +void glCopyTexSubImage2D( + GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLint x, GLint y, GLsizei width, GLsizei height) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (!width || !height) { + return; // okay, but no-op. + } + + // find out which texture is bound to the current unit + const int active = c->textures.active; + EGLTextureObject* tex = c->textures.tmu[active].texture; + const GGLSurface& surface(tex->mip(level)); + + if (!tex->internalformat) { + ogles_error(c, GL_INVALID_OPERATION); + return; + } + if ((xoffset + width > GLsizei(surface.width)) || + (yoffset + height > GLsizei(surface.height))) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + // The bottom row is stored first in textures + GGLSurface txSurface(surface); + txSurface.stride = -txSurface.stride; + + // (x,y) is the lower-left corner of colorBuffer + const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; + y = cbSurface.height - (y + height); + + int err = copyPixels(c, + surface, xoffset, yoffset, + cbSurface, x, y, width, height); + if (err) { + ogles_error(c, err); + return; + } + + generateMipmap(c, level); +} + +void glReadPixels( + GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid *pixels) +{ + ogles_context_t* c = ogles_context_t::get(); + if ((format != GL_RGBA) && (format != GL_RGB)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if ((type != GL_UNSIGNED_BYTE) && (type != GL_UNSIGNED_SHORT_5_6_5)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (width<0 || height<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (x<0 || x<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + int32_t formatIdx = GGL_PIXEL_FORMAT_NONE; + if ((format == GL_RGBA) && (type == GL_UNSIGNED_BYTE)) { + formatIdx = GGL_PIXEL_FORMAT_RGBA_8888; + } else if ((format == GL_RGB) && (type == GL_UNSIGNED_SHORT_5_6_5)) { + formatIdx = GGL_PIXEL_FORMAT_RGB_565; + } else { + ogles_error(c, GL_INVALID_OPERATION); + return; + } + + const GGLSurface& readSurface = c->rasterizer.state.buffers.read.s; + if ((x+width > GLint(readSurface.width)) || + (y+height > GLint(readSurface.height))) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); + const int32_t align = c->textures.packAlignment-1; + const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; + const int32_t stride = bpr / pixelFormat.size; + + GGLSurface userSurface; + userSurface.version = sizeof(userSurface); + userSurface.width = width; + userSurface.height = height; + userSurface.stride = -stride; // bottom row is transfered first + userSurface.format = formatIdx; + userSurface.compressedFormat = 0; + userSurface.data = (GLubyte*)pixels; + + // use pixel-flinger to handle all the conversions + GGLContext* ggl = getRasterizer(c); + if (!ggl) { + // the only reason this would fail is because we ran out of memory + ogles_error(c, GL_OUT_OF_MEMORY); + return; + } + + ggl->colorBuffer(ggl, &userSurface); // destination is user buffer + ggl->bindTexture(ggl, &readSurface); // source is read-buffer + ggl->texCoord2i(ggl, x, readSurface.height - (y + height)); + ggl->recti(ggl, 0, 0, width, height); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark DrawTexture Extension +#endif + +void glDrawTexsvOES(const GLshort* coords) { + ogles_context_t* c = ogles_context_t::get(); + drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); +} +void glDrawTexivOES(const GLint* coords) { + ogles_context_t* c = ogles_context_t::get(); + drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); +} +void glDrawTexsOES(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) { + ogles_context_t* c = ogles_context_t::get(); + drawTexiOES(x, y, z, w, h, c); +} +void glDrawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h) { + ogles_context_t* c = ogles_context_t::get(); + drawTexiOES(x, y, z, w, h, c); +} + +void glDrawTexfvOES(const GLfloat* coords) { + ogles_context_t* c = ogles_context_t::get(); + drawTexxOES( + gglFloatToFixed(coords[0]), + gglFloatToFixed(coords[1]), + gglFloatToFixed(coords[2]), + gglFloatToFixed(coords[3]), + gglFloatToFixed(coords[4]), + c); +} +void glDrawTexxvOES(const GLfixed* coords) { + ogles_context_t* c = ogles_context_t::get(); + drawTexxOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); +} +void glDrawTexfOES(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h){ + ogles_context_t* c = ogles_context_t::get(); + drawTexxOES( + gglFloatToFixed(x), gglFloatToFixed(y), gglFloatToFixed(z), + gglFloatToFixed(w), gglFloatToFixed(h), + c); +} +void glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) { + ogles_context_t* c = ogles_context_t::get(); + drawTexxOES(x, y, z, w, h, c); +} diff --git a/opengl/libagl/texture.h b/opengl/libagl/texture.h new file mode 100644 index 000000000..5c57948c9 --- /dev/null +++ b/opengl/libagl/texture.h @@ -0,0 +1,45 @@ +/* libs/opengles/texture.h +** +** Copyright 2006, 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. +*/ + +#ifndef ANDROID_OPENGLES_TEXTURE_H +#define ANDROID_OPENGLES_TEXTURE_H + +#include +#include +#include + +#include + +#include + +#include "context.h" + +namespace android { + +void ogles_init_texture(ogles_context_t* c); +void ogles_uninit_texture(ogles_context_t* c); +void ogles_validate_texture_impl(ogles_context_t* c); + +inline void ogles_validate_texture(ogles_context_t* c) { + if (c->rasterizer.state.enables & GGL_ENABLE_TMUS) + ogles_validate_texture_impl(c); +} + + +}; // namespace android + +#endif // ANDROID_OPENGLES_TEXTURE_H diff --git a/opengl/libagl/vertex.cpp b/opengl/libagl/vertex.cpp new file mode 100644 index 000000000..dad04d6cb --- /dev/null +++ b/opengl/libagl/vertex.cpp @@ -0,0 +1,247 @@ +/* libs/opengles/vertex.cpp +** +** Copyright 2006, 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 "context.h" +#include "fp.h" +#include "vertex.h" +#include "state.h" +#include "matrix.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +void ogles_init_vertex(ogles_context_t* c) +{ + c->cull.enable = GL_FALSE; + c->cull.cullFace = GL_BACK; + c->cull.frontFace = GL_CCW; + + c->current.color.r = 0x10000; + c->current.color.g = 0x10000; + c->current.color.b = 0x10000; + c->current.color.a = 0x10000; + + c->currentNormal.z = 0x10000; +} + +void ogles_uninit_vertex(ogles_context_t* c) +{ +} + +// ---------------------------------------------------------------------------- +// vertex processing +// ---------------------------------------------------------------------------- + +// Divides a vertex clip coordinates by W +static inline +void perspective(ogles_context_t* c, vertex_t* v, uint32_t enables) +{ + // [x,y,z]window = vpt * ([x,y,z]clip / clip.w) + // [w]window = 1/w + + // With a regular projection generated by glFrustum(), + // we have w=-z, therefore, w is in [zNear, zFar]. + // Also, zNear and zFar are stricly positive, + // and 1/w (window.w) is in [1/zFar, 1/zNear], usually this + // means ]0, +inf[ -- however, it is always recommended + // to use as large values as possible for zNear. + // All in all, w is usually smaller than 1.0 (assuming + // zNear is at least 1.0); and even if zNear is smaller than 1.0 + // values of w won't be too big. + + const int32_t rw = gglRecip28(v->clip.w); + const GLfixed* const m = c->transforms.vpt.transform.matrix.m; + v->window.w = rw; + v->window.x = gglMulAddx(gglMulx(v->clip.x, rw, 16), m[ 0], m[12], 28); + v->window.y = gglMulAddx(gglMulx(v->clip.y, rw, 16), m[ 5], m[13], 28); + v->window.x = TRI_FROM_FIXED(v->window.x); + v->window.y = TRI_FROM_FIXED(v->window.y); + if (enables & GGL_ENABLE_DEPTH_TEST) { + v->window.z = gglMulAddx(gglMulx(v->clip.z, rw, 16), m[10], m[14], 28); + } +} + +// frustum clipping and W-divide +static inline +void clipFrustumPerspective(ogles_context_t* c, vertex_t* v, uint32_t enables) +{ + // ndc = clip / W + // window = ncd * viewport + + // clip to the view-volume + uint32_t clip = v->flags & vertex_t::CLIP_ALL; + const GLfixed w = v->clip.w; + if (v->clip.x < -w) clip |= vertex_t::CLIP_L; + if (v->clip.x > w) clip |= vertex_t::CLIP_R; + if (v->clip.y < -w) clip |= vertex_t::CLIP_B; + if (v->clip.y > w) clip |= vertex_t::CLIP_T; + if (v->clip.z < -w) clip |= vertex_t::CLIP_N; + if (v->clip.z > w) clip |= vertex_t::CLIP_F; + + v->flags |= clip; + c->arrays.cull &= clip; + + if (ggl_likely(!clip)) { + // if the vertex is clipped, we don't do the perspective + // divide, since we don't need its window coordinates. + perspective(c, v, enables); + } +} + +// frustum clipping, user clipping and W-divide +static inline +void clipAllPerspective(ogles_context_t* c, vertex_t* v, uint32_t enables) +{ + // compute eye coordinates + c->arrays.mv_transform( + &c->transforms.modelview.transform, &v->eye, &v->obj); + v->flags |= vertex_t::EYE; + + // clip this vertex against each user clip plane + uint32_t clip = 0; + int planes = c->clipPlanes.enable; + while (planes) { + const int i = 31 - gglClz(planes); + planes &= ~(1<clipPlanes.plane[i].equation.v, v->eye.v); + if (d < 0) { + clip |= 0x100<flags |= clip; + + clipFrustumPerspective(c, v, enables); +} + +// ---------------------------------------------------------------------------- + +void ogles_vertex_project(ogles_context_t* c, vertex_t* v) { + perspective(c, v, c->rasterizer.state.enables); +} + +void ogles_vertex_perspective2D(ogles_context_t* c, vertex_t* v) +{ + // here we assume w=1.0 and the viewport transformation + // has been applied already. + c->arrays.cull = 0; + v->window.x = TRI_FROM_FIXED(v->clip.x); + v->window.y = TRI_FROM_FIXED(v->clip.y); + v->window.z = v->clip.z; + v->window.w = v->clip.w << 12; +} + +void ogles_vertex_perspective3DZ(ogles_context_t* c, vertex_t* v) { + clipFrustumPerspective(c, v, GGL_ENABLE_DEPTH_TEST); +} +void ogles_vertex_perspective3D(ogles_context_t* c, vertex_t* v) { + clipFrustumPerspective(c, v, 0); +} +void ogles_vertex_clipAllPerspective3DZ(ogles_context_t* c, vertex_t* v) { + clipAllPerspective(c, v, GGL_ENABLE_DEPTH_TEST); +} +void ogles_vertex_clipAllPerspective3D(ogles_context_t* c, vertex_t* v) { + clipAllPerspective(c, v, 0); +} + +static void clipPlanex(GLenum plane, const GLfixed* equ, ogles_context_t* c) +{ + const int p = plane - GL_CLIP_PLANE0; + if (ggl_unlikely(uint32_t(p) > (GL_CLIP_PLANE5 - GL_CLIP_PLANE0))) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + vec4_t& equation = c->clipPlanes.plane[p].equation; + memcpy(equation.v, equ, sizeof(vec4_t)); + + ogles_validate_transform(c, transform_state_t::MVIT); + transform_t& mvit = c->transforms.mvit4; + mvit.point4(&mvit, &equation, &equation); +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + + +void glColor4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a) +{ + ogles_context_t* c = ogles_context_t::get(); + c->current.color.r = gglFloatToFixed(r); + c->currentColorClamped.r = gglClampx(c->current.color.r); + c->current.color.g = gglFloatToFixed(g); + c->currentColorClamped.g = gglClampx(c->current.color.g); + c->current.color.b = gglFloatToFixed(b); + c->currentColorClamped.b = gglClampx(c->current.color.b); + c->current.color.a = gglFloatToFixed(a); + c->currentColorClamped.a = gglClampx(c->current.color.a); +} + +void glColor4x(GLfixed r, GLfixed g, GLfixed b, GLfixed a) +{ + ogles_context_t* c = ogles_context_t::get(); + c->current.color.r = r; + c->current.color.g = g; + c->current.color.b = b; + c->current.color.a = a; + c->currentColorClamped.r = gglClampx(r); + c->currentColorClamped.g = gglClampx(g); + c->currentColorClamped.b = gglClampx(b); + c->currentColorClamped.a = gglClampx(a); +} + +void glNormal3f(GLfloat x, GLfloat y, GLfloat z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->currentNormal.x = gglFloatToFixed(x); + c->currentNormal.y = gglFloatToFixed(y); + c->currentNormal.z = gglFloatToFixed(z); +} + +void glNormal3x(GLfixed x, GLfixed y, GLfixed z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->currentNormal.x = x; + c->currentNormal.y = y; + c->currentNormal.z = z; +} + +// ---------------------------------------------------------------------------- + +void glClipPlanef(GLenum plane, const GLfloat* equ) +{ + const GLfixed equx[4] = { + gglFloatToFixed(equ[0]), + gglFloatToFixed(equ[1]), + gglFloatToFixed(equ[2]), + gglFloatToFixed(equ[3]) + }; + ogles_context_t* c = ogles_context_t::get(); + clipPlanex(plane, equx, c); +} + +void glClipPlanex(GLenum plane, const GLfixed* equ) +{ + ogles_context_t* c = ogles_context_t::get(); + clipPlanex(plane, equ, c); +} diff --git a/opengl/libagl/vertex.h b/opengl/libagl/vertex.h new file mode 100644 index 000000000..55e62137c --- /dev/null +++ b/opengl/libagl/vertex.h @@ -0,0 +1,48 @@ +/* libs/opengles/vertex.h +** +** Copyright 2006, 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. +*/ + +#ifndef ANDROID_OPENGLES_VERTEX_H +#define ANDROID_OPENGLES_VERTEX_H + +#include +#include +#include + +namespace android { + +namespace gl { +struct vertex_t; +struct ogles_context_t; +}; + +void ogles_init_vertex(ogles_context_t* c); +void ogles_uninit_vertex(ogles_context_t* c); + +void ogles_vertex_perspective2D(ogles_context_t*, vertex_t*); + +void ogles_vertex_perspective3D(ogles_context_t*, vertex_t*); +void ogles_vertex_perspective3DZ(ogles_context_t*, vertex_t*); +void ogles_vertex_clipAllPerspective3D(ogles_context_t*, vertex_t*); +void ogles_vertex_clipAllPerspective3DZ(ogles_context_t*, vertex_t*); + + +void ogles_vertex_project(ogles_context_t* c, vertex_t*); + +}; // namespace android + +#endif // ANDROID_OPENGLES_VERTEX_H + diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk new file mode 100644 index 000000000..2ecc7768c --- /dev/null +++ b/opengl/libs/Android.mk @@ -0,0 +1,53 @@ +LOCAL_PATH:= $(call my-dir) + +# +# Build META EGL library +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + EGL/egl.cpp \ + EGL/gpu.cpp \ +# + +LOCAL_SHARED_LIBRARIES += libcutils libutils libui +LOCAL_LDLIBS := -lpthread -ldl +LOCAL_MODULE:= libEGL + +# needed on sim build because of weird logging issues +ifeq ($(TARGET_SIMULATOR),true) +else + LOCAL_SHARED_LIBRARIES += libdl + # we need to access the Bionic private header + LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private +endif + +include $(BUILD_SHARED_LIBRARY) + + + +# +# Build the wrapper OpenGL ES library +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + GLES_CM/gl.cpp.arm \ + GLES_CM/gl_logger.cpp \ +# + +LOCAL_SHARED_LIBRARIES += libcutils libutils libui libEGL +LOCAL_LDLIBS := -lpthread -ldl +LOCAL_MODULE:= libGLESv1_CM + +# needed on sim build because of weird logging issues +ifeq ($(TARGET_SIMULATOR),true) +else + LOCAL_SHARED_LIBRARIES += libdl + # we need to access the Bionic private header + LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private +endif + +include $(BUILD_SHARED_LIBRARY) diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp new file mode 100644 index 000000000..687c8bc1f --- /dev/null +++ b/opengl/libs/EGL/egl.cpp @@ -0,0 +1,1363 @@ +/* + ** Copyright 2007, 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. + */ + +#define LOG_TAG "GLLogger" + +#include +#include +#include +#include + +#include + +#if HAVE_ANDROID_OS +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "hooks.h" +#include "egl_impl.h" + + +#define MAKE_CONFIG(_impl, _index) ((EGLConfig)(((_impl)<<24) | (_index))) +#define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r) + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 4 +static char const * const gVendorString = "Android"; +static char const * const gVersionString = "1.31 Android META-EGL"; +static char const * const gClientApiString = "OpenGL ES"; +static char const * const gExtensionString = ""; + +template +struct egl_object_t +{ + egl_object_t() : magic(MAGIC) { } + ~egl_object_t() { magic = 0; } + bool isValid() const { return magic == MAGIC; } +private: + uint32_t magic; +}; + +struct egl_display_t : public egl_object_t<'_dpy'> +{ + EGLDisplay dpys[2]; + EGLConfig* configs[2]; + EGLint numConfigs[2]; + EGLint numTotalConfigs; + char const* extensionsString; + volatile int32_t refs; + struct strings_t { + char const * vendor; + char const * version; + char const * clientApi; + char const * extensions; + }; + strings_t queryString[2]; +}; + +struct egl_surface_t : public egl_object_t<'_srf'> +{ + egl_surface_t(EGLDisplay dpy, EGLSurface surface, + NativeWindowType window, int impl, egl_connection_t const* cnx) + : dpy(dpy), surface(surface), window(window), impl(impl), cnx(cnx) + { + // NOTE: window must be incRef'ed and connected already + } + ~egl_surface_t() { + if (window) { + if (window->disconnect) + window->disconnect(window); + window->decRef(window); + } + } + EGLDisplay dpy; + EGLSurface surface; + NativeWindowType window; + int impl; + egl_connection_t const* cnx; +}; + +struct egl_context_t : public egl_object_t<'_ctx'> +{ + egl_context_t(EGLDisplay dpy, EGLContext context, + int impl, egl_connection_t const* cnx) + : dpy(dpy), context(context), read(0), draw(0), impl(impl), cnx(cnx) + { + } + EGLDisplay dpy; + EGLContext context; + EGLSurface read; + EGLSurface draw; + int impl; + egl_connection_t const* cnx; +}; + +struct tls_t +{ + tls_t() : error(EGL_SUCCESS), ctx(0) { } + EGLint error; + EGLContext ctx; +}; + +static void gl_unimplemented() { + LOGE("called unimplemented OpenGL ES API"); +} + +// ---------------------------------------------------------------------------- +// GL / EGL hooks +// ---------------------------------------------------------------------------- + +#undef GL_ENTRY +#undef EGL_ENTRY +#define GL_ENTRY(_r, _api, ...) #_api, +#define EGL_ENTRY(_r, _api, ...) #_api, + +static char const * const gl_names[] = { + #include "gl_entries.in" + NULL +}; + +static char const * const egl_names[] = { + #include "egl_entries.in" + NULL +}; + +#undef GL_ENTRY +#undef EGL_ENTRY + +// ---------------------------------------------------------------------------- + +egl_connection_t gEGLImpl[2]; +static egl_display_t gDisplay[NUM_DISPLAYS]; +static pthread_mutex_t gThreadLocalStorageKeyMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_key_t gEGLThreadLocalStorageKey = -1; + +// ---------------------------------------------------------------------------- + +gl_hooks_t gHooks[IMPL_NUM_IMPLEMENTATIONS]; +pthread_key_t gGLWrapperKey = -1; + +// ---------------------------------------------------------------------------- + +static __attribute__((noinline)) +const char *egl_strerror(EGLint err) +{ + switch (err){ + case EGL_SUCCESS: return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; + case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; + case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; + case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; + case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; + case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; + case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; + case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; + default: return "UNKNOWN"; + } +} + +static __attribute__((noinline)) +void clearTLS() { + if (gEGLThreadLocalStorageKey != -1) { + tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey); + if (tls) { + delete tls; + pthread_setspecific(gEGLThreadLocalStorageKey, 0); + } + } +} + +static tls_t* getTLS() +{ + tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey); + if (tls == 0) { + tls = new tls_t; + pthread_setspecific(gEGLThreadLocalStorageKey, tls); + } + return tls; +} + +template +static __attribute__((noinline)) +T setErrorEtc(const char* caller, int line, EGLint error, T returnValue) { + if (gEGLThreadLocalStorageKey == -1) { + pthread_mutex_lock(&gThreadLocalStorageKeyMutex); + if (gEGLThreadLocalStorageKey == -1) + pthread_key_create(&gEGLThreadLocalStorageKey, NULL); + pthread_mutex_unlock(&gThreadLocalStorageKeyMutex); + } + tls_t* tls = getTLS(); + if (tls->error != error) { + LOGE("%s:%d error %x (%s)", caller, line, error, egl_strerror(error)); + tls->error = error; + } + return returnValue; +} + +static __attribute__((noinline)) +GLint getError() { + if (gEGLThreadLocalStorageKey == -1) + return EGL_SUCCESS; + tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey); + if (!tls) return EGL_SUCCESS; + GLint error = tls->error; + tls->error = EGL_SUCCESS; + return error; +} + +static __attribute__((noinline)) +void setContext(EGLContext ctx) { + if (gEGLThreadLocalStorageKey == -1) { + pthread_mutex_lock(&gThreadLocalStorageKeyMutex); + if (gEGLThreadLocalStorageKey == -1) + pthread_key_create(&gEGLThreadLocalStorageKey, NULL); + pthread_mutex_unlock(&gThreadLocalStorageKeyMutex); + } + tls_t* tls = getTLS(); + tls->ctx = ctx; +} + +static __attribute__((noinline)) +EGLContext getContext() { + if (gEGLThreadLocalStorageKey == -1) + return EGL_NO_CONTEXT; + tls_t* tls = (tls_t*)pthread_getspecific(gEGLThreadLocalStorageKey); + if (!tls) return EGL_NO_CONTEXT; + return tls->ctx; +} + + +/*****************************************************************************/ + +class ISurfaceComposer; +const sp& getSurfaceFlinger(); +request_gpu_t* gpu_acquire(void* user); +int gpu_release(void*, request_gpu_t* gpu); + +static __attribute__((noinline)) +void *load_driver(const char* driver, gl_hooks_t* hooks) +{ + void* dso = dlopen(driver, RTLD_NOW | RTLD_LOCAL); + LOGE_IF(!dso, + "couldn't load <%s> library (%s)", + driver, dlerror()); + + if (dso) { + void** curr; + char const * const * api; + gl_hooks_t::gl_t* gl = &hooks->gl; + curr = (void**)gl; + api = gl_names; + while (*api) { + void* f = dlsym(dso, *api); + //LOGD("<%s> @ 0x%p", *api, f); + if (f == NULL) { + //LOGW("<%s> not found in %s", *api, driver); + f = (void*)gl_unimplemented; + } + *curr++ = f; + api++; + } + gl_hooks_t::egl_t* egl = &hooks->egl; + curr = (void**)egl; + api = egl_names; + while (*api) { + void* f = dlsym(dso, *api); + if (f == NULL) { + //LOGW("<%s> not found in %s", *api, driver); + f = (void*)0; + } + *curr++ = f; + api++; + } + + // hook this driver up with surfaceflinger if needed + register_gpu_t register_gpu = + (register_gpu_t)dlsym(dso, "oem_register_gpu"); + + if (register_gpu != NULL) { + if (getSurfaceFlinger() != 0) { + register_gpu(dso, gpu_acquire, gpu_release); + } + } + } + return dso; +} + +template +static __attribute__((noinline)) +int binarySearch( + T const sortedArray[], int first, int last, T key) +{ + while (first <= last) { + int mid = (first + last) / 2; + if (key > sortedArray[mid]) { + first = mid + 1; + } else if (key < sortedArray[mid]) { + last = mid - 1; + } else { + return mid; + } + } + return -1; +} + +static EGLint configToUniqueId(egl_display_t const* dp, int i, int index) +{ + // NOTE: this mapping works only if we have no more than two EGLimpl + return (i>0 ? dp->numConfigs[0] : 0) + index; +} + +static void uniqueIdToConfig(egl_display_t const* dp, EGLint configId, + int& i, int& index) +{ + // NOTE: this mapping works only if we have no more than two EGLimpl + size_t numConfigs = dp->numConfigs[0]; + i = configId / numConfigs; + index = configId % numConfigs; +} + +static int cmp_configs(const void* a, const void *b) +{ + EGLConfig c0 = *(EGLConfig const *)a; + EGLConfig c1 = *(EGLConfig const *)b; + return c0c1 ? 1 : 0); +} + +struct extention_map_t { + const char* name; + __eglMustCastToProperFunctionPointerType address; +}; + +static const extention_map_t gExtentionMap[] = { +}; + +static extention_map_t gGLExtentionMap[MAX_NUMBER_OF_GL_EXTENSIONS]; + +static void(*findProcAddress(const char* name, + const extention_map_t* map, size_t n))() +{ + for (uint32_t i=0 ; i= NUM_DISPLAYS) ? NULL : &gDisplay[index]; +} + +static inline +egl_surface_t* get_surface(EGLSurface surface) +{ + egl_surface_t* s = (egl_surface_t *)surface; + return s; +} + +static inline +egl_context_t* get_context(EGLContext context) +{ + egl_context_t* c = (egl_context_t *)context; + return c; +} + +static egl_connection_t* validate_display_config( + EGLDisplay dpy, EGLConfig config, + egl_display_t const*& dp, int& impl, int& index) +{ + dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (egl_connection_t*)NULL); + + impl = uintptr_t(config)>>24; + if (uint32_t(impl) >= 2) { + return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL); + } + index = uintptr_t(config) & 0xFFFFFF; + if (index >= dp->numConfigs[impl]) { + return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL); + } + egl_connection_t* const cnx = &gEGLImpl[impl]; + if (cnx->dso == 0) { + return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL); + } + return cnx; +} + +static EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx) +{ + if ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + if (!get_display(dpy)->isValid()) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + if (!ctx) // TODO: make sure context is a valid object + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + if (!get_context(ctx)->isValid()) + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + return EGL_TRUE; +} + +static EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface) +{ + if ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + if (!get_display(dpy)->isValid()) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + if (!surface) // TODO: make sure surface is a valid object + return setError(EGL_BAD_SURFACE, EGL_FALSE); + if (!get_surface(surface)->isValid()) + return setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_TRUE; +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + +EGLDisplay eglGetDisplay(NativeDisplayType display) +{ + if (sEarlyInitState) { + return EGL_NO_DISPLAY; + } + + uint32_t index = uint32_t(display); + if (index >= NUM_DISPLAYS) { + return EGL_NO_DISPLAY; + } + + EGLDisplay dpy = EGLDisplay(uintptr_t(display) + 1LU); + egl_display_t* d = &gDisplay[index]; + + // dynamically load all our EGL implementations for that display + // and call into the real eglGetGisplay() + egl_connection_t* cnx = &gEGLImpl[IMPL_SOFTWARE]; + if (cnx->dso == 0) { + cnx->hooks = &gHooks[IMPL_SOFTWARE]; + cnx->dso = load_driver("libagl.so", cnx->hooks); + } + if (cnx->dso && d->dpys[IMPL_SOFTWARE]==EGL_NO_DISPLAY) { + d->dpys[IMPL_SOFTWARE] = cnx->hooks->egl.eglGetDisplay(display); + LOGE_IF(d->dpys[IMPL_SOFTWARE]==EGL_NO_DISPLAY, + "No EGLDisplay for software EGL!"); + } + + cnx = &gEGLImpl[IMPL_HARDWARE]; + if (cnx->dso == 0 && cnx->unavailable == 0) { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.egl.hw", value, "1"); + if (atoi(value) != 0) { + cnx->hooks = &gHooks[IMPL_HARDWARE]; + cnx->dso = load_driver("libhgl.so", cnx->hooks); + } else { + LOGD("3D hardware acceleration is disabled"); + } + } + if (cnx->dso && d->dpys[IMPL_HARDWARE]==EGL_NO_DISPLAY) { + android_memset32( + (uint32_t*)(void*)&gHooks[IMPL_CONTEXT_LOST].gl, + (uint32_t)((void*)gl_context_lost), + sizeof(gHooks[IMPL_CONTEXT_LOST].gl)); + android_memset32( + (uint32_t*)(void*)&gHooks[IMPL_CONTEXT_LOST].egl, + (uint32_t)((void*)egl_context_lost), + sizeof(gHooks[IMPL_CONTEXT_LOST].egl)); + android_memset32( + (uint32_t*)(void*)&gHooks[IMPL_CONTEXT_LOST].ext, + (uint32_t)((void*)ext_context_lost), + sizeof(gHooks[IMPL_CONTEXT_LOST].ext)); + + gHooks[IMPL_CONTEXT_LOST].egl.eglSwapBuffers = + egl_context_lost_swap_buffers; + + gHooks[IMPL_CONTEXT_LOST].egl.eglGetError = + egl_context_lost_get_error; + + gHooks[IMPL_CONTEXT_LOST].egl.eglTerminate = + gHooks[IMPL_HARDWARE].egl.eglTerminate; + + d->dpys[IMPL_HARDWARE] = cnx->hooks->egl.eglGetDisplay(display); + if (d->dpys[IMPL_HARDWARE] == EGL_NO_DISPLAY) { + LOGE("h/w accelerated eglGetDisplay() failed (%s)", + egl_strerror(cnx->hooks->egl.eglGetError())); + dlclose((void*)cnx->dso); + cnx->dso = 0; + // in case of failure, we want to make sure we don't try again + // as it's expensive. + cnx->unavailable = 1; + } + } + + return dpy; +} + +// ---------------------------------------------------------------------------- +// Initialization +// ---------------------------------------------------------------------------- + +EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) +{ + egl_display_t * const dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + if (android_atomic_inc(&dp->refs) > 0) { + if (major != NULL) *major = VERSION_MAJOR; + if (minor != NULL) *minor = VERSION_MINOR; + return EGL_TRUE; + } + + setGlThreadSpecific(&gHooks[IMPL_NO_CONTEXT]); + + // initialize each EGL and + // build our own extension string first, based on the extension we know + // and the extension supported by our client implementation + dp->extensionsString = strdup(gExtensionString); + for (int i=0 ; i<2 ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + cnx->major = -1; + cnx->minor = -1; + if (!cnx->dso) + continue; + + if (cnx->hooks->egl.eglInitialize( + dp->dpys[i], &cnx->major, &cnx->minor)) { + + //LOGD("initialized %d dpy=%p, ver=%d.%d, cnx=%p", + // i, dp->dpys[i], cnx->major, cnx->minor, cnx); + + // get the query-strings for this display for each implementation + dp->queryString[i].vendor = + cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_VENDOR); + dp->queryString[i].version = + cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_VERSION); + dp->queryString[i].extensions = strdup( + cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_EXTENSIONS)); + dp->queryString[i].clientApi = + cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_CLIENT_APIS); + + } else { + LOGD("%d: eglInitialize() failed (%s)", + i, egl_strerror(cnx->hooks->egl.eglGetError())); + } + } + + EGLBoolean res = EGL_FALSE; + for (int i=0 ; i<2 ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso && cnx->major>=0 && cnx->minor>=0) { + EGLint n; + if (cnx->hooks->egl.eglGetConfigs(dp->dpys[i], 0, 0, &n)) { + dp->configs[i] = (EGLConfig*)malloc(sizeof(EGLConfig)*n); + if (dp->configs[i]) { + if (cnx->hooks->egl.eglGetConfigs( + dp->dpys[i], dp->configs[i], n, &dp->numConfigs[i])) + { + // sort the configurations so we can do binary searches + qsort( dp->configs[i], + dp->numConfigs[i], + sizeof(EGLConfig), cmp_configs); + + dp->numTotalConfigs += n; + res = EGL_TRUE; + } + } + } + } + } + + if (res == EGL_TRUE) { + if (major != NULL) *major = VERSION_MAJOR; + if (minor != NULL) *minor = VERSION_MINOR; + return EGL_TRUE; + } + return setError(EGL_NOT_INITIALIZED, EGL_FALSE); +} + +EGLBoolean eglTerminate(EGLDisplay dpy) +{ + egl_display_t* const dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); + if (android_atomic_dec(&dp->refs) != 1) + return EGL_TRUE; + + EGLBoolean res = EGL_FALSE; + for (int i=0 ; i<2 ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso) { + cnx->hooks->egl.eglTerminate(dp->dpys[i]); + + /* REVISIT: it's unclear what to do if eglTerminate() fails, + * on one end we shouldn't care, on the other end if it fails + * it might not be safe to call dlclose() (there could be some + * threads around). */ + + free(dp->configs[i]); + free((void*)dp->queryString[i].extensions); + dp->numConfigs[i] = 0; + dp->dpys[i] = EGL_NO_DISPLAY; + dlclose((void*)cnx->dso); + cnx->dso = 0; + res = EGL_TRUE; + } + } + free((void*)dp->extensionsString); + dp->extensionsString = 0; + dp->numTotalConfigs = 0; + clearTLS(); + return res; +} + +// ---------------------------------------------------------------------------- +// configuration +// ---------------------------------------------------------------------------- + +EGLBoolean eglGetConfigs( EGLDisplay dpy, + EGLConfig *configs, + EGLint config_size, EGLint *num_config) +{ + egl_display_t const * const dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + GLint numConfigs = dp->numTotalConfigs; + if (!configs) { + *num_config = numConfigs; + return EGL_TRUE; + } + GLint n = 0; + for (int j=0 ; j<2 ; j++) { + for (int i=0 ; inumConfigs[j] && config_size ; i++) { + *configs++ = MAKE_CONFIG(j, i); + config_size--; + n++; + } + } + + *num_config = n; + return EGL_TRUE; +} + +EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, + EGLConfig *configs, EGLint config_size, + EGLint *num_config) +{ + egl_display_t const * const dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + if (configs == 0) { + *num_config = 0; + return EGL_TRUE; + } + + EGLint n; + EGLBoolean res = EGL_FALSE; + *num_config = 0; + + + // It is unfortunate, but we need to remap the EGL_CONFIG_IDs, + // to do this, we have to go through the attrib_list array once + // to figure out both its size and if it contains an EGL_CONFIG_ID + // key. If so, the full array is copied and patched. + // NOTE: we assume that there can be only one occurrence + // of EGL_CONFIG_ID. + + EGLint patch_index = -1; + GLint attr; + size_t size = 0; + while ((attr=attrib_list[size])) { + if (attr == EGL_CONFIG_ID) + patch_index = size; + size += 2; + } + if (patch_index >= 0) { + size += 2; // we need copy the sentinel as well + EGLint* new_list = (EGLint*)malloc(size*sizeof(EGLint)); + if (new_list == 0) + return setError(EGL_BAD_ALLOC, EGL_FALSE); + memcpy(new_list, attrib_list, size*sizeof(EGLint)); + + // patch the requested EGL_CONFIG_ID + int i, index; + EGLint& configId(new_list[patch_index+1]); + uniqueIdToConfig(dp, configId, i, index); + + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso) { + cnx->hooks->egl.eglGetConfigAttrib( + dp->dpys[i], dp->configs[i][index], + EGL_CONFIG_ID, &configId); + + // and switch to the new list + attrib_list = const_cast(new_list); + + // At this point, the only configuration that can match is + // dp->configs[i][index], however, we don't know if it would be + // rejected because of the other attributes, so we do have to call + // cnx->hooks->egl.eglChooseConfig() -- but we don't have to loop + // through all the EGLimpl[]. + // We also know we can only get a single config back, and we know + // which one. + + res = cnx->hooks->egl.eglChooseConfig( + dp->dpys[i], attrib_list, configs, config_size, &n); + if (res && n>0) { + // n has to be 0 or 1, by construction, and we already know + // which config it will return (since there can be only one). + configs[0] = MAKE_CONFIG(i, index); + *num_config = 1; + } + } + + free(const_cast(attrib_list)); + return res; + } + + for (int i=0 ; i<2 ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso) { + if (cnx->hooks->egl.eglChooseConfig( + dp->dpys[i], attrib_list, configs, config_size, &n)) { + // now we need to convert these client EGLConfig to our + // internal EGLConfig format. This is done in O(n log n). + for (int j=0 ; j( + dp->configs[i], 0, dp->numConfigs[i]-1, configs[j]); + if (index >= 0) { + configs[j] = MAKE_CONFIG(i, index); + } else { + return setError(EGL_BAD_CONFIG, EGL_FALSE); + } + } + configs += n; + config_size -= n; + *num_config += n; + res = EGL_TRUE; + } + } + } + return res; +} + +EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, + EGLint attribute, EGLint *value) +{ + egl_display_t const* dp = 0; + int i=0, index=0; + egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + if (!cnx) return EGL_FALSE; + + if (attribute == EGL_CONFIG_ID) { + // EGL_CONFIG_IDs must be unique, just use the order of the selected + // EGLConfig. + *value = configToUniqueId(dp, i, index); + return EGL_TRUE; + } + return cnx->hooks->egl.eglGetConfigAttrib( + dp->dpys[i], dp->configs[i][index], attribute, value); +} + +// ---------------------------------------------------------------------------- +// surfaces +// ---------------------------------------------------------------------------- + +EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, + NativeWindowType window, + const EGLint *attrib_list) +{ + egl_display_t const* dp = 0; + int i=0, index=0; + egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + if (cnx) { + // window must be connected upon calling underlying + // eglCreateWindowSurface + if (window) { + window->incRef(window); + if (window->connect) + window->connect(window); + } + + EGLSurface surface = cnx->hooks->egl.eglCreateWindowSurface( + dp->dpys[i], dp->configs[i][index], window, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dpy, surface, window, i, cnx); + return s; + } + + // something went wrong, disconnect and free window + // (will disconnect() automatically) + if (window) { + window->decRef(window); + } + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, + NativePixmapType pixmap, + const EGLint *attrib_list) +{ + egl_display_t const* dp = 0; + int i=0, index=0; + egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + if (cnx) { + EGLSurface surface = cnx->hooks->egl.eglCreatePixmapSurface( + dp->dpys[i], dp->configs[i][index], pixmap, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dpy, surface, NULL, i, cnx); + return s; + } + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, + const EGLint *attrib_list) +{ + egl_display_t const* dp = 0; + int i=0, index=0; + egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + if (cnx) { + EGLSurface surface = cnx->hooks->egl.eglCreatePbufferSurface( + dp->dpys[i], dp->configs[i][index], attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dpy, surface, NULL, i, cnx); + return s; + } + } + return EGL_NO_SURFACE; +} + +EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) +{ + if (!validate_display_surface(dpy, surface)) + return EGL_FALSE; + egl_display_t const * const dp = get_display(dpy); + egl_surface_t const * const s = get_surface(surface); + + EGLBoolean result = s->cnx->hooks->egl.eglDestroySurface( + dp->dpys[s->impl], s->surface); + + delete s; + return result; +} + +EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface, + EGLint attribute, EGLint *value) +{ + if (!validate_display_surface(dpy, surface)) + return EGL_FALSE; + egl_display_t const * const dp = get_display(dpy); + egl_surface_t const * const s = get_surface(surface); + + return s->cnx->hooks->egl.eglQuerySurface( + dp->dpys[s->impl], s->surface, attribute, value); +} + +// ---------------------------------------------------------------------------- +// contextes +// ---------------------------------------------------------------------------- + +EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, + EGLContext share_list, const EGLint *attrib_list) +{ + egl_display_t const* dp = 0; + int i=0, index=0; + egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + if (cnx) { + EGLContext context = cnx->hooks->egl.eglCreateContext( + dp->dpys[i], dp->configs[i][index], share_list, attrib_list); + if (context != EGL_NO_CONTEXT) { + egl_context_t* c = new egl_context_t(dpy, context, i, cnx); + return c; + } + } + return EGL_NO_CONTEXT; +} + +EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) +{ + if (!validate_display_context(dpy, ctx)) + return EGL_FALSE; + egl_display_t const * const dp = get_display(dpy); + egl_context_t * const c = get_context(ctx); + EGLBoolean result = c->cnx->hooks->egl.eglDestroyContext( + dp->dpys[c->impl], c->context); + delete c; + return result; +} + +EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, + EGLSurface read, EGLContext ctx) +{ + egl_display_t const * const dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + if (read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE && + ctx == EGL_NO_CONTEXT) + { + EGLBoolean result = EGL_TRUE; + ctx = getContext(); + if (ctx) { + egl_context_t * const c = get_context(ctx); + result = c->cnx->hooks->egl.eglMakeCurrent(dp->dpys[c->impl], 0, 0, 0); + if (result == EGL_TRUE) { + setGlThreadSpecific(&gHooks[IMPL_NO_CONTEXT]); + setContext(EGL_NO_CONTEXT); + } + } + return result; + } + + if (!validate_display_context(dpy, ctx)) + return EGL_FALSE; + + egl_context_t * const c = get_context(ctx); + if (draw != EGL_NO_SURFACE) { + egl_surface_t const * d = get_surface(draw); + if (!d) return setError(EGL_BAD_SURFACE, EGL_FALSE); + if (d->impl != c->impl) + return setError(EGL_BAD_MATCH, EGL_FALSE); + draw = d->surface; + } + if (read != EGL_NO_SURFACE) { + egl_surface_t const * r = get_surface(read); + if (!r) return setError(EGL_BAD_SURFACE, EGL_FALSE); + if (r->impl != c->impl) + return setError(EGL_BAD_MATCH, EGL_FALSE); + read = r->surface; + } + EGLBoolean result = c->cnx->hooks->egl.eglMakeCurrent( + dp->dpys[c->impl], draw, read, c->context); + + if (result == EGL_TRUE) { + setGlThreadSpecific(c->cnx->hooks); + setContext(ctx); + c->read = read; + c->draw = draw; + } + return result; +} + + +EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, + EGLint attribute, EGLint *value) +{ + if (!validate_display_context(dpy, ctx)) + return EGL_FALSE; + + egl_display_t const * const dp = get_display(dpy); + egl_context_t * const c = get_context(ctx); + + return c->cnx->hooks->egl.eglQueryContext( + dp->dpys[c->impl], c->context, attribute, value); +} + +EGLContext eglGetCurrentContext(void) +{ + EGLContext ctx = getContext(); + return ctx; +} + +EGLSurface eglGetCurrentSurface(EGLint readdraw) +{ + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); + switch (readdraw) { + case EGL_READ: return c->read; + case EGL_DRAW: return c->draw; + default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + } + } + return EGL_NO_SURFACE; +} + +EGLDisplay eglGetCurrentDisplay(void) +{ + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); + return c->dpy; + } + return EGL_NO_DISPLAY; +} + +EGLBoolean eglWaitGL(void) +{ + EGLBoolean res = EGL_TRUE; + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE); + if (uint32_t(c->impl)>=2) + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl[c->impl]; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + res = cnx->hooks->egl.eglWaitGL(); + } + return res; +} + +EGLBoolean eglWaitNative(EGLint engine) +{ + EGLBoolean res = EGL_TRUE; + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE); + if (uint32_t(c->impl)>=2) + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl[c->impl]; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + res = cnx->hooks->egl.eglWaitNative(engine); + } + return res; +} + +EGLint eglGetError(void) +{ + EGLint result = EGL_SUCCESS; + for (int i=0 ; i<2 ; i++) { + EGLint err = EGL_SUCCESS; + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso) + err = cnx->hooks->egl.eglGetError(); + if (err!=EGL_SUCCESS && result==EGL_SUCCESS) + result = err; + } + if (result == EGL_SUCCESS) + result = getError(); + return result; +} + +void (*eglGetProcAddress(const char *procname))() +{ + __eglMustCastToProperFunctionPointerType addr; + addr = findProcAddress(procname, gExtentionMap, NELEM(gExtentionMap)); + if (addr) return addr; + + return NULL; // TODO: finish implementation below + + addr = findProcAddress(procname, gGLExtentionMap, NELEM(gGLExtentionMap)); + if (addr) return addr; + + addr = 0; + int slot = -1; + for (int i=0 ; i<2 ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso) { + if (cnx->hooks->egl.eglGetProcAddress) { + addr = cnx->hooks->egl.eglGetProcAddress(procname); + if (addr) { + if (slot == -1) { + slot = 0; // XXX: find free slot + if (slot == -1) { + addr = 0; + break; + } + } + cnx->hooks->ext.extensions[slot] = addr; + } + } + } + } + + if (slot >= 0) { + addr = 0; // XXX: address of stub 'slot' + gGLExtentionMap[slot].name = strdup(procname); + gGLExtentionMap[slot].address = addr; + } + + return addr; + + + /* + * TODO: For OpenGL ES extensions, we must generate a stub + * that looks like + * mov r12, #0xFFFF0FFF + * ldr r12, [r12, #-15] + * ldr r12, [r12, #TLS_SLOT_OPENGL_API*4] + * mov r12, [r12, #api_offset] + * ldrne pc, r12 + * mov pc, #unsupported_extension + * + * and write the address of the extension in *all* + * gl_hooks_t::gl_ext_t at offset "api_offset" from gl_hooks_t + * + */ +} + +EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) +{ + if (!validate_display_surface(dpy, draw)) + return EGL_FALSE; + egl_display_t const * const dp = get_display(dpy); + egl_surface_t const * const s = get_surface(draw); + return s->cnx->hooks->egl.eglSwapBuffers(dp->dpys[s->impl], s->surface); +} + +EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, + NativePixmapType target) +{ + if (!validate_display_surface(dpy, surface)) + return EGL_FALSE; + egl_display_t const * const dp = get_display(dpy); + egl_surface_t const * const s = get_surface(surface); + return s->cnx->hooks->egl.eglCopyBuffers( + dp->dpys[s->impl], s->surface, target); +} + +const char* eglQueryString(EGLDisplay dpy, EGLint name) +{ + egl_display_t const * const dp = get_display(dpy); + switch (name) { + case EGL_VENDOR: + return gVendorString; + case EGL_VERSION: + return gVersionString; + case EGL_EXTENSIONS: + return gExtensionString; + case EGL_CLIENT_APIS: + return gClientApiString; + } + return setError(EGL_BAD_PARAMETER, (const char *)0); +} + + +// ---------------------------------------------------------------------------- +// EGL 1.1 +// ---------------------------------------------------------------------------- + +EGLBoolean eglSurfaceAttrib( + EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) +{ + if (!validate_display_surface(dpy, surface)) + return EGL_FALSE; + egl_display_t const * const dp = get_display(dpy); + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->hooks->egl.eglSurfaceAttrib) { + return s->cnx->hooks->egl.eglSurfaceAttrib( + dp->dpys[s->impl], s->surface, attribute, value); + } + return setError(EGL_BAD_SURFACE, EGL_FALSE); +} + +EGLBoolean eglBindTexImage( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + if (!validate_display_surface(dpy, surface)) + return EGL_FALSE; + egl_display_t const * const dp = get_display(dpy); + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->hooks->egl.eglBindTexImage) { + return s->cnx->hooks->egl.eglBindTexImage( + dp->dpys[s->impl], s->surface, buffer); + } + return setError(EGL_BAD_SURFACE, EGL_FALSE); +} + +EGLBoolean eglReleaseTexImage( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + if (!validate_display_surface(dpy, surface)) + return EGL_FALSE; + egl_display_t const * const dp = get_display(dpy); + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->hooks->egl.eglReleaseTexImage) { + return s->cnx->hooks->egl.eglReleaseTexImage( + dp->dpys[s->impl], s->surface, buffer); + } + return setError(EGL_BAD_SURFACE, EGL_FALSE); +} + +EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) +{ + egl_display_t * const dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + EGLBoolean res = EGL_TRUE; + for (int i=0 ; i<2 ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso) { + if (cnx->hooks->egl.eglSwapInterval) { + if (cnx->hooks->egl.eglSwapInterval(dp->dpys[i], interval) == EGL_FALSE) { + res = EGL_FALSE; + } + } + } + } + return res; +} + + +// ---------------------------------------------------------------------------- +// EGL 1.2 +// ---------------------------------------------------------------------------- + +EGLBoolean eglWaitClient(void) +{ + EGLBoolean res = EGL_TRUE; + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE); + if (uint32_t(c->impl)>=2) + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl[c->impl]; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + if (cnx->hooks->egl.eglWaitClient) { + res = cnx->hooks->egl.eglWaitClient(); + } else { + res = cnx->hooks->egl.eglWaitGL(); + } + } + return res; +} + +EGLBoolean eglBindAPI(EGLenum api) +{ + // bind this API on all EGLs + EGLBoolean res = EGL_TRUE; + for (int i=0 ; i<2 ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso) { + if (cnx->hooks->egl.eglBindAPI) { + if (cnx->hooks->egl.eglBindAPI(api) == EGL_FALSE) { + res = EGL_FALSE; + } + } + } + } + return res; +} + +EGLenum eglQueryAPI(void) +{ + for (int i=0 ; i<2 ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso) { + if (cnx->hooks->egl.eglQueryAPI) { + // the first one we find is okay, because they all + // should be the same + return cnx->hooks->egl.eglQueryAPI(); + } + } + } + // or, it can only be OpenGL ES + return EGL_OPENGL_ES_API; +} + +EGLBoolean eglReleaseThread(void) +{ + for (int i=0 ; i<2 ; i++) { + egl_connection_t* const cnx = &gEGLImpl[i]; + if (cnx->dso) { + if (cnx->hooks->egl.eglReleaseThread) { + cnx->hooks->egl.eglReleaseThread(); + } + } + } + clearTLS(); + return EGL_TRUE; +} + +EGLSurface eglCreatePbufferFromClientBuffer( + EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, + EGLConfig config, const EGLint *attrib_list) +{ + egl_display_t const* dp = 0; + int i=0, index=0; + egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); + if (!cnx) return EGL_FALSE; + if (cnx->hooks->egl.eglCreatePbufferFromClientBuffer) { + return cnx->hooks->egl.eglCreatePbufferFromClientBuffer( + dp->dpys[i], buftype, buffer, dp->configs[i][index], attrib_list); + } + return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); +} diff --git a/opengl/libs/EGL/gpu.cpp b/opengl/libs/EGL/gpu.cpp new file mode 100644 index 000000000..3f9fd6340 --- /dev/null +++ b/opengl/libs/EGL/gpu.cpp @@ -0,0 +1,212 @@ +/* + ** Copyright 2007, 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. + */ + +#define LOG_TAG "EGL" + +#include +#include +#include + +#include + +#if HAVE_ANDROID_OS +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "hooks.h" +#include "egl_impl.h" + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +/* + * we provide our own allocators for the GPU regions, these + * allocators go through surfaceflinger + */ + +static Mutex gRegionsLock; +static request_gpu_t gRegions; +static sp gSurfaceManager; +ISurfaceComposer* GLES_localSurfaceManager = 0; + +extern egl_connection_t gEGLImpl[2]; + +const sp& getSurfaceFlinger() +{ + Mutex::Autolock _l(gRegionsLock); + + /* + * There is a little bit of voodoo magic here. We want to access + * surfaceflinger for allocating GPU regions, however, when we are + * running as part of surfaceflinger, we want to bypass the + * service manager because surfaceflinger might not be registered yet. + * SurfaceFlinger will populate "GLES_localSurfaceManager" with its + * own address, so we can just use that. + */ + if (gSurfaceManager == 0) { + if (GLES_localSurfaceManager) { + // we're running in SurfaceFlinger's context + gSurfaceManager = GLES_localSurfaceManager; + } else { + // we're a remote process or not part of surfaceflinger, + // go through the service manager + sp sm = defaultServiceManager(); + if (sm != NULL) { + sp binder = sm->getService(String16("SurfaceFlinger")); + gSurfaceManager = interface_cast(binder); + } + } + } + return gSurfaceManager; +} + +class GPURevokeRequester : public BnGPUCallback +{ +public: + virtual void gpuLost() { + LOGD("CONTEXT_LOST: Releasing GPU upon request from SurfaceFlinger."); + gEGLImpl[IMPL_HARDWARE].hooks = &gHooks[IMPL_CONTEXT_LOST]; + } +}; + +static sp gRevokerCallback; + + +request_gpu_t* gpu_acquire(void* user) +{ + sp server( getSurfaceFlinger() ); + + Mutex::Autolock _l(gRegionsLock); + if (server == NULL) { + return 0; + } + + ISurfaceComposer::gpu_info_t info; + + if (gRevokerCallback == 0) + gRevokerCallback = new GPURevokeRequester(); + + status_t err = server->requestGPU(gRevokerCallback, &info); + if (err != NO_ERROR) { + LOGD("requestGPU returned %d", err); + return 0; + } + + bool failed = false; + request_gpu_t* gpu = &gRegions; + memset(gpu, 0, sizeof(*gpu)); + + if (info.regs != 0) { + sp heap(info.regs->getMemory()); + if (heap != 0) { + int fd = heap->heapID(); + gpu->regs.fd = fd; + gpu->regs.base = info.regs->pointer(); + gpu->regs.size = info.regs->size(); + gpu->regs.user = info.regs.get(); +#if HAVE_ANDROID_OS + struct pmem_region region; + if (ioctl(fd, PMEM_GET_PHYS, ®ion) >= 0) + gpu->regs.phys = (void*)region.offset; +#endif + info.regs->incStrong(gpu); + } else { + LOGE("GPU register handle %p is invalid!", info.regs.get()); + failed = true; + } + } + + for (size_t i=0 ; i& region(info.regions[i].region); + if (region != 0) { + sp heap(region->getMemory()); + if (heap != 0) { + const int fd = heap->heapID(); + gpu->gpu[i].fd = fd; + gpu->gpu[i].base = region->pointer(); + gpu->gpu[i].size = region->size(); + gpu->gpu[i].user = region.get(); + gpu->gpu[i].offset = info.regions[i].reserved; +#if HAVE_ANDROID_OS + struct pmem_region reg; + if (ioctl(fd, PMEM_GET_PHYS, ®) >= 0) + gpu->gpu[i].phys = (void*)reg.offset; +#endif + region->incStrong(gpu); + } else { + LOGE("GPU region handle [%d, %p] is invalid!", i, region.get()); + failed = true; + } + } + } + + if (failed) { + // something went wrong, clean up everything! + if (gpu->regs.user) { + static_cast(gpu->regs.user)->decStrong(gpu); + for (size_t i=0 ; igpu[i].user) { + static_cast(gpu->gpu[i].user)->decStrong(gpu); + } + } + } + } + + gpu->count = info.count; + return gpu; +} + +int gpu_release(void*, request_gpu_t* gpu) +{ + sp regs; + + { // scope for lock + Mutex::Autolock _l(gRegionsLock); + regs = static_cast(gpu->regs.user); + gpu->regs.user = 0; + if (regs != 0) regs->decStrong(gpu); + + for (int i=0 ; icount ; i++) { + sp r(static_cast(gpu->gpu[i].user)); + gpu->gpu[i].user = 0; + if (r != 0) r->decStrong(gpu); + } + } + + // there is a special transaction to relinquish the GPU + // (it will happen automatically anyway if we don't do this) + Parcel data, reply; + // NOTE: this transaction does not require an interface token + regs->asBinder()->transact(1000, data, &reply); + return 1; +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp new file mode 100644 index 000000000..865cf4436 --- /dev/null +++ b/opengl/libs/GLES_CM/gl.cpp @@ -0,0 +1,116 @@ +/* + ** Copyright 2007, 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. + */ + +#define LOG_TAG "GLES_CM" + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "hooks.h" + +using namespace android; + +// ---------------------------------------------------------------------------- +// extensions for the framework +// ---------------------------------------------------------------------------- + +void glColorPointerBounds(GLint size, GLenum type, GLsizei stride, + const GLvoid *ptr, GLsizei count) { + glColorPointer(size, type, stride, ptr); +} +void glNormalPointerBounds(GLenum type, GLsizei stride, + const GLvoid *pointer, GLsizei count) { + glNormalPointer(type, stride, pointer); +} +void glTexCoordPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count) { + glTexCoordPointer(size, type, stride, pointer); +} +void glVertexPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count) { + glVertexPointer(size, type, stride, pointer); +} + +// ---------------------------------------------------------------------------- +// Actual GL entry-points +// ---------------------------------------------------------------------------- + +#if GL_LOGGER +# include "gl_logger.h" +# define GL_LOGGER_IMPL(_x) _x +#else +# define GL_LOGGER_IMPL(_x) +#endif + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_RETURN + +#if USE_FAST_TLS_KEY + + #define API_ENTRY(_api) __attribute__((naked)) _api + + #define CALL_GL_API(_api, ...) \ + asm volatile( \ + "mov r12, #0xFFFF0FFF \n" \ + "ldr r12, [r12, #-15] \n" \ + "ldr r12, [r12, %[tls]] \n" \ + "cmp r12, #0 \n" \ + "ldrne pc, [r12, %[api]] \n" \ + "bx lr \n" \ + : \ + : [tls] "J"(TLS_SLOT_OPENGL_API*4), \ + [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \ + : \ + ); + + #define CALL_GL_API_RETURN(_api, ...) \ + CALL_GL_API(_api, __VA_ARGS__) \ + return 0; // placate gcc's warnings. never reached. + +#else + + #define API_ENTRY(_api) _api + + #define CALL_GL_API(_api, ...) \ + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ + GL_LOGGER_IMPL( log_##_api(__VA_ARGS__); ) \ + _c->_api(__VA_ARGS__) + + #define CALL_GL_API_RETURN(_api, ...) \ + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ + GL_LOGGER_IMPL( log_##_api(__VA_ARGS__); ) \ + return _c->_api(__VA_ARGS__) + +#endif + +extern "C" { +#include "gl_api.in" +} + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_RETURN + diff --git a/opengl/libs/GLES_CM/gl_api.in b/opengl/libs/GLES_CM/gl_api.in new file mode 100644 index 000000000..9234ef273 --- /dev/null +++ b/opengl/libs/GLES_CM/gl_api.in @@ -0,0 +1,606 @@ +void API_ENTRY(glActiveTexture)(GLenum texture) { + CALL_GL_API(glActiveTexture, texture); +} + +void API_ENTRY(glAlphaFunc)(GLenum func, GLclampf ref) { + CALL_GL_API(glAlphaFunc, func, ref); +} + +void API_ENTRY(glAlphaFuncx)(GLenum func, GLclampx ref) { + CALL_GL_API(glAlphaFuncx, func, ref); +} + +void API_ENTRY(glBindTexture)(GLenum target, GLuint texture) { + CALL_GL_API(glBindTexture, target, texture); +} + +void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) { + CALL_GL_API(glBlendFunc, sfactor, dfactor); +} + +void API_ENTRY(glClear)(GLbitfield mask) { + CALL_GL_API(glClear, mask); +} + +void API_ENTRY(glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { + CALL_GL_API(glClearColor, red, green, blue, alpha); +} + +void API_ENTRY(glClearColorx)(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) { + CALL_GL_API(glClearColorx, red, green, blue, alpha); +} + +void API_ENTRY(glClearDepthf)(GLclampf depth) { + CALL_GL_API(glClearDepthf, depth); +} + +void API_ENTRY(glClearDepthx)(GLclampx depth) { + CALL_GL_API(glClearDepthx, depth); +} + +void API_ENTRY(glClearStencil)(GLint s) { + CALL_GL_API(glClearStencil, s); +} + +void API_ENTRY(glClientActiveTexture)(GLenum texture) { + CALL_GL_API(glClientActiveTexture, texture); +} + +void API_ENTRY(glColor4f)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { + CALL_GL_API(glColor4f, red, green, blue, alpha); +} + +void API_ENTRY(glColor4x)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) { + CALL_GL_API(glColor4x, red, green, blue, alpha); +} + +void API_ENTRY(glColorMask)(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { + CALL_GL_API(glColorMask, r, g, b, a); +} + +void API_ENTRY(glColorPointer)(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) +{ + CALL_GL_API(glColorPointer, size, type, stride, ptr); +} + +void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid *data) { + CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, + width, height, border, imageSize, data); +} + +void API_ENTRY(glCompressedTexSubImage2D)( GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLsizei imageSize, + const GLvoid *data) { + CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, + width, height, format, imageSize, data); +} + +void API_ENTRY(glCopyTexImage2D)( GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, + GLint border) { + CALL_GL_API(glCopyTexImage2D, target, level, internalformat, x, y, + width, height, border); +} + +void API_ENTRY(glCopyTexSubImage2D)( GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLint x, GLint y, GLsizei width, + GLsizei height) { + CALL_GL_API(glCopyTexSubImage2D, target, level, xoffset, yoffset, x, y, + width, height); +} + +void API_ENTRY(glCullFace)(GLenum mode) { + CALL_GL_API(glCullFace, mode); +} + +void API_ENTRY(glDeleteTextures)(GLsizei n, const GLuint *textures) { + CALL_GL_API(glDeleteTextures, n, textures); +} + +void API_ENTRY(glDepthFunc)(GLenum func) { + CALL_GL_API(glDepthFunc, func); +} + +void API_ENTRY(glDepthMask)(GLboolean flag) { + CALL_GL_API(glDepthMask, flag); +} + +void API_ENTRY(glDepthRangef)(GLclampf zNear, GLclampf zFar) { + CALL_GL_API(glDepthRangef, zNear, zFar); +} + +void API_ENTRY(glDepthRangex)(GLclampx zNear, GLclampx zFar) { + CALL_GL_API(glDepthRangex, zNear, zFar); +} + +void API_ENTRY(glDisable)(GLenum cap) { + CALL_GL_API(glDisable, cap); +} + +void API_ENTRY(glDisableClientState)(GLenum array) { + CALL_GL_API(glDisableClientState, array); +} + +void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) { + CALL_GL_API(glDrawArrays, mode, first, count); +} + +void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, + GLenum type, const GLvoid *indices) { + CALL_GL_API(glDrawElements, mode, count, type, indices); +} + +void API_ENTRY(glEnable)(GLenum cap) { + CALL_GL_API(glEnable, cap); +} + +void API_ENTRY(glEnableClientState)(GLenum array) { + CALL_GL_API(glEnableClientState, array); +} + +void API_ENTRY(glFinish)(void) { + CALL_GL_API(glFinish); +} + +void API_ENTRY(glFlush)(void) { + CALL_GL_API(glFlush); +} + +void API_ENTRY(glFogf)(GLenum pname, GLfloat param) { + CALL_GL_API(glFogf, pname, param); +} + +void API_ENTRY(glFogfv)(GLenum pname, const GLfloat *params) { + CALL_GL_API(glFogfv, pname, params); +} + +void API_ENTRY(glFogx)(GLenum pname, GLfixed param) { + CALL_GL_API(glFogx, pname, param); +} + +void API_ENTRY(glFogxv)(GLenum pname, const GLfixed *params) { + CALL_GL_API(glFogxv, pname, params); +} + +void API_ENTRY(glFrontFace)(GLenum mode) { + CALL_GL_API(glFrontFace, mode); +} + +void API_ENTRY(glFrustumf)(GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar) { + CALL_GL_API(glFrustumf, left, right, bottom, top, zNear, zFar); +} + +void API_ENTRY(glFrustumx)(GLfixed left, GLfixed right, + GLfixed bottom, GLfixed top, + GLfixed zNear, GLfixed zFar) { + CALL_GL_API(glFrustumx, left, right, bottom, top, zNear, zFar); +} + +void API_ENTRY(glGenTextures)(GLsizei n, GLuint *textures) { + CALL_GL_API(glGenTextures, n, textures); +} + +GLenum API_ENTRY(glGetError)(void) { + CALL_GL_API_RETURN(glGetError); +} + +void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *params) { + CALL_GL_API(glGetIntegerv, pname, params); +} + +const GLubyte * API_ENTRY(glGetString)(GLenum name) { + CALL_GL_API_RETURN(glGetString, name); +} + +void API_ENTRY(glHint)(GLenum target, GLenum mode) { + CALL_GL_API(glHint, target, mode); +} + +void API_ENTRY(glLightModelf)(GLenum pname, GLfloat param) { + CALL_GL_API(glLightModelf, pname, param); +} + +void API_ENTRY(glLightModelfv)(GLenum pname, const GLfloat *params) { + CALL_GL_API(glLightModelfv, pname, params); +} + +void API_ENTRY(glLightModelx)(GLenum pname, GLfixed param) { + CALL_GL_API(glLightModelx, pname, param); +} + +void API_ENTRY(glLightModelxv)(GLenum pname, const GLfixed *params) { + CALL_GL_API(glLightModelxv, pname, params); +} + +void API_ENTRY(glLightf)(GLenum light, GLenum pname, GLfloat param) { + CALL_GL_API(glLightf, light, pname, param); +} + +void API_ENTRY(glLightfv)(GLenum light, GLenum pname, const GLfloat *params) { + CALL_GL_API(glLightfv, light, pname, params); +} + +void API_ENTRY(glLightx)(GLenum light, GLenum pname, GLfixed param) { + CALL_GL_API(glLightx, light, pname, param); +} + +void API_ENTRY(glLightxv)(GLenum light, GLenum pname, const GLfixed *params) { + CALL_GL_API(glLightxv, light, pname, params); +} + +void API_ENTRY(glLineWidth)(GLfloat width) { + CALL_GL_API(glLineWidth, width); +} + +void API_ENTRY(glLineWidthx)(GLfixed width) { + CALL_GL_API(glLineWidthx, width); +} + +void API_ENTRY(glLoadIdentity)(void) { + CALL_GL_API(glLoadIdentity); +} + +void API_ENTRY(glLoadMatrixf)(const GLfloat *m) { + CALL_GL_API(glLoadMatrixf, m); +} + +void API_ENTRY(glLoadMatrixx)(const GLfixed *m) { + CALL_GL_API(glLoadMatrixx, m); +} + +void API_ENTRY(glLogicOp)(GLenum opcode) { + CALL_GL_API(glLogicOp, opcode); +} + +void API_ENTRY(glMaterialf)(GLenum face, GLenum pname, GLfloat param) { + CALL_GL_API(glMaterialf, face, pname, param); +} + +void API_ENTRY(glMaterialfv)(GLenum face, GLenum pname, const GLfloat *params) { + CALL_GL_API(glMaterialfv, face, pname, params); +} + +void API_ENTRY(glMaterialx)(GLenum face, GLenum pname, GLfixed param) { + CALL_GL_API(glMaterialx, face, pname, param); +} + +void API_ENTRY(glMaterialxv)(GLenum face, GLenum pname, const GLfixed *params) { + CALL_GL_API(glMaterialxv, face, pname, params); +} + +void API_ENTRY(glMatrixMode)(GLenum mode) { + CALL_GL_API(glMatrixMode, mode); +} + +void API_ENTRY(glMultMatrixf)(const GLfloat *m) { + CALL_GL_API(glMultMatrixf, m); +} + +void API_ENTRY(glMultMatrixx)(const GLfixed *m) { + CALL_GL_API(glMultMatrixx, m); +} + +void API_ENTRY(glMultiTexCoord4f)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) { + CALL_GL_API(glMultiTexCoord4f, target, s, t, r, q); +} + +void API_ENTRY(glMultiTexCoord4x)(GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) { + CALL_GL_API(glMultiTexCoord4x, target, s, t, r, q); +} + +void API_ENTRY(glNormal3f)(GLfloat nx, GLfloat ny, GLfloat nz) { + CALL_GL_API(glNormal3f, nx, ny, nz); +} + +void API_ENTRY(glNormal3x)(GLfixed nx, GLfixed ny, GLfixed nz) { + CALL_GL_API(glNormal3x, nx, ny, nz); +} + +void API_ENTRY(glNormalPointer)(GLenum type, GLsizei stride, const GLvoid *pointer) { + CALL_GL_API(glNormalPointer, type, stride, pointer); +} + +void API_ENTRY(glOrthof)( GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar) { + CALL_GL_API(glOrthof, left, right, bottom, top, zNear, zFar); +} + +void API_ENTRY(glOrthox)( GLfixed left, GLfixed right, + GLfixed bottom, GLfixed top, + GLfixed zNear, GLfixed zFar) { + CALL_GL_API(glOrthox, left, right, bottom, top, zNear, zFar); +} + +void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) { + CALL_GL_API(glPixelStorei, pname, param); +} + +void API_ENTRY(glPointSize)(GLfloat size) { + CALL_GL_API(glPointSize, size); +} + +void API_ENTRY(glPointSizex)(GLfixed size) { + CALL_GL_API(glPointSizex, size); +} + +void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) { + CALL_GL_API(glPolygonOffset, factor, units); +} + +void API_ENTRY(glPolygonOffsetx)(GLfixed factor, GLfixed units) { + CALL_GL_API(glPolygonOffsetx, factor, units); +} + +void API_ENTRY(glPopMatrix)(void) { + CALL_GL_API(glPopMatrix); +} + +void API_ENTRY(glPushMatrix)(void) { + CALL_GL_API(glPushMatrix); +} + +void API_ENTRY(glReadPixels)( GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid *pixels) { + CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels); +} + +void API_ENTRY(glRotatef)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) { + CALL_GL_API(glRotatef, angle, x, y, z); +} + +void API_ENTRY(glRotatex)(GLfixed angle, GLfixed x, GLfixed y, GLfixed z) { + CALL_GL_API(glRotatex, angle, x, y, z); +} + +void API_ENTRY(glSampleCoverage)(GLclampf value, GLboolean invert) { + CALL_GL_API(glSampleCoverage, value, invert); +} + +void API_ENTRY(glSampleCoveragex)(GLclampx value, GLboolean invert) { + CALL_GL_API(glSampleCoveragex, value, invert); +} + +void API_ENTRY(glScalef)(GLfloat x, GLfloat y, GLfloat z) { + CALL_GL_API(glScalef, x, y, z); +} + +void API_ENTRY(glScalex)(GLfixed x, GLfixed y, GLfixed z) { + CALL_GL_API(glScalex, x, y, z); +} + +void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) { + CALL_GL_API(glScissor, x, y, width, height); +} + +void API_ENTRY(glShadeModel)(GLenum mode) { + CALL_GL_API(glShadeModel, mode); +} + +void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) { + CALL_GL_API(glStencilFunc, func, ref, mask); +} + +void API_ENTRY(glStencilMask)(GLuint mask) { + CALL_GL_API(glStencilMask, mask); +} + +void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) { + CALL_GL_API(glStencilOp, fail, zfail, zpass); +} + +void API_ENTRY(glTexCoordPointer)( GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer) { + CALL_GL_API(glTexCoordPointer, size, type, stride, pointer); +} + +void API_ENTRY(glTexEnvf)(GLenum target, GLenum pname, GLfloat param) { + CALL_GL_API(glTexEnvf, target, pname, param); +} + +void API_ENTRY(glTexEnvfv)(GLenum target, GLenum pname, const GLfloat *params) { + CALL_GL_API(glTexEnvfv, target, pname, params); +} + +void API_ENTRY(glTexEnvx)(GLenum target, GLenum pname, GLfixed param) { + CALL_GL_API(glTexEnvx, target, pname, param); +} + +void API_ENTRY(glTexEnvxv)(GLenum target, GLenum pname, const GLfixed *params) { + CALL_GL_API(glTexEnvxv, target, pname, params); +} + +void API_ENTRY(glTexImage2D)( GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, GLenum format, + GLenum type, const GLvoid *pixels) { + CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, + border, format, type, pixels); +} + +void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) { + CALL_GL_API(glTexParameterf, target, pname, param); +} + +void API_ENTRY(glTexParameterx)(GLenum target, GLenum pname, GLfixed param) { + CALL_GL_API(glTexParameterx, target, pname, param); +} + +void API_ENTRY(glTexSubImage2D)( GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLenum type, const GLvoid *pixels) { + CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, + width, height, format, type, pixels); +} + +void API_ENTRY(glTranslatef)(GLfloat x, GLfloat y, GLfloat z) { + CALL_GL_API(glTranslatef, x, y, z); +} + +void API_ENTRY(glTranslatex)(GLfixed x, GLfixed y, GLfixed z) { + CALL_GL_API(glTranslatex, x, y, z); +} + +void API_ENTRY(glVertexPointer)( GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer) { + CALL_GL_API(glVertexPointer, size, type, stride, pointer); +} + +void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) { + CALL_GL_API(glViewport, x, y, width, height); +} + +// ES 1.1 +void API_ENTRY(glClipPlanef)(GLenum plane, const GLfloat *equation) { + CALL_GL_API(glClipPlanef, plane, equation); +} +void API_ENTRY(glClipPlanex)(GLenum plane, const GLfixed *equation) { + CALL_GL_API(glClipPlanex, plane, equation); +} +void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) { + CALL_GL_API(glBindBuffer, target, buffer); +} +void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) { + CALL_GL_API(glBufferData, target, size, data, usage); +} +void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) { + CALL_GL_API(glBufferSubData, target, offset, size, data); +} +void API_ENTRY(glDeleteBuffers)(GLsizei n, const GLuint* buffers) { + CALL_GL_API(glDeleteBuffers, n, buffers); +} +void API_ENTRY(glGenBuffers)(GLsizei n, GLuint* buffers) { + CALL_GL_API(glGenBuffers, n, buffers); +} +void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *params) { + CALL_GL_API(glGetBooleanv, pname, params); +} +void API_ENTRY(glGetFixedv)(GLenum pname, GLfixed *params) { + CALL_GL_API(glGetFixedv, pname, params); +} +void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *params) { + CALL_GL_API(glGetFloatv, pname, params); +} +void API_ENTRY(glGetPointerv)(GLenum pname, void **params) { + CALL_GL_API(glGetPointerv, pname, params); +} +void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetBufferParameteriv, target, pname, params); +} +void API_ENTRY(glGetClipPlanef)(GLenum pname, GLfloat eqn[4]) { + CALL_GL_API(glGetClipPlanef, pname, eqn); +} +void API_ENTRY(glGetClipPlanex)(GLenum pname, GLfixed eqn[4]) { + CALL_GL_API(glGetClipPlanex, pname, eqn); +} +void API_ENTRY(glGetLightxv)(GLenum light, GLenum pname, GLfixed *params) { + CALL_GL_API(glGetLightxv, light, pname, params); +} +void API_ENTRY(glGetLightfv)(GLenum light, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetLightfv, light, pname, params); +} +void API_ENTRY(glGetMaterialxv)(GLenum face, GLenum pname, GLfixed *params) { + CALL_GL_API(glGetMaterialxv, face, pname, params); +} +void API_ENTRY(glGetMaterialfv)(GLenum face, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetMaterialfv, face, pname, params); +} +void API_ENTRY(glGetTexEnvfv)(GLenum env, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetTexEnvfv, env, pname, params); +} +void API_ENTRY(glGetTexEnviv)(GLenum env, GLenum pname, GLint *params) { + CALL_GL_API(glGetTexEnviv, env, pname, params); +} +void API_ENTRY(glGetTexEnvxv)(GLenum env, GLenum pname, GLfixed *params) { + CALL_GL_API(glGetTexEnvxv, env, pname, params); +} +void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetTexParameterfv, target, pname, params); +} +void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetTexParameteriv, target, pname, params); +} +void API_ENTRY(glGetTexParameterxv)(GLenum target, GLenum pname, GLfixed *params) { + CALL_GL_API(glGetTexParameterxv, target, pname, params); +} +GLboolean API_ENTRY(glIsBuffer)(GLuint buffer) { + CALL_GL_API_RETURN(glIsBuffer, buffer); +} +GLboolean API_ENTRY(glIsEnabled)(GLenum cap) { + CALL_GL_API_RETURN(glIsEnabled, cap); +} +GLboolean API_ENTRY(glIsTexture)(GLuint texture) { + CALL_GL_API_RETURN(glIsTexture, texture); +} +void API_ENTRY(glPointParameterf)(GLenum pname, GLfloat param) { + CALL_GL_API(glPointParameterf, pname, param); +} +void API_ENTRY(glPointParameterfv)(GLenum pname, const GLfloat *params) { + CALL_GL_API(glPointParameterfv, pname, params); +} +void API_ENTRY(glPointParameterx)(GLenum pname, GLfixed param) { + CALL_GL_API(glPointParameterx, pname, param); +} +void API_ENTRY(glPointParameterxv)(GLenum pname, const GLfixed *params) { + CALL_GL_API(glPointParameterxv, pname, params); +} +void API_ENTRY(glColor4ub)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) { + CALL_GL_API(glColor4ub, red, green, blue, alpha); +} +void API_ENTRY(glTexEnvi)(GLenum target, GLenum pname, GLint param) { + CALL_GL_API(glTexEnvi, target, pname, param); +} +void API_ENTRY(glTexEnviv)(GLenum target, GLenum pname, const GLint *params) { + CALL_GL_API(glTexEnviv, target, pname, params); +} + +void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat *params) { + CALL_GL_API(glTexParameterfv, target, pname, params); +} + +void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint *params) { + CALL_GL_API(glTexParameteriv, target, pname, params); +} + +void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) { + CALL_GL_API(glTexParameteri, target, pname, param); +} +void API_ENTRY(glTexParameterxv)(GLenum target, GLenum pname, const GLfixed *params) { + CALL_GL_API(glTexParameterxv, target, pname, params); +} +void API_ENTRY(glPointSizePointerOES)(GLenum type, GLsizei stride, const GLvoid *pointer) { + CALL_GL_API(glPointSizePointerOES, type, stride, pointer); +} + +// Extensions +void API_ENTRY(glDrawTexsOES)(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) { + CALL_GL_API(glDrawTexsOES, x, y, z, w, h); +} +void API_ENTRY(glDrawTexiOES)(GLint x, GLint y, GLint z, GLint w, GLint h) { + CALL_GL_API(glDrawTexiOES, x, y, z, w, h); +} +void API_ENTRY(glDrawTexfOES)(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h) { + CALL_GL_API(glDrawTexfOES, x, y, z, w, h); +} +void API_ENTRY(glDrawTexxOES)(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) { + CALL_GL_API(glDrawTexxOES, x, y, z, w, h); +} +void API_ENTRY(glDrawTexsvOES)(const GLshort* coords) { + CALL_GL_API(glDrawTexsvOES, coords); +} +void API_ENTRY(glDrawTexivOES)(const GLint* coords) { + CALL_GL_API(glDrawTexivOES, coords); +} +void API_ENTRY(glDrawTexfvOES)(const GLfloat* coords) { + CALL_GL_API(glDrawTexfvOES, coords); +} +void API_ENTRY(glDrawTexxvOES)(const GLfixed* coords) { + CALL_GL_API(glDrawTexxvOES, coords); +} +GLbitfield API_ENTRY(glQueryMatrixxOES)(GLfixed* mantissa, GLint* exponent) { + CALL_GL_API_RETURN(glQueryMatrixxOES, mantissa, exponent); +} diff --git a/opengl/libs/GLES_CM/gl_logger.cpp b/opengl/libs/GLES_CM/gl_logger.cpp new file mode 100644 index 000000000..27be5c9ba --- /dev/null +++ b/opengl/libs/GLES_CM/gl_logger.cpp @@ -0,0 +1,1060 @@ +/* + ** Copyright 2007, 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. + */ + +#define LOG_TAG "GLLogger" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "gl_logger.h" + +#undef NELEM +#define NELEM(x) (sizeof(x)/sizeof(*(x))) + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +template +static int binarySearch(T const sortedArray[], int first, int last, EGLint key) +{ + while (first <= last) { + int mid = (first + last) / 2; + if (key > sortedArray[mid].key) { + first = mid + 1; + } else if (key < sortedArray[mid].key) { + last = mid - 1; + } else { + return mid; + } + } + return -1; +} + +struct pair_t { + const char* name; + int key; +}; + +static const pair_t gEnumMap[] = { + #define GLENUM(NAME, VALUE) { #NAME, VALUE }, + #include "gl_enums.in" + #undef GLENUM +}; + +// ---------------------------------------------------------------------------- + +template +class GLLogValue { +public: + GLLogValue(TYPE value) : mValue(value) { } + const TYPE& getValue() const { return mValue; } + String8 toString() const { + return convertToString(mValue); + } +private: + const TYPE& mValue; + String8 convertToString(unsigned int v) const { + char buf[16]; + snprintf(buf, 16, "%u", v); + return String8(buf); + } + String8 convertToString(unsigned long v) const { + char buf[16]; + snprintf(buf, 16, "%lu", v); + return String8(buf); + } + String8 convertToString(int v) const { + char buf[16]; + snprintf(buf, 16, "%d", v); + return String8(buf); + } + String8 convertToString(long v) const { + char buf[16]; + snprintf(buf, 16, "%ld", v); + return String8(buf); + } + String8 convertToString(float v) const { + char buf[16]; + snprintf(buf, 16, "%f", v); + return String8(buf); + } + String8 convertToString(void const* v) const { + char buf[16]; + snprintf(buf, 16, "%p", v); + return String8(buf); + } +}; + +class GLLogEnum : public GLLogValue { +public: + GLLogEnum(GLenum v) : GLLogValue(v) { } + String8 toString() const { + GLenum v = getValue(); + int i = binarySearch(gEnumMap, 0, NELEM(gEnumMap)-1, v); + if (i >= 0) { + return String8(gEnumMap[i].name); + } else { + char buf[16]; + snprintf(buf, 16, "0x%04x", v); + return String8(buf); + } + } +}; + +class GLLogClearBitfield : public GLLogValue { +public: + GLLogClearBitfield(GLbitfield v) : GLLogValue(v) { } + String8 toString() const { + char buf[16]; + snprintf(buf, 16, "0x%08x", getValue()); + return String8(buf); + } +}; + +class GLLogBool : public GLLogValue { +public: + GLLogBool(GLboolean v) : GLLogValue(v) { } + String8 toString() const { + GLboolean v = getValue(); + if (v == GL_TRUE) return String8("GL_TRUE"); + if (v == GL_FALSE) return String8("GL_FALSE"); + return GLLogValue::toString(); + } +}; + +class GLLogFixed : public GLLogValue { +public: + GLLogFixed(GLfixed v) : GLLogValue(v) { } + String8 toString() const { + char buf[16]; + snprintf(buf, 16, "0x%08x", getValue()); + return String8(buf); + } +}; + + +template +class GLLogBuffer : public GLLogValue { +public: + GLLogBuffer(TYPE* buffer, size_t count = -1) + : GLLogValue(buffer) + { // output buffer + } + GLLogBuffer(TYPE const* buffer, size_t count = -1) + : GLLogValue(const_cast(buffer)) + { // input buffer + } +}; + +class GLLog +{ +public: + GLLog(const char* name) : mNumParams(0) { + mString.append(name); + mString.append("("); + } + + ~GLLog() { + LOGD("%s);", mString.string()); + } + + GLLog& operator << (unsigned char v) { + return *this << GLLogValue(v); + } + GLLog& operator << (short v) { + return *this << GLLogValue(v); + } + GLLog& operator << (unsigned int v) { + return *this << GLLogValue(v); + } + GLLog& operator << (int v) { + return *this << GLLogValue(v); + } + GLLog& operator << (long v) { + return *this << GLLogValue(v); + } + GLLog& operator << (unsigned long v) { + return *this << GLLogValue(v); + } + GLLog& operator << (float v) { + return *this << GLLogValue(v); + } + GLLog& operator << (const void* v) { + return *this << GLLogValue(v); + } + + template + GLLog& operator << (const TYPE& rhs) { + if (mNumParams > 0) + mString.append(", "); + mString.append(rhs.toString()); + mNumParams++; + return *this; + } + + const String8& string() const { return mString; } +private: + GLLog(const GLLog&); + + String8 mString; + int mNumParams; +}; + +#define API_ENTRY(api) log_##api +#define CALL_GL_API(_x, ...) +#define CALL_GL_API_RETURN(_x, ...) return(0); + +void API_ENTRY(glActiveTexture)(GLenum texture) { + CALL_GL_API(glActiveTexture, texture); + GLLog("glActiveTexture") << GLLogEnum(texture); +} + +void API_ENTRY(glAlphaFunc)(GLenum func, GLclampf ref) { + CALL_GL_API(glAlphaFunc, func, ref); + GLLog("glAlphaFunc") << GLLogEnum(func) << ref; +} + +void API_ENTRY(glAlphaFuncx)(GLenum func, GLclampx ref) { + CALL_GL_API(glAlphaFuncx, func, ref); + GLLog("glAlphaFuncx") << GLLogEnum(func) << GLLogFixed(ref); +} + +void API_ENTRY(glBindTexture)(GLenum target, GLuint texture) { + CALL_GL_API(glBindTexture, target, texture); + GLLog("glBindTexture") << GLLogEnum(target) << texture; +} + +void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) { + CALL_GL_API(glBlendFunc, sfactor, dfactor); + GLLog("glBlendFunc") << GLLogEnum(sfactor) << GLLogEnum(dfactor); +} + +void API_ENTRY(glClear)(GLbitfield mask) { + CALL_GL_API(glClear, mask); + GLLog("glClear") << GLLogClearBitfield(mask); +} + +void API_ENTRY(glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { + CALL_GL_API(glClearColor, red, green, blue, alpha); + GLLog("glClearColor") << red << green << blue << alpha; +} + +void API_ENTRY(glClearColorx)(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) { + CALL_GL_API(glClearColorx, red, green, blue, alpha); + GLLog("glClearColorx") << GLLogFixed(red) << GLLogFixed(green) << GLLogFixed(blue) << GLLogFixed(alpha); +} + +void API_ENTRY(glClearDepthf)(GLclampf depth) { + CALL_GL_API(glClearDepthf, depth); + GLLog("glClearDepthf") << depth; +} + +void API_ENTRY(glClearDepthx)(GLclampx depth) { + CALL_GL_API(glClearDepthx, depth); + GLLog("glClearDepthx") << GLLogFixed(depth); +} + +void API_ENTRY(glClearStencil)(GLint s) { + CALL_GL_API(glClearStencil, s); + GLLog("glClearStencil") << s; +} + +void API_ENTRY(glClientActiveTexture)(GLenum texture) { + CALL_GL_API(glClientActiveTexture, texture); + GLLog("glClientActiveTexture") << GLLogEnum(texture); +} + +void API_ENTRY(glColor4f)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { + CALL_GL_API(glColor4f, red, green, blue, alpha); + GLLog("glColor4f") << red << green << blue << alpha; +} + +void API_ENTRY(glColor4x)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) { + CALL_GL_API(glColor4x, red, green, blue, alpha); + GLLog("glColor4x") << GLLogFixed(red) << GLLogFixed(green) << GLLogFixed(blue) << GLLogFixed(alpha); +} + +void API_ENTRY(glColorMask)(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { + CALL_GL_API(glColorMask, r, g, b, a); + GLLog("glColorMask") << GLLogBool(r) << GLLogBool(g) << GLLogBool(b) << GLLogBool(a); +} + +void API_ENTRY(glColorPointer)(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) +{ + CALL_GL_API(glColorPointer, size, type, stride, ptr); + GLLog("glColorPointer") << size << GLLogEnum(type) << stride << ptr; +} + +void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid *data) { + CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, + width, height, border, imageSize, data); + GLLog("glCompressedTexImage2D") + << GLLogEnum(target) << level << GLLogEnum(internalformat) + << width << height << border << imageSize << data; +} + +void API_ENTRY(glCompressedTexSubImage2D)( GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLsizei imageSize, + const GLvoid *data) { + CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, + width, height, format, imageSize, data); + GLLog("glCompressedTexSubImage2D") + << GLLogEnum(target) << level << xoffset << yoffset + << width << height << GLLogEnum(format) << imageSize << data; +} + +void API_ENTRY(glCopyTexImage2D)( GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, + GLint border) { + CALL_GL_API(glCopyTexImage2D, target, level, internalformat, x, y, + width, height, border); + GLLog("glCopyTexImage2D") + << GLLogEnum(target) << level << GLLogEnum(internalformat) + << x << y << width << height << border; +} + +void API_ENTRY(glCopyTexSubImage2D)( GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLint x, GLint y, GLsizei width, + GLsizei height) { + CALL_GL_API(glCopyTexSubImage2D, target, level, xoffset, yoffset, x, y, + width, height); + GLLog("glCopyTexSubImage2D") + << GLLogEnum(target) << level << xoffset << yoffset + << x << y << width << height; +} + +void API_ENTRY(glCullFace)(GLenum mode) { + CALL_GL_API(glCullFace, mode); + GLLog("glCullFace") << GLLogEnum(mode); +} + +void API_ENTRY(glDeleteTextures)(GLsizei n, const GLuint *textures) { + CALL_GL_API(glDeleteTextures, n, textures); + GLLog("glDeleteTextures") << n << GLLogBuffer(textures, n); +} + +void API_ENTRY(glDepthFunc)(GLenum func) { + CALL_GL_API(glDepthFunc, func); + GLLog("glDepthFunc") << GLLogEnum(func); +} + +void API_ENTRY(glDepthMask)(GLboolean flag) { + CALL_GL_API(glDepthMask, flag); + GLLog("glDepthMask") << GLLogBool(flag); +} + +void API_ENTRY(glDepthRangef)(GLclampf zNear, GLclampf zFar) { + CALL_GL_API(glDepthRangef, zNear, zFar); + GLLog("glDepthRangef") << zNear << zFar; +} + +void API_ENTRY(glDepthRangex)(GLclampx zNear, GLclampx zFar) { + CALL_GL_API(glDepthRangex, zNear, zFar); + GLLog("glDepthRangex") << GLLogFixed(zNear) << GLLogFixed(zFar); +} + +void API_ENTRY(glDisable)(GLenum cap) { + CALL_GL_API(glDisable, cap); + GLLog("glDisable") << GLLogEnum(cap); +} + +void API_ENTRY(glDisableClientState)(GLenum array) { + CALL_GL_API(glDisableClientState, array); + GLLog("glDisableClientState") << GLLogEnum(array); +} + +void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) { + CALL_GL_API(glDrawArrays, mode, first, count); + GLLog("glDrawArrays") << GLLogEnum(mode) << first << count; +} + +void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, + GLenum type, const GLvoid *indices) { + CALL_GL_API(glDrawElements, mode, count, type, indices); + GLLog log("glDrawElements"); + log << GLLogEnum(mode) << count << GLLogEnum(type); + if (type == GL_UNSIGNED_BYTE) { + log << GLLogBuffer(static_cast(indices), count); + } else { + log << GLLogBuffer(static_cast(indices), count); + } + log; +} + +void API_ENTRY(glEnable)(GLenum cap) { + CALL_GL_API(glEnable, cap); + GLLog("glEnable") << GLLogEnum(cap); +} + +void API_ENTRY(glEnableClientState)(GLenum array) { + CALL_GL_API(glEnableClientState, array); + GLLog("glEnableClientState") << GLLogEnum(array); +} + +void API_ENTRY(glFinish)(void) { + CALL_GL_API(glFinish); + GLLog("glFinish"); +} + +void API_ENTRY(glFlush)(void) { + CALL_GL_API(glFlush); + GLLog("glFlush"); +} + +void API_ENTRY(glFogf)(GLenum pname, GLfloat param) { + CALL_GL_API(glFogf, pname, param); + GLLog("glFogf") << GLLogEnum(pname) << param; +} + +void API_ENTRY(glFogfv)(GLenum pname, const GLfloat *params) { + CALL_GL_API(glFogfv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glFogfv") << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glFogx)(GLenum pname, GLfixed param) { + CALL_GL_API(glFogx, pname, param); + GLLog("glFogx") << GLLogEnum(pname) << GLLogFixed(param); +} + +void API_ENTRY(glFogxv)(GLenum pname, const GLfixed *params) { + CALL_GL_API(glFogxv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glFogfx") << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glFrontFace)(GLenum mode) { + CALL_GL_API(glFrontFace, mode); + GLLog("glFrontFace") << GLLogEnum(mode); + } + +void API_ENTRY(glFrustumf)(GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar) { + CALL_GL_API(glFrustumf, left, right, bottom, top, zNear, zFar); + GLLog("glFrustumf") << left << right << bottom << top << zNear << zFar; +} + +void API_ENTRY(glFrustumx)(GLfixed left, GLfixed right, + GLfixed bottom, GLfixed top, + GLfixed zNear, GLfixed zFar) { + CALL_GL_API(glFrustumx, left, right, bottom, top, zNear, zFar); + GLLog("glFrustumx") + << GLLogFixed(left) << GLLogFixed(right) + << GLLogFixed(bottom) << GLLogFixed(top) + << GLLogFixed(zNear) << GLLogFixed(zFar); +} + +void API_ENTRY(glGenTextures)(GLsizei n, GLuint *textures) { + CALL_GL_API(glGenTextures, n, textures); + GLLog("glGenTextures") << n << GLLogBuffer(textures, n); +} + +GLenum API_ENTRY(glGetError)(void) { + GLLog("glGetError"); + CALL_GL_API_RETURN(glGetError); +} + +void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *params) { + CALL_GL_API(glGetIntegerv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetIntegerv") << GLLogEnum(pname) << GLLogBuffer(params); +} + +const GLubyte * API_ENTRY(glGetString)(GLenum name) { + GLLog("glGetString") << GLLogEnum(name); + CALL_GL_API_RETURN(glGetString, name); +} + +void API_ENTRY(glHint)(GLenum target, GLenum mode) { + CALL_GL_API(glHint, target, mode); + GLLog("GLenum") << GLLogEnum(target) << GLLogEnum(mode); +} + +void API_ENTRY(glLightModelf)(GLenum pname, GLfloat param) { + CALL_GL_API(glLightModelf, pname, param); + GLLog("glLightModelf") << GLLogEnum(pname) << param; +} + +void API_ENTRY(glLightModelfv)(GLenum pname, const GLfloat *params) { + CALL_GL_API(glLightModelfv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glLightModelfv") << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glLightModelx)(GLenum pname, GLfixed param) { + CALL_GL_API(glLightModelx, pname, param); + GLLog("glLightModelx") << GLLogEnum(pname) << GLLogFixed(param); +} + +void API_ENTRY(glLightModelxv)(GLenum pname, const GLfixed *params) { + CALL_GL_API(glLightModelxv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glLightModelxv") << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glLightf)(GLenum light, GLenum pname, GLfloat param) { + CALL_GL_API(glLightf, light, pname, param); + GLLog("glLightf") << GLLogEnum(light) << GLLogEnum(pname) << param; +} + +void API_ENTRY(glLightfv)(GLenum light, GLenum pname, const GLfloat *params) { + CALL_GL_API(glLightfv, light, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glLightfv") << GLLogEnum(light) << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glLightx)(GLenum light, GLenum pname, GLfixed param) { + CALL_GL_API(glLightx, light, pname, param); + GLLog("glLightx") << GLLogEnum(light) << GLLogEnum(pname) << GLLogFixed(param); +} + +void API_ENTRY(glLightxv)(GLenum light, GLenum pname, const GLfixed *params) { + CALL_GL_API(glLightxv, light, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glLightxv") << GLLogEnum(light) << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glLineWidth)(GLfloat width) { + CALL_GL_API(glLineWidth, width); + GLLog("glLineWidth") << width; +} + +void API_ENTRY(glLineWidthx)(GLfixed width) { + CALL_GL_API(glLineWidthx, width); + GLLog("glLineWidth") << GLLogFixed(width); +} + +void API_ENTRY(glLoadIdentity)(void) { + CALL_GL_API(glLoadIdentity); + GLLog("glLoadIdentity"); +} + +void API_ENTRY(glLoadMatrixf)(const GLfloat *m) { + CALL_GL_API(glLoadMatrixf, m); + GLLog("glLoadMatrixf") << GLLogBuffer(m, 16); +} + +void API_ENTRY(glLoadMatrixx)(const GLfixed *m) { + CALL_GL_API(glLoadMatrixx, m); + GLLog("glLoadMatrixx") << GLLogBuffer(m, 16); +} + +void API_ENTRY(glLogicOp)(GLenum opcode) { + CALL_GL_API(glLogicOp, opcode); + GLLog("glLogicOp") << GLLogEnum(opcode); +} + +void API_ENTRY(glMaterialf)(GLenum face, GLenum pname, GLfloat param) { + CALL_GL_API(glMaterialf, face, pname, param); + GLLog("glMaterialf") << GLLogEnum(face) << GLLogEnum(pname) << param; +} + +void API_ENTRY(glMaterialfv)(GLenum face, GLenum pname, const GLfloat *params) { + CALL_GL_API(glMaterialfv, face, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glMaterialfv") << GLLogEnum(face) << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glMaterialx)(GLenum face, GLenum pname, GLfixed param) { + CALL_GL_API(glMaterialx, face, pname, param); + GLLog("glMaterialx") << GLLogEnum(face) << GLLogEnum(pname) << GLLogFixed(param); +} + +void API_ENTRY(glMaterialxv)(GLenum face, GLenum pname, const GLfixed *params) { + CALL_GL_API(glMaterialxv, face, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glMaterialxv") << GLLogEnum(face) << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glMatrixMode)(GLenum mode) { + CALL_GL_API(glMatrixMode, mode); + GLLog("glMatrixMode") << GLLogEnum(mode); +} + +void API_ENTRY(glMultMatrixf)(const GLfloat *m) { + CALL_GL_API(glMultMatrixf, m); + GLLog("glMultMatrixf") << GLLogBuffer(m, 16); +} + +void API_ENTRY(glMultMatrixx)(const GLfixed *m) { + CALL_GL_API(glMultMatrixx, m); + GLLog("glMultMatrixx") << GLLogBuffer(m, 16); +} + +void API_ENTRY(glMultiTexCoord4f)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) { + CALL_GL_API(glMultiTexCoord4f, target, s, t, r, q); + GLLog("glMultiTexCoord4f") << GLLogEnum(target) << s << t << r << q; +} + +void API_ENTRY(glMultiTexCoord4x)(GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) { + CALL_GL_API(glMultiTexCoord4x, target, s, t, r, q); + GLLog("glMultiTexCoord4x") << GLLogEnum(target) + << GLLogFixed(s) << GLLogFixed(t) << GLLogFixed(r) << GLLogFixed(q); +} + +void API_ENTRY(glNormal3f)(GLfloat nx, GLfloat ny, GLfloat nz) { + CALL_GL_API(glNormal3f, nx, ny, nz); + GLLog("glNormal3f") << nx << ny << nz; +} + +void API_ENTRY(glNormal3x)(GLfixed nx, GLfixed ny, GLfixed nz) { + CALL_GL_API(glNormal3x, nx, ny, nz); + GLLog("glNormal3x") << GLLogFixed(nx) << GLLogFixed(ny) << GLLogFixed(nz); +} + +void API_ENTRY(glNormalPointer)(GLenum type, GLsizei stride, const GLvoid *pointer) { + CALL_GL_API(glNormalPointer, type, stride, pointer); + GLLog("glNormalPointer") << GLLogEnum(type) << stride << pointer; +} + +void API_ENTRY(glOrthof)( GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar) { + CALL_GL_API(glOrthof, left, right, bottom, top, zNear, zFar); + GLLog("glOrthof") << left << right << bottom << top << zNear << zFar; +} + +void API_ENTRY(glOrthox)( GLfixed left, GLfixed right, + GLfixed bottom, GLfixed top, + GLfixed zNear, GLfixed zFar) { + CALL_GL_API(glOrthox, left, right, bottom, top, zNear, zFar); + GLLog("glOrthox") << GLLogFixed(left) << GLLogFixed(right) + << GLLogFixed(bottom) << GLLogFixed(top) + << GLLogFixed(zNear) << GLLogFixed(zFar); +} + +void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) { + CALL_GL_API(glPixelStorei, pname, param); + GLLog("glPixelStorei") << GLLogEnum(pname) << param; +} + +void API_ENTRY(glPointSize)(GLfloat size) { + CALL_GL_API(glPointSize, size); + GLLog("glPointSize") << size; +} + +void API_ENTRY(glPointSizex)(GLfixed size) { + CALL_GL_API(glPointSizex, size); + GLLog("glPointSizex") << GLLogFixed(size); +} + +void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) { + CALL_GL_API(glPolygonOffset, factor, units); + GLLog("glPolygonOffset") << factor << units; +} + +void API_ENTRY(glPolygonOffsetx)(GLfixed factor, GLfixed units) { + CALL_GL_API(glPolygonOffsetx, factor, units); + GLLog("glPolygonOffsetx") << GLLogFixed(factor) << GLLogFixed(units); +} + +void API_ENTRY(glPopMatrix)(void) { + CALL_GL_API(glPopMatrix); + GLLog("glPopMatrix"); +} + +void API_ENTRY(glPushMatrix)(void) { + CALL_GL_API(glPushMatrix); + GLLog("glPushMatrix"); +} + +void API_ENTRY(glReadPixels)( GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid *pixels) { + CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels); + // XXX: we need to compute the size of this buffer + GLLog("glReadPixels") << x << y << width << height << GLLogEnum(format) << GLLogEnum(type) + << GLLogBuffer(static_cast(pixels)); +} + +void API_ENTRY(glRotatef)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) { + CALL_GL_API(glRotatef, angle, x, y, z); + GLLog("glRotatef") << angle << x << y << z; +} + +void API_ENTRY(glRotatex)(GLfixed angle, GLfixed x, GLfixed y, GLfixed z) { + CALL_GL_API(glRotatex, angle, x, y, z); + GLLog("glRotatex") << GLLogFixed(angle) << GLLogFixed(x) << GLLogFixed(y) << GLLogFixed(z); +} + +void API_ENTRY(glSampleCoverage)(GLclampf value, GLboolean invert) { + CALL_GL_API(glSampleCoverage, value, invert); + GLLog("glSampleCoverage") << value << GLLogBool(invert); +} + +void API_ENTRY(glSampleCoveragex)(GLclampx value, GLboolean invert) { + CALL_GL_API(glSampleCoveragex, value, invert); + GLLog("glSampleCoveragex") << GLLogFixed(value) << GLLogBool(invert); +} + +void API_ENTRY(glScalef)(GLfloat x, GLfloat y, GLfloat z) { + CALL_GL_API(glScalef, x, y, z); + GLLog("glScalef") << x << y << z; +} + +void API_ENTRY(glScalex)(GLfixed x, GLfixed y, GLfixed z) { + CALL_GL_API(glScalex, x, y, z); + GLLog("glScalex") << GLLogFixed(x) << GLLogFixed(y) << GLLogFixed(z); +} + +void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) { + CALL_GL_API(glScissor, x, y, width, height); + GLLog("glScissor") << x << y << width << height; +} + +void API_ENTRY(glShadeModel)(GLenum mode) { + CALL_GL_API(glShadeModel, mode); + GLLog("glShadeModel") << GLLogEnum(mode); +} + +void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) { + CALL_GL_API(glStencilFunc, func, ref, mask); + GLLog("glStencilFunc") << GLLogEnum(func) << ref << mask; +} + +void API_ENTRY(glStencilMask)(GLuint mask) { + CALL_GL_API(glStencilMask, mask); + GLLog("glStencilMask") << mask; +} + +void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) { + CALL_GL_API(glStencilOp, fail, zfail, zpass); + GLLog("glStencilOp") << GLLogEnum(fail) << GLLogEnum(zfail) << GLLogEnum(zpass); +} + +void API_ENTRY(glTexCoordPointer)( GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer) { + CALL_GL_API(glTexCoordPointer, size, type, stride, pointer); + GLLog("glTexCoordPointer") << size << GLLogEnum(type) << stride << pointer; +} + +void API_ENTRY(glTexEnvf)(GLenum target, GLenum pname, GLfloat param) { + CALL_GL_API(glTexEnvf, target, pname, param); + GLLog("glTexEnvf") << GLLogEnum(target) << GLLogEnum(pname) << param; +} + +void API_ENTRY(glTexEnvfv)(GLenum target, GLenum pname, const GLfloat *params) { + CALL_GL_API(glTexEnvfv, target, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glTexEnvx") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glTexEnvx)(GLenum target, GLenum pname, GLfixed param) { + CALL_GL_API(glTexEnvx, target, pname, param); + GLLog("glTexEnvx") << GLLogEnum(target) << GLLogEnum(pname) << GLLogFixed(param); +} + +void API_ENTRY(glTexEnvxv)(GLenum target, GLenum pname, const GLfixed *params) { + CALL_GL_API(glTexEnvxv, target, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glTexEnvxv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glTexImage2D)( GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, GLenum format, + GLenum type, const GLvoid *pixels) { + CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, + border, format, type, pixels); + GLLog("glTexImage2D") << GLLogEnum(target) << level << GLLogEnum(internalformat) + << width << height << border << GLLogEnum(format) << GLLogEnum(type) + << GLLogBuffer( static_cast(pixels)); +} + +void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) { + CALL_GL_API(glTexParameterf, target, pname, param); + GLLog("glTexParameterf") << GLLogEnum(target) << GLLogEnum(pname) << param; +} + +void API_ENTRY(glTexParameterx)(GLenum target, GLenum pname, GLfixed param) { + CALL_GL_API(glTexParameterx, target, pname, param); + GLLog("glTexParameterx") << GLLogEnum(target) << GLLogEnum(pname) << GLLogFixed(param); +} + +void API_ENTRY(glTexSubImage2D)( GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLenum type, const GLvoid *pixels) { + CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, + width, height, format, type, pixels); + GLLog("glTexSubImage2D") << GLLogEnum(target) << level << xoffset << yoffset + << width << height << GLLogEnum(format) << GLLogEnum(type) + << GLLogBuffer( static_cast(pixels)); +} + +void API_ENTRY(glTranslatef)(GLfloat x, GLfloat y, GLfloat z) { + CALL_GL_API(glTranslatef, x, y, z); + GLLog("glTranslatef") << x << y << z; +} + +void API_ENTRY(glTranslatex)(GLfixed x, GLfixed y, GLfixed z) { + CALL_GL_API(glTranslatex, x, y, z); + GLLog("glTranslatex") << GLLogFixed(x) << GLLogFixed(y) << GLLogFixed(z); +} + +void API_ENTRY(glVertexPointer)( GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer) { + CALL_GL_API(glVertexPointer, size, type, stride, pointer); + GLLog("glVertexPointer") << size << GLLogEnum(type) << stride << pointer; +} + +void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) { + CALL_GL_API(glViewport, x, y, width, height); + GLLog("glViewport") << x << y << width << height; +} + +// ES 1.1 +void API_ENTRY(glClipPlanef)(GLenum plane, const GLfloat *equation) { + CALL_GL_API(glClipPlanef, plane, equation); + GLLog("glClipPlanef") << GLLogEnum(plane) << GLLogBuffer(equation, 4); +} +void API_ENTRY(glClipPlanex)(GLenum plane, const GLfixed *equation) { + CALL_GL_API(glClipPlanex, plane, equation); + GLLog("glClipPlanex") << GLLogEnum(plane) << GLLogBuffer(equation, 4); +} +void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) { + CALL_GL_API(glBindBuffer, target, buffer); + GLLog("glBindBuffer") << GLLogEnum(target) << buffer; +} +void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) { + CALL_GL_API(glBufferData, target, size, data, usage); + GLLog("glBufferData") << GLLogEnum(target) << size + << GLLogBuffer(static_cast(data), size); +} +void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) { + CALL_GL_API(glBufferSubData, target, offset, size, data); + GLLog("glBufferSubData") << GLLogEnum(target) << offset << size + << GLLogBuffer(static_cast(data), size); +} +void API_ENTRY(glDeleteBuffers)(GLsizei n, const GLuint* buffers) { + CALL_GL_API(glDeleteBuffers, n, buffers); + GLLog("glDeleteBuffers") << n << GLLogBuffer(buffers, n); +} +void API_ENTRY(glGenBuffers)(GLsizei n, GLuint* buffers) { + CALL_GL_API(glGenBuffers, n, buffers); + GLLog("glGenBuffers") << n << GLLogBuffer(buffers, n); +} +void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *params) { + CALL_GL_API(glGetBooleanv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetBooleanv") << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetFixedv)(GLenum pname, GLfixed *params) { + CALL_GL_API(glGetFixedv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetFixedv") << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *params) { + CALL_GL_API(glGetFloatv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetFloatv") << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetPointerv)(GLenum pname, void **params) { + CALL_GL_API(glGetPointerv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetPointerv") << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint *params) { + // XXX: we need to compute the size of this buffer + CALL_GL_API(glGetBufferParameteriv, target, pname, params); + GLLog("glGetBufferParameteriv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetClipPlanef)(GLenum pname, GLfloat eqn[4]) { + CALL_GL_API(glGetClipPlanef, pname, eqn); + GLLog("glGetClipPlanef") << GLLogEnum(pname) << GLLogBuffer(eqn, 4); +} +void API_ENTRY(glGetClipPlanex)(GLenum pname, GLfixed eqn[4]) { + CALL_GL_API(glGetClipPlanex, pname, eqn); + GLLog("glGetClipPlanex") << GLLogEnum(pname) << GLLogBuffer(eqn, 4); +} +void API_ENTRY(glGetLightxv)(GLenum light, GLenum pname, GLfixed *params) { + CALL_GL_API(glGetLightxv, light, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetLightxv") << GLLogEnum(light) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetLightfv)(GLenum light, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetLightfv, light, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetLightfv") << GLLogEnum(light) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetMaterialxv)(GLenum face, GLenum pname, GLfixed *params) { + CALL_GL_API(glGetMaterialxv, face, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetMaterialxv") << GLLogEnum(face) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetMaterialfv)(GLenum face, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetMaterialfv, face, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetMaterialfv") << GLLogEnum(face) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetTexEnvfv)(GLenum env, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetTexEnvfv, env, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetTexEnvfv") << GLLogEnum(env) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetTexEnviv)(GLenum env, GLenum pname, GLint *params) { + CALL_GL_API(glGetTexEnviv, env, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetTexEnviv") << GLLogEnum(env) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetTexEnvxv)(GLenum env, GLenum pname, GLfixed *params) { + CALL_GL_API(glGetTexEnvxv, env, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetTexEnvxv") << GLLogEnum(env) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetTexParameterfv, target, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetTexParameterfv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetTexParameteriv, target, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetTexParameteriv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glGetTexParameterxv)(GLenum target, GLenum pname, GLfixed *params) { + CALL_GL_API(glGetTexParameterxv, target, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glGetTexParameterxv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); +} +GLboolean API_ENTRY(glIsBuffer)(GLuint buffer) { + GLLog("glIsBuffer") << buffer; + CALL_GL_API_RETURN(glIsBuffer, buffer); +} +GLboolean API_ENTRY(glIsEnabled)(GLenum cap) { + GLLog("glIsEnabled") << GLLogEnum(cap); + CALL_GL_API_RETURN(glIsEnabled, cap); +} +GLboolean API_ENTRY(glIsTexture)(GLuint texture) { + GLLog("glIsTexture") << texture; + CALL_GL_API_RETURN(glIsTexture, texture); +} +void API_ENTRY(glPointParameterf)(GLenum pname, GLfloat param) { + CALL_GL_API(glPointParameterf, pname, param); + GLLog("glPointParameterf") << GLLogEnum(pname) << param; +} +void API_ENTRY(glPointParameterfv)(GLenum pname, const GLfloat *params) { + CALL_GL_API(glPointParameterfv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glPointParameterfv") << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glPointParameterx)(GLenum pname, GLfixed param) { + CALL_GL_API(glPointParameterx, pname, param); + GLLog("glPointParameterx") << GLLogEnum(pname) << GLLogFixed(param); +} +void API_ENTRY(glPointParameterxv)(GLenum pname, const GLfixed *params) { + CALL_GL_API(glPointParameterxv, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glPointParameterxv") << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glColor4ub)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) { + CALL_GL_API(glColor4ub, red, green, blue, alpha); + GLLog("glColor4ub") << red << green << blue << alpha; +} +void API_ENTRY(glTexEnvi)(GLenum target, GLenum pname, GLint param) { + CALL_GL_API(glTexEnvi, target, pname, param); + GLLog("glTexEnvi") << GLLogEnum(target) << GLLogEnum(pname) << param; +} +void API_ENTRY(glTexEnviv)(GLenum target, GLenum pname, const GLint *params) { + CALL_GL_API(glTexEnviv, target, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glTexEnviv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat *params) { + CALL_GL_API(glTexParameterfv, target, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glTexParameterfv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint *params) { + CALL_GL_API(glTexParameteriv, target, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glTexParameteriv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); +} + +void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) { + CALL_GL_API(glTexParameteri, target, pname, param); + GLLog("glTexParameteri") << GLLogEnum(target) << GLLogEnum(pname) << param; +} +void API_ENTRY(glTexParameterxv)(GLenum target, GLenum pname, const GLfixed *params) { + CALL_GL_API(glTexParameterxv, target, pname, params); + // XXX: we need to compute the size of this buffer + GLLog("glTexParameterxv") << GLLogEnum(target) << GLLogEnum(pname) << GLLogBuffer(params); +} +void API_ENTRY(glPointSizePointerOES)(GLenum type, GLsizei stride, const GLvoid *pointer) { + CALL_GL_API(glPointSizePointerOES, type, stride, pointer); + GLLog("glPointSizePointerOES") << GLLogEnum(type) << stride << pointer; +} + +// Extensions +void API_ENTRY(glDrawTexsOES)(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) { + CALL_GL_API(glDrawTexsOES, x, y, z, w, h); + GLLog("glDrawTexsOES") << x << y << z << w << h; +} +void API_ENTRY(glDrawTexiOES)(GLint x, GLint y, GLint z, GLint w, GLint h) { + CALL_GL_API(glDrawTexiOES, x, y, z, w, h); + GLLog("glDrawTexiOES") << x << y << z << w << h; +} +void API_ENTRY(glDrawTexfOES)(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h) { + CALL_GL_API(glDrawTexfOES, x, y, z, w, h); + GLLog("glDrawTexfOES") << x << y << z << w << h; +} +void API_ENTRY(glDrawTexxOES)(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) { + CALL_GL_API(glDrawTexxOES, x, y, z, w, h); + GLLog("glDrawTexfOES") << GLLogFixed(x) << GLLogFixed(y) << GLLogFixed(z) << GLLogFixed(w) << GLLogFixed(h); +} +void API_ENTRY(glDrawTexsvOES)(const GLshort* coords) { + CALL_GL_API(glDrawTexsvOES, coords); + GLLog("glDrawTexsvOES") << GLLogBuffer(coords, 5); +} +void API_ENTRY(glDrawTexivOES)(const GLint* coords) { + CALL_GL_API(glDrawTexivOES, coords); + GLLog("glDrawTexivOES") << GLLogBuffer(coords, 5); +} +void API_ENTRY(glDrawTexfvOES)(const GLfloat* coords) { + CALL_GL_API(glDrawTexfvOES, coords); + GLLog("glDrawTexfvOES") << GLLogBuffer(coords, 5); +} +void API_ENTRY(glDrawTexxvOES)(const GLfixed* coords) { + CALL_GL_API(glDrawTexxvOES, coords); + GLLog("glDrawTexxvOES") << GLLogBuffer(coords, 5); +} +GLbitfield API_ENTRY(glQueryMatrixxOES)(GLfixed* mantissa, GLint* exponent) { + GLLog("glQueryMatrixxOES") << GLLogBuffer(mantissa, 16) << GLLogBuffer(exponent, 16); + CALL_GL_API_RETURN(glQueryMatrixxOES, mantissa, exponent); +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- diff --git a/opengl/libs/egl_entries.in b/opengl/libs/egl_entries.in new file mode 100644 index 000000000..33b4c654d --- /dev/null +++ b/opengl/libs/egl_entries.in @@ -0,0 +1,45 @@ +EGL_ENTRY(EGLDisplay, eglGetDisplay, NativeDisplayType) +EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*) +EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay) +EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*) +EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *) + +EGL_ENTRY(EGLBoolean, eglGetConfigAttrib, EGLDisplay, EGLConfig, EGLint, EGLint *) +EGL_ENTRY(EGLSurface, eglCreateWindowSurface, EGLDisplay, EGLConfig, NativeWindowType, const EGLint *) +EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint *) +EGL_ENTRY(EGLSurface, eglCreatePbufferSurface, EGLDisplay, EGLConfig, const EGLint *) +EGL_ENTRY(EGLBoolean, eglDestroySurface, EGLDisplay, EGLSurface) +EGL_ENTRY(EGLBoolean, eglQuerySurface, EGLDisplay, EGLSurface, EGLint, EGLint *) +EGL_ENTRY(EGLContext, eglCreateContext, EGLDisplay, EGLConfig, EGLContext, const EGLint *) +EGL_ENTRY(EGLBoolean, eglDestroyContext, EGLDisplay, EGLContext) +EGL_ENTRY(EGLBoolean, eglMakeCurrent, EGLDisplay, EGLSurface, EGLSurface, EGLContext) +EGL_ENTRY(EGLContext, eglGetCurrentContext, void) +EGL_ENTRY(EGLSurface, eglGetCurrentSurface, EGLint) +EGL_ENTRY(EGLDisplay, eglGetCurrentDisplay, void) +EGL_ENTRY(EGLBoolean, eglQueryContext, EGLDisplay, EGLContext, EGLint, EGLint *) +EGL_ENTRY(EGLBoolean, eglWaitGL, void) +EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint) +EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface) +EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType) +EGL_ENTRY(EGLint, eglGetError, void) +EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint) +EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char *) + +/* EGL 1.1 */ + +EGL_ENTRY(EGLBoolean, eglSurfaceAttrib, EGLDisplay, EGLSurface, EGLint, EGLint) +EGL_ENTRY(EGLBoolean, eglBindTexImage, EGLDisplay, EGLSurface, EGLint) +EGL_ENTRY(EGLBoolean, eglReleaseTexImage, EGLDisplay, EGLSurface, EGLint) +EGL_ENTRY(EGLBoolean, eglSwapInterval, EGLDisplay, EGLint) + +/* EGL 1.2 */ + +EGL_ENTRY(EGLBoolean, eglBindAPI, EGLenum) +EGL_ENTRY(EGLenum, eglQueryAPI, void) +EGL_ENTRY(EGLBoolean, eglWaitClient, void) +EGL_ENTRY(EGLBoolean, eglReleaseThread, void) +EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint *) + +/* EGL 1.3 */ + +/* EGL 1.4 */ diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h new file mode 100644 index 000000000..62ce3fce1 --- /dev/null +++ b/opengl/libs/egl_impl.h @@ -0,0 +1,43 @@ +/* + ** Copyright 2007, 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. + */ + +#ifndef ANDROID_EGL_IMPL_H +#define ANDROID_EGL_IMPL_H + +#include + +#include + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +struct gl_hooks_t; + +struct egl_connection_t +{ + void volatile * dso; + gl_hooks_t * hooks; + EGLint major; + EGLint minor; + int unavailable; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +#endif /* ANDROID_EGL_IMPL_H */ diff --git a/opengl/libs/gl_entries.in b/opengl/libs/gl_entries.in new file mode 100644 index 000000000..b97e8fe3c --- /dev/null +++ b/opengl/libs/gl_entries.in @@ -0,0 +1,159 @@ +GL_ENTRY(void, glColor4f, GLfloat, GLfloat, GLfloat, GLfloat) +GL_ENTRY(void, glColor4x, GLfixed, GLfixed, GLfixed, GLfixed) +GL_ENTRY(void, glNormal3f, GLfloat, GLfloat, GLfloat) +GL_ENTRY(void, glNormal3x, GLfixed, GLfixed, GLfixed) +GL_ENTRY(void, glCullFace, GLenum) +GL_ENTRY(void, glFrontFace, GLenum) +GL_ENTRY(void, glDisable, GLenum) +GL_ENTRY(void, glEnable, GLenum) +GL_ENTRY(void, glFinish, void) +GL_ENTRY(void, glFlush, void) +GL_ENTRY(GLenum, glGetError, void) +GL_ENTRY(const GLubyte*, glGetString, GLenum) +GL_ENTRY(void, glGetIntegerv, GLenum, GLint *) +GL_ENTRY(void, glColorMask, GLboolean, GLboolean, GLboolean, GLboolean) +GL_ENTRY(void, glDepthMask, GLboolean) +GL_ENTRY(void, glStencilMask, GLuint) +GL_ENTRY(void, glDepthFunc, GLenum) +GL_ENTRY(void, glDepthRangef, GLclampf zNear, GLclampf zFar) +GL_ENTRY(void, glDepthRangex, GLclampx zNear, GLclampx zFar) +GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units) +GL_ENTRY(void, glPolygonOffsetx, GLfixed factor, GLfixed units) +GL_ENTRY(void, glLogicOp, GLenum opcode) +GL_ENTRY(void, glAlphaFuncx, GLenum func, GLclampx ref) +GL_ENTRY(void, glAlphaFunc, GLenum func, GLclampf ref) +GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor) +GL_ENTRY(void, glClear, GLbitfield mask) +GL_ENTRY(void, glClearColor, GLclampf r, GLclampf g, GLclampf b, GLclampf a) +GL_ENTRY(void, glClearColorx, GLclampx r, GLclampx g, GLclampx b, GLclampx a) +GL_ENTRY(void, glClearDepthf, GLclampf depth) +GL_ENTRY(void, glClearDepthx, GLclampx depth) +GL_ENTRY(void, glClearStencil, GLint s) +GL_ENTRY(void, glPointSize, GLfloat) +GL_ENTRY(void, glPointSizex, GLfixed) +GL_ENTRY(void, glSampleCoverage, GLclampf value, GLboolean invert) +GL_ENTRY(void, glSampleCoveragex, GLclampx value, GLboolean invert) +GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask) +GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass) +GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glHint, GLenum, GLenum mode) +GL_ENTRY(void, glLineWidth, GLfloat width) +GL_ENTRY(void, glLineWidthx, GLfixed width) +GL_ENTRY(void, glShadeModel, GLenum) +GL_ENTRY(void, glLightModelf, GLenum, GLfloat) +GL_ENTRY(void, glLightModelfv, GLenum, const GLfloat *) +GL_ENTRY(void, glLightModelx, GLenum, GLfixed) +GL_ENTRY(void, glLightModelxv, GLenum, const GLfixed *) +GL_ENTRY(void, glLightf, GLenum, GLenum, GLfloat) +GL_ENTRY(void, glLightfv, GLenum, GLenum, const GLfloat *) +GL_ENTRY(void, glLightx, GLenum, GLenum, GLfixed) +GL_ENTRY(void, glLightxv, GLenum, GLenum, const GLfixed *) +GL_ENTRY(void, glMaterialf, GLenum, GLenum, GLfloat) +GL_ENTRY(void, glMaterialfv, GLenum, GLenum, const GLfloat *) +GL_ENTRY(void, glMaterialx, GLenum, GLenum, GLfixed) +GL_ENTRY(void, glMaterialxv, GLenum, GLenum, const GLfixed *) +GL_ENTRY(void, glFogf, GLenum, GLfloat) +GL_ENTRY(void, glFogfv, GLenum, const GLfloat *) +GL_ENTRY(void, glFogx, GLenum, GLfixed) +GL_ENTRY(void, glFogxv, GLenum, const GLfixed *) +GL_ENTRY(void, glVertexPointer, GLint, GLenum, GLsizei, const GLvoid *) +GL_ENTRY(void, glColorPointer, GLint, GLenum, GLsizei, const GLvoid *) +GL_ENTRY(void, glNormalPointer, GLenum, GLsizei, const GLvoid *) +GL_ENTRY(void, glTexCoordPointer, GLint, GLenum, GLsizei, const GLvoid *) +GL_ENTRY(void, glEnableClientState, GLenum) +GL_ENTRY(void, glDisableClientState, GLenum) +GL_ENTRY(void, glClientActiveTexture, GLenum) +GL_ENTRY(void, glDrawArrays, GLenum, GLint first, GLsizei) +GL_ENTRY(void, glDrawElements, GLenum, GLsizei, GLenum, const GLvoid *) +GL_ENTRY(void, glLoadIdentity, void) +GL_ENTRY(void, glLoadMatrixf, const GLfloat*) +GL_ENTRY(void, glLoadMatrixx, const GLfixed*) +GL_ENTRY(void, glMatrixMode, GLenum mode) +GL_ENTRY(void, glMultMatrixf, const GLfloat*) +GL_ENTRY(void, glMultMatrixx, const GLfixed*) +GL_ENTRY(void, glPopMatrix, void) +GL_ENTRY(void, glPushMatrix, void) +GL_ENTRY(void, glFrustumf, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat) +GL_ENTRY(void, glFrustumx, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed) +GL_ENTRY(void, glOrthof, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat) +GL_ENTRY(void, glOrthox, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed) +GL_ENTRY(void, glRotatef, GLfloat, GLfloat, GLfloat, GLfloat) +GL_ENTRY(void, glRotatex, GLfixed, GLfixed, GLfixed, GLfixed) +GL_ENTRY(void, glScalef, GLfloat, GLfloat, GLfloat) +GL_ENTRY(void, glScalex, GLfixed, GLfixed, GLfixed) +GL_ENTRY(void, glTranslatef, GLfloat, GLfloat, GLfloat) +GL_ENTRY(void, glTranslatex, GLfixed, GLfixed, GLfixed) +GL_ENTRY(void, glViewport, GLint, GLint, GLsizei, GLsizei) +GL_ENTRY(void, glActiveTexture, GLenum) +GL_ENTRY(void, glBindTexture, GLenum, GLuint) +GL_ENTRY(void, glGenTextures, GLsizei, GLuint*) +GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *) +GL_ENTRY(void, glMultiTexCoord4f, GLenum, GLfloat, GLfloat, GLfloat, GLfloat) +GL_ENTRY(void, glMultiTexCoord4x, GLenum, GLfixed, GLfixed, GLfixed, GLfixed) +GL_ENTRY(void, glPixelStorei, GLenum, GLint) +GL_ENTRY(void, glTexEnvf, GLenum, GLenum, GLfloat) +GL_ENTRY(void, glTexEnvfv, GLenum, GLenum, const GLfloat*) +GL_ENTRY(void, glTexEnvx, GLenum, GLenum, GLfixed) +GL_ENTRY(void, glTexEnvxv, GLenum, GLenum, const GLfixed*) +GL_ENTRY(void, glTexParameterf, GLenum, GLenum, GLfloat) +GL_ENTRY(void, glTexParameterx, GLenum, GLenum, GLfixed) +GL_ENTRY(void, glCompressedTexImage2D, GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*) +GL_ENTRY(void, glCompressedTexSubImage2D, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*) +GL_ENTRY(void, glCopyTexImage2D, GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint) +GL_ENTRY(void, glCopyTexSubImage2D, GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei) +GL_ENTRY(void, glTexImage2D, GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*) +GL_ENTRY(void, glTexSubImage2D, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*) +GL_ENTRY(void, glReadPixels, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *) + +// 1.1 additions +GL_ENTRY(void, glClipPlanef, GLenum plane, const GLfloat*) +GL_ENTRY(void, glClipPlanex, GLenum plane, const GLfixed*) +GL_ENTRY(void, glBindBuffer, GLenum, GLuint) +GL_ENTRY(void, glBufferData, GLenum, GLsizeiptr, const GLvoid*, GLenum) +GL_ENTRY(void, glBufferSubData, GLenum, GLintptr, GLsizeiptr, const GLvoid*) +GL_ENTRY(void, glDeleteBuffers, GLsizei, const GLuint*) +GL_ENTRY(void, glGenBuffers, GLsizei, GLuint*) +GL_ENTRY(void, glGetBooleanv, GLenum, GLboolean *) +GL_ENTRY(void, glGetFixedv, GLenum, GLfixed *) +GL_ENTRY(void, glGetFloatv, GLenum, GLfloat *) +GL_ENTRY(void, glGetPointerv, GLenum, void **) +GL_ENTRY(void, glGetBufferParameteriv, GLenum, GLenum, GLint *) +GL_ENTRY(void, glGetClipPlanef, GLenum, GLfloat[4]) +GL_ENTRY(void, glGetClipPlanex, GLenum, GLfixed[4]) +GL_ENTRY(void, glGetLightxv, GLenum, GLenum, GLfixed *) +GL_ENTRY(void, glGetLightfv, GLenum, GLenum, GLfloat *) +GL_ENTRY(void, glGetMaterialxv, GLenum, GLenum, GLfixed *) +GL_ENTRY(void, glGetMaterialfv, GLenum, GLenum, GLfloat *) +GL_ENTRY(void, glGetTexEnvfv, GLenum, GLenum, GLfloat *) +GL_ENTRY(void, glGetTexEnviv, GLenum, GLenum, GLint *) +GL_ENTRY(void, glGetTexEnvxv, GLenum, GLenum, GLfixed *) +GL_ENTRY(void, glGetTexParameterfv, GLenum, GLenum, GLfloat *) +GL_ENTRY(void, glGetTexParameteriv, GLenum, GLenum, GLint *) +GL_ENTRY(void, glGetTexParameterxv, GLenum, GLenum, GLfixed *) +GL_ENTRY(GLboolean, glIsBuffer, GLuint) +GL_ENTRY(GLboolean, glIsEnabled, GLenum) +GL_ENTRY(GLboolean, glIsTexture, GLuint) +GL_ENTRY(void, glPointParameterf, GLenum, GLfloat) +GL_ENTRY(void, glPointParameterfv, GLenum, const GLfloat *) +GL_ENTRY(void, glPointParameterx, GLenum, GLfixed) +GL_ENTRY(void, glPointParameterxv, GLenum, const GLfixed *) +GL_ENTRY(void, glColor4ub, GLubyte, GLubyte, GLubyte, GLubyte) +GL_ENTRY(void, glTexEnvi, GLenum, GLenum, GLint) +GL_ENTRY(void, glTexEnviv, GLenum, GLenum, const GLint *) +GL_ENTRY(void, glTexParameterfv, GLenum, GLenum, const GLfloat *) +GL_ENTRY(void, glTexParameteriv, GLenum, GLenum, const GLint *) +GL_ENTRY(void, glTexParameteri, GLenum, GLenum, GLint) +GL_ENTRY(void, glTexParameterxv, GLenum, GLenum, const GLfixed *) +GL_ENTRY(void, glPointSizePointerOES, GLenum type, GLsizei stride, const GLvoid*) + +// Extensions +GL_ENTRY(void, glDrawTexsOES, GLshort, GLshort, GLshort, GLshort, GLshort) +GL_ENTRY(void, glDrawTexiOES, GLint, GLint, GLint, GLint, GLint) +GL_ENTRY(void, glDrawTexfOES, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat) +GL_ENTRY(void, glDrawTexxOES, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed) +GL_ENTRY(void, glDrawTexsvOES, const GLshort*) +GL_ENTRY(void, glDrawTexivOES, const GLint*) +GL_ENTRY(void, glDrawTexfvOES, const GLfloat*) +GL_ENTRY(void, glDrawTexxvOES, const GLfixed*) +GL_ENTRY(GLbitfield, glQueryMatrixxOES, GLfixed* mantissa, GLint* exponent) + diff --git a/opengl/libs/gl_enums.in b/opengl/libs/gl_enums.in new file mode 100644 index 000000000..ffc2fad43 --- /dev/null +++ b/opengl/libs/gl_enums.in @@ -0,0 +1,261 @@ +GLENUM(GL_POINTS, 0x0000) +GLENUM(GL_LINES, 0x0001) +GLENUM(GL_LINE_LOOP, 0x0002) +GLENUM(GL_LINE_STRIP, 0x0003) +GLENUM(GL_TRIANGLES, 0x0004) +GLENUM(GL_TRIANGLE_STRIP, 0x0005) +GLENUM(GL_TRIANGLE_FAN, 0x0006) +GLENUM(GL_ADD, 0x0104) +GLENUM(GL_NEVER, 0x0200) +GLENUM(GL_LESS, 0x0201) +GLENUM(GL_EQUAL, 0x0202) +GLENUM(GL_LEQUAL, 0x0203) +GLENUM(GL_GREATER, 0x0204) +GLENUM(GL_NOTEQUAL, 0x0205) +GLENUM(GL_GEQUAL, 0x0206) +GLENUM(GL_ALWAYS, 0x0207) +GLENUM(GL_SRC_COLOR, 0x0300) +GLENUM(GL_ONE_MINUS_SRC_COLOR, 0x0301) +GLENUM(GL_SRC_ALPHA, 0x0302) +GLENUM(GL_ONE_MINUS_SRC_ALPHA, 0x0303) +GLENUM(GL_DST_ALPHA, 0x0304) +GLENUM(GL_ONE_MINUS_DST_ALPHA, 0x0305) +GLENUM(GL_DST_COLOR, 0x0306) +GLENUM(GL_ONE_MINUS_DST_COLOR, 0x0307) +GLENUM(GL_SRC_ALPHA_SATURATE, 0x0308) +GLENUM(GL_FRONT, 0x0404) +GLENUM(GL_BACK, 0x0405) +GLENUM(GL_FRONT_AND_BACK, 0x0408) +GLENUM(GL_INVALID_ENUM, 0x0500) +GLENUM(GL_INVALID_VALUE, 0x0501) +GLENUM(GL_INVALID_OPERATION, 0x0502) +GLENUM(GL_STACK_OVERFLOW, 0x0503) +GLENUM(GL_STACK_UNDERFLOW, 0x0504) +GLENUM(GL_OUT_OF_MEMORY, 0x0505) +GLENUM(GL_EXP, 0x0800) +GLENUM(GL_EXP2, 0x0801) +GLENUM(GL_CW, 0x0900) +GLENUM(GL_CCW, 0x0901) +GLENUM(GL_POINT_SMOOTH, 0x0B10) +GLENUM(GL_SMOOTH_POINT_SIZE_RANGE, 0x0B12) +GLENUM(GL_LINE_SMOOTH, 0x0B20) +GLENUM(GL_SMOOTH_LINE_WIDTH_RANGE, 0x0B22) +GLENUM(GL_CULL_FACE, 0x0B44) +GLENUM(GL_LIGHTING, 0x0B50) +GLENUM(GL_LIGHT_MODEL_TWO_SIDE, 0x0B52) +GLENUM(GL_LIGHT_MODEL_AMBIENT, 0x0B53) +GLENUM(GL_COLOR_MATERIAL, 0x0B57) +GLENUM(GL_FOG, 0x0B60) +GLENUM(GL_FOG_DENSITY, 0x0B62) +GLENUM(GL_FOG_START, 0x0B63) +GLENUM(GL_FOG_END, 0x0B64) +GLENUM(GL_FOG_MODE, 0x0B65) +GLENUM(GL_FOG_COLOR, 0x0B66) +GLENUM(GL_DEPTH_TEST, 0x0B71) +GLENUM(GL_STENCIL_TEST, 0x0B90) +GLENUM(GL_NORMALIZE, 0x0BA1) +GLENUM(GL_ALPHA_TEST, 0x0BC0) +GLENUM(GL_DITHER, 0x0BD0) +GLENUM(GL_BLEND, 0x0BE2) +GLENUM(GL_COLOR_LOGIC_OP, 0x0BF2) +GLENUM(GL_SCISSOR_TEST, 0x0C11) +GLENUM(GL_PERSPECTIVE_CORRECTION_HINT, 0x0C50) +GLENUM(GL_POINT_SMOOTH_HINT, 0x0C51) +GLENUM(GL_LINE_SMOOTH_HINT, 0x0C52) +GLENUM(GL_POLYGON_SMOOTH_HINT, 0x0C53) +GLENUM(GL_FOG_HINT, 0x0C54) +GLENUM(GL_UNPACK_ALIGNMENT, 0x0CF5) +GLENUM(GL_PACK_ALIGNMENT, 0x0D05) +GLENUM(GL_MAX_LIGHTS, 0x0D31) +GLENUM(GL_MAX_CLIP_PLANES, 0x0D32) +GLENUM(GL_MAX_TEXTURE_SIZE, 0x0D33) +GLENUM(GL_MAX_MODELVIEW_STACK_DEPTH, 0x0D36) +GLENUM(GL_MAX_PROJECTION_STACK_DEPTH, 0x0D38) +GLENUM(GL_MAX_TEXTURE_STACK_DEPTH, 0x0D39) +GLENUM(GL_MAX_VIEWPORT_DIMS, 0x0D3A) +GLENUM(GL_RED_BITS, 0x0D52) +GLENUM(GL_GREEN_BITS, 0x0D53) +GLENUM(GL_BLUE_BITS, 0x0D54) +GLENUM(GL_ALPHA_BITS, 0x0D55) +GLENUM(GL_DEPTH_BITS, 0x0D56) +GLENUM(GL_STENCIL_BITS, 0x0D57) +GLENUM(GL_TEXTURE_2D, 0x0DE1) +GLENUM(GL_DONT_CARE, 0x1100) +GLENUM(GL_FASTEST, 0x1101) +GLENUM(GL_NICEST, 0x1102) +GLENUM(GL_AMBIENT, 0x1200) +GLENUM(GL_DIFFUSE, 0x1201) +GLENUM(GL_SPECULAR, 0x1202) +GLENUM(GL_POSITION, 0x1203) +GLENUM(GL_SPOT_DIRECTION, 0x1204) +GLENUM(GL_SPOT_EXPONENT, 0x1205) +GLENUM(GL_SPOT_CUTOFF, 0x1206) +GLENUM(GL_CONSTANT_ATTENUATION, 0x1207) +GLENUM(GL_LINEAR_ATTENUATION, 0x1208) +GLENUM(GL_QUADRATIC_ATTENUATION, 0x1209) +GLENUM(GL_BYTE, 0x1400) +GLENUM(GL_UNSIGNED_BYTE, 0x1401) +GLENUM(GL_SHORT, 0x1402) +GLENUM(GL_UNSIGNED_SHORT, 0x1403) +GLENUM(GL_FLOAT, 0x1406) +GLENUM(GL_FIXED, 0x140C) +GLENUM(GL_CLEAR, 0x1500) +GLENUM(GL_AND, 0x1501) +GLENUM(GL_AND_REVERSE, 0x1502) +GLENUM(GL_COPY, 0x1503) +GLENUM(GL_AND_INVERTED, 0x1504) +GLENUM(GL_NOOP, 0x1505) +GLENUM(GL_XOR, 0x1506) +GLENUM(GL_OR, 0x1507) +GLENUM(GL_NOR, 0x1508) +GLENUM(GL_EQUIV, 0x1509) +GLENUM(GL_INVERT, 0x150A) +GLENUM(GL_OR_REVERSE, 0x150B) +GLENUM(GL_COPY_INVERTED, 0x150C) +GLENUM(GL_OR_INVERTED, 0x150D) +GLENUM(GL_NAND, 0x150E) +GLENUM(GL_SET, 0x150F) +GLENUM(GL_EMISSION, 0x1600) +GLENUM(GL_SHININESS, 0x1601) +GLENUM(GL_AMBIENT_AND_DIFFUSE, 0x1602) +GLENUM(GL_MODELVIEW, 0x1700) +GLENUM(GL_PROJECTION, 0x1701) +GLENUM(GL_TEXTURE, 0x1702) +GLENUM(GL_ALPHA, 0x1906) +GLENUM(GL_RGB, 0x1907) +GLENUM(GL_RGBA, 0x1908) +GLENUM(GL_LUMINANCE, 0x1909) +GLENUM(GL_LUMINANCE_ALPHA, 0x190A) +GLENUM(GL_FLAT, 0x1D00) +GLENUM(GL_SMOOTH, 0x1D01) +GLENUM(GL_KEEP, 0x1E00) +GLENUM(GL_REPLACE, 0x1E01) +GLENUM(GL_REPLACE, 0x1E01) +GLENUM(GL_INCR, 0x1E02) +GLENUM(GL_DECR, 0x1E03) +GLENUM(GL_VENDOR, 0x1F00) +GLENUM(GL_RENDERER, 0x1F01) +GLENUM(GL_VERSION, 0x1F02) +GLENUM(GL_EXTENSIONS, 0x1F03) +GLENUM(GL_MODULATE, 0x2100) +GLENUM(GL_DECAL, 0x2101) +GLENUM(GL_TEXTURE_ENV_MODE, 0x2200) +GLENUM(GL_TEXTURE_ENV_COLOR, 0x2201) +GLENUM(GL_TEXTURE_ENV, 0x2300) +GLENUM(GL_NEAREST, 0x2600) +GLENUM(GL_LINEAR, 0x2601) +GLENUM(GL_NEAREST_MIPMAP_NEAREST, 0x2700) +GLENUM(GL_LINEAR_MIPMAP_NEAREST, 0x2701) +GLENUM(GL_NEAREST_MIPMAP_LINEAR, 0x2702) +GLENUM(GL_LINEAR_MIPMAP_LINEAR, 0x2703) +GLENUM(GL_TEXTURE_MAG_FILTER, 0x2800) +GLENUM(GL_TEXTURE_MIN_FILTER, 0x2801) +GLENUM(GL_TEXTURE_WRAP_S, 0x2802) +GLENUM(GL_TEXTURE_WRAP_T, 0x2803) +GLENUM(GL_CLAMP, 0x2900) +GLENUM(GL_REPEAT, 0x2901) +GLENUM(GL_CLIP_PLANE0, 0x3000) +GLENUM(GL_CLIP_PLANE1, 0x3001) +GLENUM(GL_CLIP_PLANE2, 0x3002) +GLENUM(GL_CLIP_PLANE3, 0x3003) +GLENUM(GL_CLIP_PLANE4, 0x3004) +GLENUM(GL_CLIP_PLANE5, 0x3005) +GLENUM(GL_LIGHT0, 0x4000) +GLENUM(GL_LIGHT1, 0x4001) +GLENUM(GL_LIGHT2, 0x4002) +GLENUM(GL_LIGHT3, 0x4003) +GLENUM(GL_LIGHT4, 0x4004) +GLENUM(GL_LIGHT5, 0x4005) +GLENUM(GL_LIGHT6, 0x4006) +GLENUM(GL_LIGHT7, 0x4007) +GLENUM(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0x7E80) +GLENUM(GL_UNSIGNED_SHORT_4_4_4_4, 0x8033) +GLENUM(GL_UNSIGNED_SHORT_5_5_5_1, 0x8034) +GLENUM(GL_POLYGON_OFFSET_FILL, 0x8037) +GLENUM(GL_RESCALE_NORMAL, 0x803A) +GLENUM(GL_VERTEX_ARRAY, 0x8074) +GLENUM(GL_NORMAL_ARRAY, 0x8075) +GLENUM(GL_COLOR_ARRAY, 0x8076) +GLENUM(GL_TEXTURE_COORD_ARRAY, 0x8078) +GLENUM(GL_MULTISAMPLE, 0x809D) +GLENUM(GL_SAMPLE_ALPHA_TO_COVERAGE, 0x809E) +GLENUM(GL_SAMPLE_ALPHA_TO_ONE, 0x809F) +GLENUM(GL_SAMPLE_COVERAGE, 0x80A0) +GLENUM(GL_MAX_ELEMENTS_VERTICES, 0x80E8) +GLENUM(GL_MAX_ELEMENTS_INDICES, 0x80E9) +GLENUM(GL_CLAMP_TO_EDGE, 0x812F) +GLENUM(GL_GENERATE_MIPMAP, 0x8191) +GLENUM(GL_GENERATE_MIPMAP_HINT, 0x8192) +GLENUM(GL_UNSIGNED_SHORT_5_6_5, 0x8363) +GLENUM(GL_ALIASED_POINT_SIZE_RANGE, 0x846D) +GLENUM(GL_ALIASED_LINE_WIDTH_RANGE, 0x846E) +GLENUM(GL_TEXTURE0, 0x84C0) +GLENUM(GL_TEXTURE1, 0x84C1) +GLENUM(GL_TEXTURE2, 0x84C2) +GLENUM(GL_TEXTURE3, 0x84C3) +GLENUM(GL_TEXTURE4, 0x84C4) +GLENUM(GL_TEXTURE5, 0x84C5) +GLENUM(GL_TEXTURE6, 0x84C6) +GLENUM(GL_TEXTURE7, 0x84C7) +GLENUM(GL_TEXTURE8, 0x84C8) +GLENUM(GL_TEXTURE9, 0x84C9) +GLENUM(GL_TEXTURE10, 0x84CA) +GLENUM(GL_TEXTURE11, 0x84CB) +GLENUM(GL_TEXTURE12, 0x84CC) +GLENUM(GL_TEXTURE13, 0x84CD) +GLENUM(GL_TEXTURE14, 0x84CE) +GLENUM(GL_TEXTURE15, 0x84CF) +GLENUM(GL_TEXTURE16, 0x84D0) +GLENUM(GL_TEXTURE17, 0x84D1) +GLENUM(GL_TEXTURE18, 0x84D2) +GLENUM(GL_TEXTURE19, 0x84D3) +GLENUM(GL_TEXTURE20, 0x84D4) +GLENUM(GL_TEXTURE21, 0x84D5) +GLENUM(GL_TEXTURE22, 0x84D6) +GLENUM(GL_TEXTURE23, 0x84D7) +GLENUM(GL_TEXTURE24, 0x84D8) +GLENUM(GL_TEXTURE25, 0x84D9) +GLENUM(GL_TEXTURE26, 0x84DA) +GLENUM(GL_TEXTURE27, 0x84DB) +GLENUM(GL_TEXTURE28, 0x84DC) +GLENUM(GL_TEXTURE29, 0x84DD) +GLENUM(GL_TEXTURE30, 0x84DE) +GLENUM(GL_TEXTURE31, 0x84DF) +GLENUM(GL_MAX_TEXTURE_UNITS, 0x84E2) +GLENUM(GL_NUM_COMPRESSED_TEXTURE_FORMATS, 0x86A2) +GLENUM(GL_COMPRESSED_TEXTURE_FORMATS, 0x86A3) +GLENUM(GL_BUFFER_SIZE, 0x8764) +GLENUM(GL_BUFFER_USAGE, 0x8765) +GLENUM(GL_POINT_SPRITE_OES, 0x8861) +GLENUM(GL_COORD_REPLACE_OES, 0x8862) +GLENUM(GL_ARRAY_BUFFER, 0x8892) +GLENUM(GL_ELEMENT_ARRAY_BUFFER, 0x8893) +GLENUM(GL_ARRAY_BUFFER_BINDING, 0x8894) +GLENUM(GL_ELEMENT_ARRAY_BUFFER_BINDING, 0x8895) +GLENUM(GL_VERTEX_ARRAY_BUFFER_BINDING, 0x8896) +GLENUM(GL_NORMAL_ARRAY_BUFFER_BINDING, 0x8897) +GLENUM(GL_COLOR_ARRAY_BUFFER_BINDING, 0x8898) +GLENUM(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, 0x889A) +GLENUM(GL_STATIC_DRAW, 0x88E4) +GLENUM(GL_DYNAMIC_DRAW, 0x88E8) +GLENUM(GL_POINT_SIZE_ARRAY_TYPE_OES, 0x898A) +GLENUM(GL_POINT_SIZE_ARRAY_STRIDE_OES, 0x898B) +GLENUM(GL_POINT_SIZE_ARRAY_POINTER_OES, 0x898C) +GLENUM(GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES, 0x898D) +GLENUM(GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES, 0x898E) +GLENUM(GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES, 0x898F) +GLENUM(GL_PALETTE4_RGB8_OES, 0x8B90) +GLENUM(GL_PALETTE4_RGBA8_OES, 0x8B91) +GLENUM(GL_PALETTE4_R5_G6_B5_OES, 0x8B92) +GLENUM(GL_PALETTE4_RGBA4_OES, 0x8B93) +GLENUM(GL_PALETTE4_RGB5_A1_OES, 0x8B94) +GLENUM(GL_PALETTE8_RGB8_OES, 0x8B95) +GLENUM(GL_PALETTE8_RGBA8_OES, 0x8B96) +GLENUM(GL_PALETTE8_R5_G6_B5_OES, 0x8B97) +GLENUM(GL_PALETTE8_RGBA4_OES, 0x8B98) +GLENUM(GL_PALETTE8_RGB5_A1_OES, 0x8B99) +GLENUM(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES, 0x8B9A) +GLENUM(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES, 0x8B9B) +GLENUM(GL_POINT_SIZE_ARRAY_OES, 0x8B9C) +GLENUM(GL_TEXTURE_CROP_RECT_OES, 0x8B9D) +GLENUM(GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES, 0x8B9F) diff --git a/opengl/libs/gl_logger.h b/opengl/libs/gl_logger.h new file mode 100644 index 000000000..ce85dd1ad --- /dev/null +++ b/opengl/libs/gl_logger.h @@ -0,0 +1,26 @@ +/* + ** Copyright 2007, 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. + */ + +#ifndef ANDROID_GL_LOGGER_H +#define ANDROID_GL_LOGGER_H + +namespace android { +#define GL_ENTRY(r, api, ...) r log_##api(__VA_ARGS__); +#include "gl_entries.in" +#undef GL_ENTRY +}; // namespace android + +#endif /* ANDROID_GL_LOGGER_H */ diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h new file mode 100644 index 000000000..63fb01767 --- /dev/null +++ b/opengl/libs/hooks.h @@ -0,0 +1,134 @@ +/* + ** Copyright 2007, 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. + */ + +#ifndef ANDROID_GLES_CM_HOOKS_H +#define ANDROID_GLES_CM_HOOKS_H + +#include +#include +#include + +#include +#include + +#define GL_LOGGER 0 +#if !defined(__arm__) +#define USE_SLOW_BINDING 1 +#else +#define USE_SLOW_BINDING 0 +#endif +#undef NELEM +#define NELEM(x) (sizeof(x)/sizeof(*(x))) +#define MAX_NUMBER_OF_GL_EXTENSIONS 32 + + +#if defined(HAVE_ANDROID_OS) && !USE_SLOW_BINDING && !GL_LOGGER && __OPTIMIZE__ +#define USE_FAST_TLS_KEY 1 +#else +#define USE_FAST_TLS_KEY 0 +#endif + +#if USE_FAST_TLS_KEY +# include /* special private C library header */ +#endif + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +// EGLDisplay are global, not attached to a given thread +const unsigned int NUM_DISPLAYS = 1; + +enum { + IMPL_HARDWARE = 0, + IMPL_SOFTWARE, + IMPL_CONTEXT_LOST, + IMPL_NO_CONTEXT, + + IMPL_NUM_IMPLEMENTATIONS +}; + +// ---------------------------------------------------------------------------- + +// GL / EGL hooks + +#undef GL_ENTRY +#undef EGL_ENTRY +#define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__); +#define EGL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__); + +struct gl_hooks_t { + struct gl_t { + #include "gl_entries.in" + } gl; + struct egl_t { + #include "egl_entries.in" + } egl; + struct gl_ext_t { + void (*extensions[MAX_NUMBER_OF_GL_EXTENSIONS])(void); + } ext; +}; +#undef GL_ENTRY +#undef EGL_ENTRY + + +// ---------------------------------------------------------------------------- + +extern gl_hooks_t gHooks[IMPL_NUM_IMPLEMENTATIONS]; +extern pthread_key_t gGLWrapperKey; + +#if USE_FAST_TLS_KEY + +// We have a dedicated TLS slot in bionic +static inline gl_hooks_t const * volatile * get_tls_hooks() { + volatile void *tls_base = __get_tls(); + gl_hooks_t const * volatile * tls_hooks = + reinterpret_cast(tls_base); + return tls_hooks; +} + +static inline void setGlThreadSpecific(gl_hooks_t const *value) { + gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); + tls_hooks[TLS_SLOT_OPENGL_API] = value; +} + +static gl_hooks_t const* getGlThreadSpecific() { + gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); + gl_hooks_t const* hooks = tls_hooks[TLS_SLOT_OPENGL_API]; + if (hooks) return hooks; + return &gHooks[IMPL_NO_CONTEXT]; +} + +#else + +static inline void setGlThreadSpecific(gl_hooks_t const *value) { + pthread_setspecific(gGLWrapperKey, value); +} + +static gl_hooks_t const* getGlThreadSpecific() { + gl_hooks_t const* hooks = static_cast(pthread_getspecific(gGLWrapperKey)); + if (hooks) return hooks; + return &gHooks[IMPL_NO_CONTEXT]; +} + +#endif + + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +#endif /* ANDROID_GLES_CM_HOOKS_H */ diff --git a/opengl/libs/tools/enumextract.sh b/opengl/libs/tools/enumextract.sh new file mode 100644 index 000000000..570730215 --- /dev/null +++ b/opengl/libs/tools/enumextract.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +awk ' +/^#define GL_/ { + names[count] = $2; + values[count] = $3; + sort[count] = $3 + 0; + count++; +} +END { + for (i = 1; i < count; i++) { + for (j = 0; j < i; j++) { + if (sort[i] < sort[j]) { + tn = names[i]; + tv = values[i]; + ts = sort[i]; + names[i] = names[j]; + values[i] = values[j]; + sort[i] = sort[j]; + names[j] = tn; + values[j] = tv; + sort[j] = ts; + } + } + } + + for (i = 0; i < count; i++) { + printf("GLENUM(%s, %s)\n", names[i], values[i]); + } +} +' < $1 + diff --git a/opengl/tests/Android.mk b/opengl/tests/Android.mk new file mode 100644 index 000000000..5053e7d64 --- /dev/null +++ b/opengl/tests/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/opengl/tests/angeles/Android.mk b/opengl/tests/angeles/Android.mk new file mode 100644 index 000000000..46958d3c3 --- /dev/null +++ b/opengl/tests/angeles/Android.mk @@ -0,0 +1,17 @@ +# Copyright 2006 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= app-linux.c demo.c.arm +LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM libui +LOCAL_MODULE:= angeles +LOCAL_MODULE_TAGS := tests +include $(BUILD_EXECUTABLE) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= gpustate.c +LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM +LOCAL_MODULE:= gpustate +LOCAL_MODULE_TAGS := tests +include $(BUILD_EXECUTABLE) diff --git a/opengl/tests/angeles/MODULE_LICENSE_BSD_OR_LGPL b/opengl/tests/angeles/MODULE_LICENSE_BSD_OR_LGPL new file mode 100644 index 000000000..e69de29bb diff --git a/opengl/tests/angeles/README.txt b/opengl/tests/angeles/README.txt new file mode 100644 index 000000000..38b8a4a74 --- /dev/null +++ b/opengl/tests/angeles/README.txt @@ -0,0 +1,77 @@ +------------------------------------------------------------------------ +San Angeles Observation OpenGL ES version example +Copyright 2004-2005 Jetro Lauha +Web: http://iki.fi/jetro/ +See file license.txt for licensing information. +------------------------------------------------------------------------ + +This is an OpenGL ES port of the small self-running demonstration +called "San Angeles Observation", which was first presented in the +Assembly'2004 event. It won the first place in the 4 KB intro +competition category. + +The demonstration features a sightseeing of a futuristic city +having many different kind of buildings and items. Everything is +flat shaded with three different lights. + +The original version was made for desktop with OpenGL. It was +naturally heavily size optimized in order to fit it in the size +limit. For this OpenGL ES version example much of the code is +cleaned up and the sound is removed. Also detail level is lowered, +although it still contains over 60000 faces. + +The Win32 (2000/XP) binary package of original version is +available from this address: http://jet.ro/files/angeles.zip + +First version of this OpenGL ES port was submitted to the Khronos +OpenGL ES Coding Challenge held in 2004-2005. + +As a code example, this source shows the following: + * How to create a minimal and portable ad hoc framework + for small testing/demonstration programs. This framework + compiles for both desktop and PocketPC Win32 environment, + and a separate source is included for Linux with X11. + * How to dynamically find and use the OpenGL ES DLL or + shared object, so that the library is not needed at + the compile/link stage. + * How to use the basic features of OpenGL ES 1.0/1.1 + Common Lite, such as vertex arrays, color arrays and + lighting. + * How to create a self contained small demonstration + application with objects generated using procedural + algorithms. + +As the original version was optimized for size instead of +performance, that holds true for this OpenGL ES version as +well. Thus the performance could be significantly increased, +for example by changing the code to use glDrawElements +instead of glDrawArrays. The code uses only OpenGL ES 1.0 +Common Lite -level function calls without any extensions. + +The reference OpenGL ES implementations used for this application: + * Hybrid's OpenGL ES API Implementation (Gerbera) version 2.0.4 + Prebuilt Win32 PC executable: SanOGLES-Gerbera.exe + * PowerVR MBX SDK, OpenGL ES Windows PC Emulation version 1.04.14.0170 + Prebuilt Win32 PC executable: SanOGLES-PVRSDK.exe + +Note that DISABLE_IMPORTGL preprocessor macro can be used +to specify not to use dynamic runtime binding of the library. +You also need to define preprocessor macro PVRSDK to compile +the source with PowerVR OpenGL ES SDK. + +The demo application is briefly tested with a few other OpenGL ES +implementations as well (e.g. Vincent, GLESonGL on Linux, Dell +Axim X50v). Most of these other implementations rendered the demo +erroneously in some aspect. This may indicate that the demo source +could still have some work to do with compatibility and correct +API usage, although the non-conforming implementations are most +probably unfinished as well. + +Thanks and Acknowledgements: + +* Toni Lönnberg (!Cube) created the music for original version, which + is not featured in this OpenGL ES port. +* Sara Kapli (st Rana) for additional camera work. +* Paul Bourke for information about the supershapes. + +------------------------------------------------------------------------ diff --git a/opengl/tests/angeles/app-linux.c b/opengl/tests/angeles/app-linux.c new file mode 100644 index 000000000..7d0d320a3 --- /dev/null +++ b/opengl/tests/angeles/app-linux.c @@ -0,0 +1,223 @@ +/* San Angeles Observation OpenGL ES version example + * Copyright 2004-2005 Jetro Lauha + * All rights reserved. + * Web: http://iki.fi/jetro/ + * + * This source is free software; you can redistribute it and/or + * modify it under the terms of EITHER: + * (1) The GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. The text of the GNU Lesser + * General Public License is included with this source in the + * file LICENSE-LGPL.txt. + * (2) The BSD-style license that is included with this source in + * the file LICENSE-BSD.txt. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details. + * + * $Id: app-linux.c,v 1.4 2005/02/08 18:42:48 tonic Exp $ + * $Revision: 1.4 $ + * + * Parts of this source file is based on test/example code from + * GLESonGL implementation by David Blythe. Here is copy of the + * license notice from that source: + * + * Copyright (C) 2003 David Blythe All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DAVID BLYTHE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +#include "app.h" + + +int gAppAlive = 1; + +static const char sAppName[] = + "San Angeles Observation OpenGL ES version example (Linux)"; + +static int sWindowWidth = WINDOW_DEFAULT_WIDTH; +static int sWindowHeight = WINDOW_DEFAULT_HEIGHT; +static EGLDisplay sEglDisplay = EGL_NO_DISPLAY; +static EGLContext sEglContext = EGL_NO_CONTEXT; +static EGLSurface sEglSurface = EGL_NO_SURFACE; + +const char *egl_strerror(unsigned err) +{ + switch(err){ + case EGL_SUCCESS: return "SUCCESS"; + case EGL_NOT_INITIALIZED: return "NOT INITIALIZED"; + case EGL_BAD_ACCESS: return "BAD ACCESS"; + case EGL_BAD_ALLOC: return "BAD ALLOC"; + case EGL_BAD_ATTRIBUTE: return "BAD_ATTRIBUTE"; + case EGL_BAD_CONFIG: return "BAD CONFIG"; + case EGL_BAD_CONTEXT: return "BAD CONTEXT"; + case EGL_BAD_CURRENT_SURFACE: return "BAD CURRENT SURFACE"; + case EGL_BAD_DISPLAY: return "BAD DISPLAY"; + case EGL_BAD_MATCH: return "BAD MATCH"; + case EGL_BAD_NATIVE_PIXMAP: return "BAD NATIVE PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: return "BAD NATIVE WINDOW"; + case EGL_BAD_PARAMETER: return "BAD PARAMETER"; + case EGL_BAD_SURFACE: return "BAD_SURFACE"; +// case EGL_CONTEXT_LOST: return "CONTEXT LOST"; + default: return "UNKNOWN"; + } +} + +void egl_error(const char *name) +{ + unsigned err = eglGetError(); + if(err != EGL_SUCCESS) { + fprintf(stderr,"%s(): egl error 0x%x (%s)\n", + name, err, egl_strerror(err)); + } +} + +static void checkGLErrors() +{ + GLenum error = glGetError(); + if (error != GL_NO_ERROR) + fprintf(stderr, "GL Error: 0x%04x\n", (int)error); +} + + +static void checkEGLErrors() +{ + EGLint error = eglGetError(); + // GLESonGL seems to be returning 0 when there is no errors? + if (error && error != EGL_SUCCESS) + fprintf(stderr, "EGL Error: 0x%04x\n", (int)error); +} + +static int initGraphics() +{ + EGLint s_configAttribs[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + #if 1 + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 0, + #else + EGL_ALPHA_SIZE, EGL_DONT_CARE, + EGL_DEPTH_SIZE, EGL_DONT_CARE, + EGL_STENCIL_SIZE, EGL_DONT_CARE, + EGL_SURFACE_TYPE, EGL_DONT_CARE, + #endif + EGL_NONE + }; + + EGLint numConfigs = -1; + EGLint majorVersion; + EGLint minorVersion; + EGLConfig config; + EGLContext context; + EGLSurface surface; + + EGLDisplay dpy; + + dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + egl_error("eglGetDisplay"); + fprintf(stderr,"dpy = 0x%08x\n", (unsigned) dpy); + + eglInitialize(dpy, &majorVersion, &minorVersion); + egl_error("eglInitialize"); + + eglGetConfigs(dpy, NULL, 0, &numConfigs); + egl_error("eglGetConfigs"); + fprintf(stderr,"num configs %d\n", numConfigs); + + eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs); + egl_error("eglChooseConfig"); + + surface = eglCreateWindowSurface(dpy, config, + android_createDisplaySurface(), NULL); + egl_error("eglMapWindowSurface"); + + fprintf(stderr,"surface = %p\n", surface); + + context = eglCreateContext(dpy, config, NULL, NULL); + egl_error("eglCreateContext"); + fprintf(stderr,"context = %p\n", context); + + eglMakeCurrent(dpy, surface, surface, context); + egl_error("eglMakeCurrent"); + + eglQuerySurface(dpy, surface, EGL_WIDTH, &sWindowWidth); + eglQuerySurface(dpy, surface, EGL_HEIGHT, &sWindowHeight); + + sEglDisplay = dpy; + sEglSurface = surface; + sEglContext = context; + + return EGL_TRUE; +} + + +static void deinitGraphics() +{ + eglMakeCurrent(sEglDisplay, NULL, NULL, NULL); + eglDestroyContext(sEglDisplay, sEglContext); + eglDestroySurface(sEglDisplay, sEglSurface); + eglTerminate(sEglDisplay); +} + + +int main(int argc, char *argv[]) +{ + // not referenced: + argc = argc; + argv = argv; + + if (!initGraphics()) + { + fprintf(stderr, "Graphics initialization failed.\n"); + return EXIT_FAILURE; + } + + appInit(); + + while (gAppAlive) + { + struct timeval timeNow; + + if (gAppAlive) + { + gettimeofday(&timeNow, NULL); + appRender(timeNow.tv_sec * 1000 + timeNow.tv_usec / 1000, + sWindowWidth, sWindowHeight); + checkGLErrors(); + eglSwapBuffers(sEglDisplay, sEglSurface); + checkEGLErrors(); + } + } + + appDeinit(); + deinitGraphics(); + + return EXIT_SUCCESS; +} diff --git a/opengl/tests/angeles/app.h b/opengl/tests/angeles/app.h new file mode 100644 index 000000000..70ebd35be --- /dev/null +++ b/opengl/tests/angeles/app.h @@ -0,0 +1,56 @@ +/* San Angeles Observation OpenGL ES version example + * Copyright 2004-2005 Jetro Lauha + * All rights reserved. + * Web: http://iki.fi/jetro/ + * + * This source is free software; you can redistribute it and/or + * modify it under the terms of EITHER: + * (1) The GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. The text of the GNU Lesser + * General Public License is included with this source in the + * file LICENSE-LGPL.txt. + * (2) The BSD-style license that is included with this source in + * the file LICENSE-BSD.txt. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details. + * + * $Id: app.h,v 1.14 2005/02/06 21:13:54 tonic Exp $ + * $Revision: 1.14 $ + */ + +#ifndef APP_H_INCLUDED +#define APP_H_INCLUDED + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define WINDOW_DEFAULT_WIDTH 640 +#define WINDOW_DEFAULT_HEIGHT 480 + +#define WINDOW_BPP 16 + + +// The simple framework expects the application code to define these functions. +extern void appInit(); +extern void appDeinit(); +extern void appRender(long tick, int width, int height); + +/* Value is non-zero when application is alive, and 0 when it is closing. + * Defined by the application framework. + */ +extern int gAppAlive; + + +#ifdef __cplusplus +} +#endif + + +#endif // !APP_H_INCLUDED diff --git a/opengl/tests/angeles/cams.h b/opengl/tests/angeles/cams.h new file mode 100644 index 000000000..2b1acb3c3 --- /dev/null +++ b/opengl/tests/angeles/cams.h @@ -0,0 +1,65 @@ +/* San Angeles Observation OpenGL ES version example + * Copyright 2004-2005 Jetro Lauha + * All rights reserved. + * Web: http://iki.fi/jetro/ + * + * This source is free software; you can redistribute it and/or + * modify it under the terms of EITHER: + * (1) The GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. The text of the GNU Lesser + * General Public License is included with this source in the + * file LICENSE-LGPL.txt. + * (2) The BSD-style license that is included with this source in + * the file LICENSE-BSD.txt. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details. + * + * $Id: cams.h,v 1.7 2005/01/31 22:15:15 tonic Exp $ + * $Revision: 1.7 $ + */ + +#ifndef CAMS_H_INCLUDED +#define CAMS_H_INCLUDED + + +/* Length in milliseconds of one camera track base unit. + * The value originates from the music synchronization. + */ +#define CAMTRACK_LEN 5442 + + +// Camera track definition for one camera trucking shot. +typedef struct +{ + /* Five parameters of src[5] and dest[5]: + * eyeX, eyeY, eyeZ, viewAngle, viewHeightOffs + */ + short src[5], dest[5]; + unsigned char dist; // if >0, cam rotates around eye xy on dist * 0.1 + unsigned char len; // length multiplier +} CAMTRACK; + +static CAMTRACK sCamTracks[] = +{ + { { 4500, 2700, 100, 70, -30 }, { 50, 50, -90, -100, 0 }, 20, 1 }, + { { -1448, 4294, 25, 363, 0 }, { -136, 202, 125, -98, 100 }, 0, 1 }, + { { 1437, 4930, 200, -275, -20 }, { 1684, 0, 0, 9, 0 }, 0, 1 }, + { { 1800, 3609, 200, 0, 675 }, { 0, 0, 0, 300, 0 }, 0, 1 }, + { { 923, 996, 50, 2336, -80 }, { 0, -20, -50, 0, 170 }, 0, 1 }, + { { -1663, -43, 600, 2170, 0 }, { 20, 0, -600, 0, 100 }, 0, 1 }, + { { 1049, -1420, 175, 2111, -17 }, { 0, 0, 0, -334, 0 }, 0, 2 }, + { { 0, 0, 50, 300, 25 }, { 0, 0, 0, 300, 0 }, 70, 2 }, + { { -473, -953, 3500, -353, -350 }, { 0, 0, -2800, 0, 0 }, 0, 2 }, + { { 191, 1938, 35, 1139, -17 }, { 1205, -2909, 0, 0, 0 }, 0, 2 }, + { { -1449, -2700, 150, 0, 0 }, { 0, 2000, 0, 0, 0 }, 0, 2 }, + { { 5273, 4992, 650, 373, -50 }, { -4598, -3072, 0, 0, 0 }, 0, 2 }, + { { 3223, -3282, 1075, -393, -25 }, { 1649, -1649, 0, 0, 0 }, 0, 2 } +}; +#define CAMTRACK_COUNT (sizeof(camTracks) / sizeof(camTracks[0])) + + +#endif // !CAMS_H_INCLUDED diff --git a/opengl/tests/angeles/demo.c b/opengl/tests/angeles/demo.c new file mode 100644 index 000000000..802f3980f --- /dev/null +++ b/opengl/tests/angeles/demo.c @@ -0,0 +1,792 @@ +/* San Angeles Observation OpenGL ES version example + * Copyright 2004-2005 Jetro Lauha + * All rights reserved. + * Web: http://iki.fi/jetro/ + * + * This source is free software; you can redistribute it and/or + * modify it under the terms of EITHER: + * (1) The GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. The text of the GNU Lesser + * General Public License is included with this source in the + * file LICENSE-LGPL.txt. + * (2) The BSD-style license that is included with this source in + * the file LICENSE-BSD.txt. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details. + * + * $Id: demo.c,v 1.10 2005/02/08 20:54:39 tonic Exp $ + * $Revision: 1.10 $ + */ + +#include +#include +#include +#include + +#include + +#include "app.h" +#include "shapes.h" +#include "cams.h" + + +// Total run length is 20 * camera track base unit length (see cams.h). +#define RUN_LENGTH (20 * CAMTRACK_LEN) +#undef PI +#define PI 3.1415926535897932f +#define RANDOM_UINT_MAX 65535 + + +static unsigned long sRandomSeed = 0; + +static void seedRandom(unsigned long seed) +{ + sRandomSeed = seed; +} + +static unsigned long randomUInt() +{ + sRandomSeed = sRandomSeed * 0x343fd + 0x269ec3; + return sRandomSeed >> 16; +} + + +// Capped conversion from float to fixed. +static long floatToFixed(float value) +{ + if (value < -32768) value = -32768; + if (value > 32767) value = 32767; + return (long)(value * 65536); +} + +#define FIXED(value) floatToFixed(value) + + +// Definition of one GL object in this demo. +typedef struct { + /* Vertex array and color array are enabled for all objects, so their + * pointers must always be valid and non-NULL. Normal array is not + * used by the ground plane, so when its pointer is NULL then normal + * array usage is disabled. + * + * Vertex array is supposed to use GL_FIXED datatype and stride 0 + * (i.e. tightly packed array). Color array is supposed to have 4 + * components per color with GL_UNSIGNED_BYTE datatype and stride 0. + * Normal array is supposed to use GL_FIXED datatype and stride 0. + */ + GLfixed *vertexArray; + GLubyte *colorArray; + GLfixed *normalArray; + GLint vertexComponents; + GLsizei count; +} GLOBJECT; + + +static long sStartTick = 0; +static long sTick = 0; + +static int sCurrentCamTrack = 0; +static long sCurrentCamTrackStartTick = 0; +static long sNextCamTrackStartTick = 0x7fffffff; + +static GLOBJECT *sSuperShapeObjects[SUPERSHAPE_COUNT] = { NULL }; +static GLOBJECT *sGroundPlane = NULL; + + +typedef struct { + float x, y, z; +} VECTOR3; + + +static void freeGLObject(GLOBJECT *object) +{ + if (object == NULL) + return; + free(object->normalArray); + free(object->colorArray); + free(object->vertexArray); + free(object); +} + + +static GLOBJECT * newGLObject(long vertices, int vertexComponents, + int useNormalArray) +{ + GLOBJECT *result; + result = (GLOBJECT *)malloc(sizeof(GLOBJECT)); + if (result == NULL) + return NULL; + result->count = vertices; + result->vertexComponents = vertexComponents; + result->vertexArray = (GLfixed *)malloc(vertices * vertexComponents * + sizeof(GLfixed)); + result->colorArray = (GLubyte *)malloc(vertices * 4 * sizeof(GLubyte)); + if (useNormalArray) + { + result->normalArray = (GLfixed *)malloc(vertices * 3 * + sizeof(GLfixed)); + } + else + result->normalArray = NULL; + if (result->vertexArray == NULL || + result->colorArray == NULL || + (useNormalArray && result->normalArray == NULL)) + { + freeGLObject(result); + return NULL; + } + return result; +} + + +static void drawGLObject(GLOBJECT *object) +{ + assert(object != NULL); + + glVertexPointer(object->vertexComponents, GL_FIXED, + 0, object->vertexArray); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, object->colorArray); + + // Already done in initialization: + //glEnableClientState(GL_VERTEX_ARRAY); + //glEnableClientState(GL_COLOR_ARRAY); + + if (object->normalArray) + { + glNormalPointer(GL_FIXED, 0, object->normalArray); + glEnableClientState(GL_NORMAL_ARRAY); + } + else + glDisableClientState(GL_NORMAL_ARRAY); + glDrawArrays(GL_TRIANGLES, 0, object->count); +} + + +static void vector3Sub(VECTOR3 *dest, VECTOR3 *v1, VECTOR3 *v2) +{ + dest->x = v1->x - v2->x; + dest->y = v1->y - v2->y; + dest->z = v1->z - v2->z; +} + + +static void superShapeMap(VECTOR3 *point, float r1, float r2, float t, float p) +{ + // sphere-mapping of supershape parameters + point->x = (float)(cos(t) * cos(p) / r1 / r2); + point->y = (float)(sin(t) * cos(p) / r1 / r2); + point->z = (float)(sin(p) / r2); +} + + +static float ssFunc(const float t, const float *p) +{ + return (float)(pow(pow(fabs(cos(p[0] * t / 4)) / p[1], p[4]) + + pow(fabs(sin(p[0] * t / 4)) / p[2], p[5]), 1 / p[3])); +} + + +// Creates and returns a supershape object. +// Based on Paul Bourke's POV-Ray implementation. +// http://astronomy.swin.edu.au/~pbourke/povray/supershape/ +static GLOBJECT * createSuperShape(const float *params) +{ + const int resol1 = (int)params[SUPERSHAPE_PARAMS - 3]; + const int resol2 = (int)params[SUPERSHAPE_PARAMS - 2]; + // latitude 0 to pi/2 for no mirrored bottom + // (latitudeBegin==0 for -pi/2 to pi/2 originally) + const int latitudeBegin = resol2 / 4; + const int latitudeEnd = resol2 / 2; // non-inclusive + const int longitudeCount = resol1; + const int latitudeCount = latitudeEnd - latitudeBegin; + const long triangleCount = longitudeCount * latitudeCount * 2; + const long vertices = triangleCount * 3; + GLOBJECT *result; + float baseColor[3]; + int a, longitude, latitude; + long currentVertex, currentQuad; + + result = newGLObject(vertices, 3, 1); + if (result == NULL) + return NULL; + + for (a = 0; a < 3; ++a) + baseColor[a] = ((randomUInt() % 155) + 100) / 255.f; + + currentQuad = 0; + currentVertex = 0; + + // longitude -pi to pi + for (longitude = 0; longitude < longitudeCount; ++longitude) + { + + // latitude 0 to pi/2 + for (latitude = latitudeBegin; latitude < latitudeEnd; ++latitude) + { + float t1 = -PI + longitude * 2 * PI / resol1; + float t2 = -PI + (longitude + 1) * 2 * PI / resol1; + float p1 = -PI / 2 + latitude * 2 * PI / resol2; + float p2 = -PI / 2 + (latitude + 1) * 2 * PI / resol2; + float r0, r1, r2, r3; + + r0 = ssFunc(t1, params); + r1 = ssFunc(p1, ¶ms[6]); + r2 = ssFunc(t2, params); + r3 = ssFunc(p2, ¶ms[6]); + + if (r0 != 0 && r1 != 0 && r2 != 0 && r3 != 0) + { + VECTOR3 pa, pb, pc, pd; + VECTOR3 v1, v2, n; + float ca; + int i; + //float lenSq, invLenSq; + + superShapeMap(&pa, r0, r1, t1, p1); + superShapeMap(&pb, r2, r1, t2, p1); + superShapeMap(&pc, r2, r3, t2, p2); + superShapeMap(&pd, r0, r3, t1, p2); + + // kludge to set lower edge of the object to fixed level + if (latitude == latitudeBegin + 1) + pa.z = pb.z = 0; + + vector3Sub(&v1, &pb, &pa); + vector3Sub(&v2, &pd, &pa); + + // Calculate normal with cross product. + /* i j k i j + * v1.x v1.y v1.z | v1.x v1.y + * v2.x v2.y v2.z | v2.x v2.y + */ + + n.x = v1.y * v2.z - v1.z * v2.y; + n.y = v1.z * v2.x - v1.x * v2.z; + n.z = v1.x * v2.y - v1.y * v2.x; + + /* Pre-normalization of the normals is disabled here because + * they will be normalized anyway later due to automatic + * normalization (GL_NORMALIZE). It is enabled because the + * objects are scaled with glScale. + */ + /* + lenSq = n.x * n.x + n.y * n.y + n.z * n.z; + invLenSq = (float)(1 / sqrt(lenSq)); + n.x *= invLenSq; + n.y *= invLenSq; + n.z *= invLenSq; + */ + + ca = pa.z + 0.5f; + + for (i = currentVertex * 3; + i < (currentVertex + 6) * 3; + i += 3) + { + result->normalArray[i] = FIXED(n.x); + result->normalArray[i + 1] = FIXED(n.y); + result->normalArray[i + 2] = FIXED(n.z); + } + for (i = currentVertex * 4; + i < (currentVertex + 6) * 4; + i += 4) + { + int a, color[3]; + for (a = 0; a < 3; ++a) + { + color[a] = (int)(ca * baseColor[a] * 255); + if (color[a] > 255) color[a] = 255; + } + result->colorArray[i] = (GLubyte)color[0]; + result->colorArray[i + 1] = (GLubyte)color[1]; + result->colorArray[i + 2] = (GLubyte)color[2]; + result->colorArray[i + 3] = 0; + } + result->vertexArray[currentVertex * 3] = FIXED(pa.x); + result->vertexArray[currentVertex * 3 + 1] = FIXED(pa.y); + result->vertexArray[currentVertex * 3 + 2] = FIXED(pa.z); + ++currentVertex; + result->vertexArray[currentVertex * 3] = FIXED(pb.x); + result->vertexArray[currentVertex * 3 + 1] = FIXED(pb.y); + result->vertexArray[currentVertex * 3 + 2] = FIXED(pb.z); + ++currentVertex; + result->vertexArray[currentVertex * 3] = FIXED(pd.x); + result->vertexArray[currentVertex * 3 + 1] = FIXED(pd.y); + result->vertexArray[currentVertex * 3 + 2] = FIXED(pd.z); + ++currentVertex; + result->vertexArray[currentVertex * 3] = FIXED(pb.x); + result->vertexArray[currentVertex * 3 + 1] = FIXED(pb.y); + result->vertexArray[currentVertex * 3 + 2] = FIXED(pb.z); + ++currentVertex; + result->vertexArray[currentVertex * 3] = FIXED(pc.x); + result->vertexArray[currentVertex * 3 + 1] = FIXED(pc.y); + result->vertexArray[currentVertex * 3 + 2] = FIXED(pc.z); + ++currentVertex; + result->vertexArray[currentVertex * 3] = FIXED(pd.x); + result->vertexArray[currentVertex * 3 + 1] = FIXED(pd.y); + result->vertexArray[currentVertex * 3 + 2] = FIXED(pd.z); + ++currentVertex; + } // r0 && r1 && r2 && r3 + ++currentQuad; + } // latitude + } // longitude + + // Set number of vertices in object to the actual amount created. + result->count = currentVertex; + + return result; +} + + +static GLOBJECT * createGroundPlane() +{ + const int scale = 4; + const int yBegin = -15, yEnd = 15; // ends are non-inclusive + const int xBegin = -15, xEnd = 15; + const long triangleCount = (yEnd - yBegin) * (xEnd - xBegin) * 2; + const long vertices = triangleCount * 3; + GLOBJECT *result; + int x, y; + long currentVertex, currentQuad; + + result = newGLObject(vertices, 2, 0); + if (result == NULL) + return NULL; + + currentQuad = 0; + currentVertex = 0; + + for (y = yBegin; y < yEnd; ++y) + { + for (x = xBegin; x < xEnd; ++x) + { + GLubyte color; + int i, a; + color = (GLubyte)((randomUInt() & 0x5f) + 81); // 101 1111 + for (i = currentVertex * 4; i < (currentVertex + 6) * 4; i += 4) + { + result->colorArray[i] = color; + result->colorArray[i + 1] = color; + result->colorArray[i + 2] = color; + result->colorArray[i + 3] = 0; + } + + // Axis bits for quad triangles: + // x: 011100 (0x1c), y: 110001 (0x31) (clockwise) + // x: 001110 (0x0e), y: 100011 (0x23) (counter-clockwise) + for (a = 0; a < 6; ++a) + { + const int xm = x + ((0x1c >> a) & 1); + const int ym = y + ((0x31 >> a) & 1); + const float m = (float)(cos(xm * 2) * sin(ym * 4) * 0.75f); + result->vertexArray[currentVertex * 2] = + FIXED(xm * scale + m); + result->vertexArray[currentVertex * 2 + 1] = + FIXED(ym * scale + m); + ++currentVertex; + } + ++currentQuad; + } + } + return result; +} + + +static void drawGroundPlane() +{ + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_ZERO, GL_SRC_COLOR); + glDisable(GL_LIGHTING); + + drawGLObject(sGroundPlane); + + glEnable(GL_LIGHTING); + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); +} + + +static void drawFadeQuad() +{ + static const GLfixed quadVertices[] = { + -0x10000, -0x10000, + 0x10000, -0x10000, + -0x10000, 0x10000, + 0x10000, -0x10000, + 0x10000, 0x10000, + -0x10000, 0x10000 + }; + + const int beginFade = sTick - sCurrentCamTrackStartTick; + const int endFade = sNextCamTrackStartTick - sTick; + const int minFade = beginFade < endFade ? beginFade : endFade; + + if (minFade < 1024) + { + const GLfixed fadeColor = minFade << 6; + glColor4x(fadeColor, fadeColor, fadeColor, 0); + + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_ZERO, GL_SRC_COLOR); + glDisable(GL_LIGHTING); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glVertexPointer(2, GL_FIXED, 0, quadVertices); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glEnableClientState(GL_COLOR_ARRAY); + + glMatrixMode(GL_MODELVIEW); + + glEnable(GL_LIGHTING); + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + } +} + + +// Called from the app framework. +void appInit() +{ + int a; + + glEnable(GL_NORMALIZE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glShadeModel(GL_FLAT); + + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + glEnable(GL_LIGHT2); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + seedRandom(15); + + for (a = 0; a < SUPERSHAPE_COUNT; ++a) + { + sSuperShapeObjects[a] = createSuperShape(sSuperShapeParams[a]); + assert(sSuperShapeObjects[a] != NULL); + } + sGroundPlane = createGroundPlane(); + assert(sGroundPlane != NULL); +} + + +// Called from the app framework. +void appDeinit() +{ + int a; + for (a = 0; a < SUPERSHAPE_COUNT; ++a) + freeGLObject(sSuperShapeObjects[a]); + freeGLObject(sGroundPlane); +} + + +static void gluPerspective(GLfloat fovy, GLfloat aspect, + GLfloat zNear, GLfloat zFar) +{ + GLfloat xmin, xmax, ymin, ymax; + + ymax = zNear * (GLfloat)tan(fovy * PI / 360); + ymin = -ymax; + xmin = ymin * aspect; + xmax = ymax * aspect; + + glFrustumx((GLfixed)(xmin * 65536), (GLfixed)(xmax * 65536), + (GLfixed)(ymin * 65536), (GLfixed)(ymax * 65536), + (GLfixed)(zNear * 65536), (GLfixed)(zFar * 65536)); +} + + +static void prepareFrame(int width, int height) +{ + glViewport(0, 0, width, height); + + glClearColorx((GLfixed)(0.1f * 65536), + (GLfixed)(0.2f * 65536), + (GLfixed)(0.3f * 65536), 0x10000); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45, (float)width / height, 0.5f, 150); + + glMatrixMode(GL_MODELVIEW); + + glLoadIdentity(); +} + + +static void configureLightAndMaterial() +{ + static GLfixed light0Position[] = { -0x40000, 0x10000, 0x10000, 0 }; + static GLfixed light0Diffuse[] = { 0x10000, 0x6666, 0, 0x10000 }; + static GLfixed light1Position[] = { 0x10000, -0x20000, -0x10000, 0 }; + static GLfixed light1Diffuse[] = { 0x11eb, 0x23d7, 0x5999, 0x10000 }; + static GLfixed light2Position[] = { -0x10000, 0, -0x40000, 0 }; + static GLfixed light2Diffuse[] = { 0x11eb, 0x2b85, 0x23d7, 0x10000 }; + static GLfixed materialSpecular[] = { 0x10000, 0x10000, 0x10000, 0x10000 }; + + glLightxv(GL_LIGHT0, GL_POSITION, light0Position); + glLightxv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse); + glLightxv(GL_LIGHT1, GL_POSITION, light1Position); + glLightxv(GL_LIGHT1, GL_DIFFUSE, light1Diffuse); + glLightxv(GL_LIGHT2, GL_POSITION, light2Position); + glLightxv(GL_LIGHT2, GL_DIFFUSE, light2Diffuse); + glMaterialxv(GL_FRONT_AND_BACK, GL_SPECULAR, materialSpecular); + + glMaterialx(GL_FRONT_AND_BACK, GL_SHININESS, 60 << 16); + glEnable(GL_COLOR_MATERIAL); +} + + +static void drawModels(float zScale) +{ + const int translationScale = 9; + int x, y; + + seedRandom(9); + + glScalex(1 << 16, 1 << 16, (GLfixed)(zScale * 65536)); + + for (y = -5; y <= 5; ++y) + { + for (x = -5; x <= 5; ++x) + { + float buildingScale; + GLfixed fixedScale; + + int curShape = randomUInt() % SUPERSHAPE_COUNT; + buildingScale = sSuperShapeParams[curShape][SUPERSHAPE_PARAMS - 1]; + fixedScale = (GLfixed)(buildingScale * 65536); + + glPushMatrix(); + glTranslatex((x * translationScale) * 65536, + (y * translationScale) * 65536, + 0); + glRotatex((GLfixed)((randomUInt() % 360) << 16), 0, 0, 1 << 16); + glScalex(fixedScale, fixedScale, fixedScale); + + drawGLObject(sSuperShapeObjects[curShape]); + glPopMatrix(); + } + } + + for (x = -2; x <= 2; ++x) + { + const int shipScale100 = translationScale * 500; + const int offs100 = x * shipScale100 + (sTick % shipScale100); + float offs = offs100 * 0.01f; + GLfixed fixedOffs = (GLfixed)(offs * 65536); + glPushMatrix(); + glTranslatex(fixedOffs, -4 * 65536, 2 << 16); + drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]); + glPopMatrix(); + glPushMatrix(); + glTranslatex(-4 * 65536, fixedOffs, 4 << 16); + glRotatex(90 << 16, 0, 0, 1 << 16); + drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]); + glPopMatrix(); + } +} + + +/* Following gluLookAt implementation is adapted from the + * Mesa 3D Graphics library. http://www.mesa3d.org + */ +static void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez, + GLfloat centerx, GLfloat centery, GLfloat centerz, + GLfloat upx, GLfloat upy, GLfloat upz) +{ + GLfloat m[16]; + GLfloat x[3], y[3], z[3]; + GLfloat mag; + + /* Make rotation matrix */ + + /* Z vector */ + z[0] = eyex - centerx; + z[1] = eyey - centery; + z[2] = eyez - centerz; + mag = (float)sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]); + if (mag) { /* mpichler, 19950515 */ + z[0] /= mag; + z[1] /= mag; + z[2] /= mag; + } + + /* Y vector */ + y[0] = upx; + y[1] = upy; + y[2] = upz; + + /* X vector = Y cross Z */ + x[0] = y[1] * z[2] - y[2] * z[1]; + x[1] = -y[0] * z[2] + y[2] * z[0]; + x[2] = y[0] * z[1] - y[1] * z[0]; + + /* Recompute Y = Z cross X */ + y[0] = z[1] * x[2] - z[2] * x[1]; + y[1] = -z[0] * x[2] + z[2] * x[0]; + y[2] = z[0] * x[1] - z[1] * x[0]; + + /* mpichler, 19950515 */ + /* cross product gives area of parallelogram, which is < 1.0 for + * non-perpendicular unit-length vectors; so normalize x, y here + */ + + mag = (float)sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); + if (mag) { + x[0] /= mag; + x[1] /= mag; + x[2] /= mag; + } + + mag = (float)sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]); + if (mag) { + y[0] /= mag; + y[1] /= mag; + y[2] /= mag; + } + +#define M(row,col) m[col*4+row] + M(0, 0) = x[0]; + M(0, 1) = x[1]; + M(0, 2) = x[2]; + M(0, 3) = 0.0; + M(1, 0) = y[0]; + M(1, 1) = y[1]; + M(1, 2) = y[2]; + M(1, 3) = 0.0; + M(2, 0) = z[0]; + M(2, 1) = z[1]; + M(2, 2) = z[2]; + M(2, 3) = 0.0; + M(3, 0) = 0.0; + M(3, 1) = 0.0; + M(3, 2) = 0.0; + M(3, 3) = 1.0; +#undef M + { + int a; + GLfixed fixedM[16]; + for (a = 0; a < 16; ++a) + fixedM[a] = (GLfixed)(m[a] * 65536); + glMultMatrixx(fixedM); + } + + /* Translate Eye to Origin */ + glTranslatex((GLfixed)(-eyex * 65536), + (GLfixed)(-eyey * 65536), + (GLfixed)(-eyez * 65536)); +} + + +static void camTrack() +{ + float lerp[5]; + float eX, eY, eZ, cX, cY, cZ; + float trackPos; + CAMTRACK *cam; + long currentCamTick; + int a; + + if (sNextCamTrackStartTick <= sTick) + { + ++sCurrentCamTrack; + sCurrentCamTrackStartTick = sNextCamTrackStartTick; + } + sNextCamTrackStartTick = sCurrentCamTrackStartTick + + sCamTracks[sCurrentCamTrack].len * CAMTRACK_LEN; + + cam = &sCamTracks[sCurrentCamTrack]; + currentCamTick = sTick - sCurrentCamTrackStartTick; + trackPos = (float)currentCamTick / (CAMTRACK_LEN * cam->len); + + for (a = 0; a < 5; ++a) + lerp[a] = (cam->src[a] + cam->dest[a] * trackPos) * 0.01f; + + if (cam->dist) + { + float dist = cam->dist * 0.1f; + cX = lerp[0]; + cY = lerp[1]; + cZ = lerp[2]; + eX = cX - (float)cos(lerp[3]) * dist; + eY = cY - (float)sin(lerp[3]) * dist; + eZ = cZ - lerp[4]; + } + else + { + eX = lerp[0]; + eY = lerp[1]; + eZ = lerp[2]; + cX = eX + (float)cos(lerp[3]); + cY = eY + (float)sin(lerp[3]); + cZ = eZ + lerp[4]; + } + gluLookAt(eX, eY, eZ, cX, cY, cZ, 0, 0, 1); +} + + +// Called from the app framework. +/* The tick is current time in milliseconds, width and height + * are the image dimensions to be rendered. + */ +void appRender(long tick, int width, int height) +{ + if (sStartTick == 0) + sStartTick = tick; + if (!gAppAlive) + return; + + // Actual tick value is "blurred" a little bit. + sTick = (sTick + tick - sStartTick) >> 1; + + // Terminate application after running through the demonstration once. + if (sTick >= RUN_LENGTH) + { + gAppAlive = 0; + return; + } + + // Prepare OpenGL ES for rendering of the frame. + prepareFrame(width, height); + + // Update the camera position and set the lookat. + camTrack(); + + // Configure environment. + configureLightAndMaterial(); + + // Draw the reflection by drawing models with negated Z-axis. + glPushMatrix(); + drawModels(-1); + glPopMatrix(); + + // Blend the ground plane to the window. + drawGroundPlane(); + + // Draw all the models normally. + drawModels(1); + + // Draw fade quad over whole window (when changing cameras). + drawFadeQuad(); +} diff --git a/opengl/tests/angeles/gpustate.c b/opengl/tests/angeles/gpustate.c new file mode 100644 index 000000000..3c540c9ca --- /dev/null +++ b/opengl/tests/angeles/gpustate.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include + +static void *map_memory(const char *fn, unsigned base, unsigned size) +{ + int fd; + void *ptr; + + fd = open(fn, O_RDWR | O_SYNC); + if(fd < 0) { + perror("cannot open %s for mapping"); + return MAP_FAILED; + } + + ptr = mmap(0, size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, base); + close(fd); + + if(ptr == MAP_FAILED) { + fprintf(stderr,"cannot map %s (@%08x,%08x)\n", fn, base, size); + } + return ptr; +} + + +int main(int argc, char** argv) +{ + void *grp_regs = map_memory("/dev/hw3d", 0, 1024 * 1024); + printf("GPU base mapped at %p\n", grp_regs); + int state_offset = 0x10140; + printf("GPU state = %08lx\n", + *((long*)((char*)grp_regs + state_offset)) ); + + return 0; +} diff --git a/opengl/tests/angeles/include/GLES/egl.h b/opengl/tests/angeles/include/GLES/egl.h new file mode 100644 index 000000000..cdf841083 --- /dev/null +++ b/opengl/tests/angeles/include/GLES/egl.h @@ -0,0 +1,229 @@ +#ifndef __egl_h_ +#define __egl_h_ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.0 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2004 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +*/ + +#include +#include + +/* +** egltypes.h is platform dependent. It defines: +** +** - EGL types and resources +** - Native types +** - EGL and native handle values +** +** EGL types and resources are to be typedef'ed with appropriate platform +** dependent resource handle types. EGLint must be an integer of at least +** 32-bit. +** +** NativeDisplayType, NativeWindowType and NativePixmapType are to be +** replaced with corresponding types of the native window system in egl.h. +** +** EGL and native handle values must match their types. +** +** Example egltypes.h: +*/ + +#if 0 + +#include +#include + +/* +** Types and resources +*/ +typedef int EGLBoolean; +typedef int32_t EGLint; +typedef void *EGLDisplay; +typedef void *EGLConfig; +typedef void *EGLSurface; +typedef void *EGLContext; + +/* +** EGL and native handle values +*/ +#define EGL_DEFAULT_DISPLAY ((NativeDisplayType)0) +#define EGL_NO_CONTEXT ((EGLContext)0) +#define EGL_NO_DISPLAY ((EGLDisplay)0) +#define EGL_NO_SURFACE ((EGLSurface)0) + +#endif + +/* +** Versioning and extensions +*/ +#define EGL_VERSION_1_0 1 + +/* +** Boolean +*/ +#define EGL_FALSE 0 +#define EGL_TRUE 1 + +/* +** Errors +*/ +#define EGL_SUCCESS 0x3000 +#define EGL_NOT_INITIALIZED 0x3001 +#define EGL_BAD_ACCESS 0x3002 +#define EGL_BAD_ALLOC 0x3003 +#define EGL_BAD_ATTRIBUTE 0x3004 +#define EGL_BAD_CONFIG 0x3005 +#define EGL_BAD_CONTEXT 0x3006 +#define EGL_BAD_CURRENT_SURFACE 0x3007 +#define EGL_BAD_DISPLAY 0x3008 +#define EGL_BAD_MATCH 0x3009 +#define EGL_BAD_NATIVE_PIXMAP 0x300A +#define EGL_BAD_NATIVE_WINDOW 0x300B +#define EGL_BAD_PARAMETER 0x300C +#define EGL_BAD_SURFACE 0x300D +/* 0x300E - 0x301F reserved for additional errors. */ + +/* +** Config attributes +*/ +#define EGL_BUFFER_SIZE 0x3020 +#define EGL_ALPHA_SIZE 0x3021 +#define EGL_BLUE_SIZE 0x3022 +#define EGL_GREEN_SIZE 0x3023 +#define EGL_RED_SIZE 0x3024 +#define EGL_DEPTH_SIZE 0x3025 +#define EGL_STENCIL_SIZE 0x3026 +#define EGL_CONFIG_CAVEAT 0x3027 +#define EGL_CONFIG_ID 0x3028 +#define EGL_LEVEL 0x3029 +#define EGL_MAX_PBUFFER_HEIGHT 0x302A +#define EGL_MAX_PBUFFER_PIXELS 0x302B +#define EGL_MAX_PBUFFER_WIDTH 0x302C +#define EGL_NATIVE_RENDERABLE 0x302D +#define EGL_NATIVE_VISUAL_ID 0x302E +#define EGL_NATIVE_VISUAL_TYPE 0x302F +/*#define EGL_PRESERVED_RESOURCES 0x3030*/ +#define EGL_SAMPLES 0x3031 +#define EGL_SAMPLE_BUFFERS 0x3032 +#define EGL_SURFACE_TYPE 0x3033 +#define EGL_TRANSPARENT_TYPE 0x3034 +#define EGL_TRANSPARENT_BLUE_VALUE 0x3035 +#define EGL_TRANSPARENT_GREEN_VALUE 0x3036 +#define EGL_TRANSPARENT_RED_VALUE 0x3037 + +/* +** Config attribute and value +*/ +#define EGL_NONE 0x3038 +/* 0x3039 - 0x304F reserved for additional config attributes. */ + +/* +** Config values +*/ +#define EGL_DONT_CARE ((EGLint) -1) +#define EGL_PBUFFER_BIT 0x01 +#define EGL_PIXMAP_BIT 0x02 +#define EGL_WINDOW_BIT 0x04 +#define EGL_SLOW_CONFIG 0x3050 +#define EGL_NON_CONFORMANT_CONFIG 0x3051 +#define EGL_TRANSPARENT_RGB 0x3052 + +/* +** String names +*/ +#define EGL_VENDOR 0x3053 +#define EGL_VERSION 0x3054 +#define EGL_EXTENSIONS 0x3055 + +/* +** Surface attributes +*/ +#define EGL_HEIGHT 0x3056 +#define EGL_WIDTH 0x3057 +#define EGL_LARGEST_PBUFFER 0x3058 + +/* +** Current surfaces +*/ +#define EGL_DRAW 0x3059 +#define EGL_READ 0x305A + +/* +** Engines +*/ +#define EGL_CORE_NATIVE_ENGINE 0x305B + +/* 0x305C-0x3FFFF reserved for future use */ + +/* +** Functions +*/ +#ifdef __cplusplus +extern "C" { +#endif + +GLAPI EGLint APIENTRY eglGetError (void); + +GLAPI EGLDisplay APIENTRY eglGetDisplay (NativeDisplayType display); +GLAPI EGLBoolean APIENTRY eglInitialize (EGLDisplay dpy, EGLint *major, EGLint *minor); +GLAPI EGLBoolean APIENTRY eglTerminate (EGLDisplay dpy); +GLAPI const char * APIENTRY eglQueryString (EGLDisplay dpy, EGLint name); +GLAPI void (* APIENTRY eglGetProcAddress (const char *procname))(); + +GLAPI EGLBoolean APIENTRY eglGetConfigs (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config); +GLAPI EGLBoolean APIENTRY eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config); +GLAPI EGLBoolean APIENTRY eglGetConfigAttrib (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value); + +GLAPI EGLSurface APIENTRY eglCreateWindowSurface (EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list); +GLAPI EGLSurface APIENTRY eglCreatePixmapSurface (EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap, const EGLint *attrib_list); +GLAPI EGLSurface APIENTRY eglCreatePbufferSurface (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list); +GLAPI EGLBoolean APIENTRY eglDestroySurface (EGLDisplay dpy, EGLSurface surface); +GLAPI EGLBoolean APIENTRY eglQuerySurface (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value); + +GLAPI EGLContext APIENTRY eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list); +GLAPI EGLBoolean APIENTRY eglDestroyContext (EGLDisplay dpy, EGLContext ctx); +GLAPI EGLBoolean APIENTRY eglMakeCurrent (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); +GLAPI EGLContext APIENTRY eglGetCurrentContext (void); +GLAPI EGLSurface APIENTRY eglGetCurrentSurface (EGLint readdraw); +GLAPI EGLDisplay APIENTRY eglGetCurrentDisplay (void); +GLAPI EGLBoolean APIENTRY eglQueryContext (EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value); + +GLAPI EGLBoolean APIENTRY eglWaitGL (void); +GLAPI EGLBoolean APIENTRY eglWaitNative (EGLint engine); +GLAPI EGLBoolean APIENTRY eglSwapBuffers (EGLDisplay dpy, EGLSurface draw); +GLAPI EGLBoolean APIENTRY eglCopyBuffers (EGLDisplay dpy, EGLSurface surface, NativePixmapType target); + +#ifdef __cplusplus +} +#endif + +#endif /* ___egl_h_ */ diff --git a/opengl/tests/angeles/include/GLES/egltypes.h b/opengl/tests/angeles/include/GLES/egltypes.h new file mode 100644 index 000000000..9db36c987 --- /dev/null +++ b/opengl/tests/angeles/include/GLES/egltypes.h @@ -0,0 +1,20 @@ +/* +** Types and resources +*/ +typedef int EGLBoolean; +typedef long EGLint; +typedef void *EGLDisplay; +typedef void *EGLConfig; +typedef void *EGLSurface; +typedef void *EGLContext; +typedef void *NativeDisplayType; +typedef void *NativeWindowType; +typedef void *NativePixmapType; + +/* +** EGL and native handle values +*/ +#define EGL_DEFAULT_DISPLAY ((NativeDisplayType)0) +#define EGL_NO_CONTEXT ((EGLContext)0) +#define EGL_NO_DISPLAY ((EGLDisplay)0) +#define EGL_NO_SURFACE ((EGLSurface)0) diff --git a/opengl/tests/angeles/include/GLES/gl.h b/opengl/tests/angeles/include/GLES/gl.h new file mode 100644 index 000000000..415482228 --- /dev/null +++ b/opengl/tests/angeles/include/GLES/gl.h @@ -0,0 +1,584 @@ +#ifndef __gl_h_ +#define __gl_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.0 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) +#define WIN32_LEAN_AND_MEAN 1 +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef signed char GLbyte; +typedef short GLshort; +typedef int GLint; +typedef int GLsizei; +typedef unsigned char GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef float GLfloat; +typedef float GLclampf; +typedef void GLvoid; +typedef int GLintptrARB; +typedef int GLsizeiptrARB; +typedef int GLfixed; +typedef int GLclampx; +/* Internal convenience typedefs */ +typedef void (*_GLfuncptr)(); + +/*************************************************************/ + +/* Extensions */ +#define GL_OES_VERSION_1_0 1 +#define GL_OES_read_format 1 +#define GL_OES_compressed_paletted_texture 1 + +/* ClearBufferMask */ +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 + +/* Boolean */ +#define GL_FALSE 0 +#define GL_TRUE 1 + +/* BeginMode */ +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 + +/* AlphaFunction */ +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 + +/* BlendingFactorDest */ +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 + +/* BlendingFactorSrc */ +/* GL_ZERO */ +/* GL_ONE */ +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +/* GL_SRC_ALPHA */ +/* GL_ONE_MINUS_SRC_ALPHA */ +/* GL_DST_ALPHA */ +/* GL_ONE_MINUS_DST_ALPHA */ + +/* ColorMaterialFace */ +/* GL_FRONT_AND_BACK */ + +/* ColorMaterialParameter */ +/* GL_AMBIENT_AND_DIFFUSE */ + +/* ColorPointerType */ +/* GL_UNSIGNED_BYTE */ +/* GL_FLOAT */ +/* GL_FIXED */ + +/* CullFaceMode */ +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_FRONT_AND_BACK 0x0408 + +/* DepthFunction */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* EnableCap */ +#define GL_FOG 0x0B60 +#define GL_LIGHTING 0x0B50 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_CULL_FACE 0x0B44 +#define GL_ALPHA_TEST 0x0BC0 +#define GL_BLEND 0x0BE2 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_DITHER 0x0BD0 +#define GL_STENCIL_TEST 0x0B90 +#define GL_DEPTH_TEST 0x0B71 +/* GL_LIGHT0 */ +/* GL_LIGHT1 */ +/* GL_LIGHT2 */ +/* GL_LIGHT3 */ +/* GL_LIGHT4 */ +/* GL_LIGHT5 */ +/* GL_LIGHT6 */ +/* GL_LIGHT7 */ +#define GL_POINT_SMOOTH 0x0B10 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_MATERIAL 0x0B57 +#define GL_NORMALIZE 0x0BA1 +#define GL_RESCALE_NORMAL 0x803A +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_COLOR_ARRAY 0x8076 +#define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 + +/* ErrorCode */ +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 + +/* FogMode */ +/* GL_LINEAR */ +#define GL_EXP 0x0800 +#define GL_EXP2 0x0801 + +/* FogParameter */ +#define GL_FOG_DENSITY 0x0B62 +#define GL_FOG_START 0x0B63 +#define GL_FOG_END 0x0B64 +#define GL_FOG_MODE 0x0B65 +#define GL_FOG_COLOR 0x0B66 + +/* FrontFaceDirection */ +#define GL_CW 0x0900 +#define GL_CCW 0x0901 + +/* GetPName */ +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#define GL_MAX_LIGHTS 0x0D31 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 +#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 +#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_RED_BITS 0x0D52 +#define GL_GREEN_BITS 0x0D53 +#define GL_BLUE_BITS 0x0D54 +#define GL_ALPHA_BITS 0x0D55 +#define GL_DEPTH_BITS 0x0D56 +#define GL_STENCIL_BITS 0x0D57 + +/* HintMode */ +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 + +/* HintTarget */ +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_FOG_HINT 0x0C54 + +/* LightModelParameter */ +#define GL_LIGHT_MODEL_AMBIENT 0x0B53 +#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 + +/* LightParameter */ +#define GL_AMBIENT 0x1200 +#define GL_DIFFUSE 0x1201 +#define GL_SPECULAR 0x1202 +#define GL_POSITION 0x1203 +#define GL_SPOT_DIRECTION 0x1204 +#define GL_SPOT_EXPONENT 0x1205 +#define GL_SPOT_CUTOFF 0x1206 +#define GL_CONSTANT_ATTENUATION 0x1207 +#define GL_LINEAR_ATTENUATION 0x1208 +#define GL_QUADRATIC_ATTENUATION 0x1209 + +/* DataType */ +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_FLOAT 0x1406 +#define GL_FIXED 0x140C + +/* LogicOp */ +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F + +/* MaterialFace */ +/* GL_FRONT_AND_BACK */ + +/* MaterialParameter */ +#define GL_EMISSION 0x1600 +#define GL_SHININESS 0x1601 +#define GL_AMBIENT_AND_DIFFUSE 0x1602 +/* GL_AMBIENT */ +/* GL_DIFFUSE */ +/* GL_SPECULAR */ + +/* MatrixMode */ +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 + +/* NormalPointerType */ +/* GL_BYTE */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ + +/* PixelFormat */ +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A + +/* PixelStoreParameter */ +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 + +/* PixelType */ +/* GL_UNSIGNED_BYTE */ +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 + +/* ShadingModel */ +#define GL_FLAT 0x1D00 +#define GL_SMOOTH 0x1D01 + +/* StencilFunction */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* StencilOp */ +/* GL_ZERO */ +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +/* GL_INVERT */ + +/* StringName */ +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +/* TexCoordPointerType */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ +/* GL_BYTE */ + +/* TextureEnvMode */ +#define GL_MODULATE 0x2100 +#define GL_DECAL 0x2101 +/* GL_BLEND */ +#define GL_ADD 0x0104 +/* GL_REPLACE */ + +/* TextureEnvParameter */ +#define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_ENV_COLOR 0x2201 + +/* TextureEnvTarget */ +#define GL_TEXTURE_ENV 0x2300 + +/* TextureMagFilter */ +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 + +/* TextureMinFilter */ +/* GL_NEAREST */ +/* GL_LINEAR */ +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 + +/* TextureParameterName */ +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 + +/* TextureTarget */ +/* GL_TEXTURE_2D */ + +/* TextureUnit */ +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF + +/* TextureWrapMode */ +#define GL_REPEAT 0x2901 +#define GL_CLAMP_TO_EDGE 0x812F + +/* PixelInternalFormat */ +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 + +/* VertexPointerType */ +/* GL_SHORT */ +/* GL_FLOAT */ +/* GL_FIXED */ +/* GL_BYTE */ + +/* LightName */ +#define GL_LIGHT0 0x4000 +#define GL_LIGHT1 0x4001 +#define GL_LIGHT2 0x4002 +#define GL_LIGHT3 0x4003 +#define GL_LIGHT4 0x4004 +#define GL_LIGHT5 0x4005 +#define GL_LIGHT6 0x4006 +#define GL_LIGHT7 0x4007 + + +/*************************************************************/ + +GLAPI void APIENTRY glActiveTexture (GLenum texture); +GLAPI void APIENTRY glAlphaFunc (GLenum func, GLclampf ref); +GLAPI void APIENTRY glAlphaFuncx (GLenum func, GLclampx ref); +GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture); +GLAPI void APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); +GLAPI void APIENTRY glClear (GLbitfield mask); +GLAPI void APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GLAPI void APIENTRY glClearColorx (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha); +GLAPI void APIENTRY glClearDepthf (GLclampf depth); +GLAPI void APIENTRY glClearDepthx (GLclampx depth); +GLAPI void APIENTRY glClearStencil (GLint s); +GLAPI void APIENTRY glClientActiveTexture (GLenum texture); +GLAPI void APIENTRY glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glColor4x (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GLAPI void APIENTRY glColorPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glCullFace (GLenum mode); +GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); +GLAPI void APIENTRY glDepthFunc (GLenum func); +GLAPI void APIENTRY glDepthMask (GLboolean flag); +GLAPI void APIENTRY glDepthRangef (GLclampf zNear, GLclampf zFar); +GLAPI void APIENTRY glDepthRangex (GLclampx zNear, GLclampx zFar); +GLAPI void APIENTRY glDisable (GLenum cap); +GLAPI void APIENTRY glDisableClientState (GLenum array); +GLAPI void APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +GLAPI void APIENTRY glEnable (GLenum cap); +GLAPI void APIENTRY glEnableClientState (GLenum array); +GLAPI void APIENTRY glFinish (void); +GLAPI void APIENTRY glFlush (void); +GLAPI void APIENTRY glFogf (GLenum pname, GLfloat param); +GLAPI void APIENTRY glFogfv (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFogx (GLenum pname, GLfixed param); +GLAPI void APIENTRY glFogxv (GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glFrontFace (GLenum mode); +GLAPI void APIENTRY glFrustumf (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GLAPI void APIENTRY glFrustumx (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); +GLAPI GLenum APIENTRY glGetError (void); +GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *params); +GLAPI const GLubyte * APIENTRY glGetString (GLenum name); +GLAPI void APIENTRY glHint (GLenum target, GLenum mode); +GLAPI void APIENTRY glLightModelf (GLenum pname, GLfloat param); +GLAPI void APIENTRY glLightModelfv (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glLightModelx (GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightModelxv (GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glLightf (GLenum light, GLenum pname, GLfloat param); +GLAPI void APIENTRY glLightfv (GLenum light, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glLightx (GLenum light, GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightxv (GLenum light, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glLineWidth (GLfloat width); +GLAPI void APIENTRY glLineWidthx (GLfixed width); +GLAPI void APIENTRY glLoadIdentity (void); +GLAPI void APIENTRY glLoadMatrixf (const GLfloat *m); +GLAPI void APIENTRY glLoadMatrixx (const GLfixed *m); +GLAPI void APIENTRY glLogicOp (GLenum opcode); +GLAPI void APIENTRY glMaterialf (GLenum face, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMaterialfv (GLenum face, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMaterialx (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glMaterialxv (GLenum face, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glMatrixMode (GLenum mode); +GLAPI void APIENTRY glMultMatrixf (const GLfloat *m); +GLAPI void APIENTRY glMultMatrixx (const GLfixed *m); +GLAPI void APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4x (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz); +GLAPI void APIENTRY glNormal3x (GLfixed nx, GLfixed ny, GLfixed nz); +GLAPI void APIENTRY glNormalPointer (GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glOrthof (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar); +GLAPI void APIENTRY glOrthox (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar); +GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param); +GLAPI void APIENTRY glPointSize (GLfloat size); +GLAPI void APIENTRY glPointSizex (GLfixed size); +GLAPI void APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); +GLAPI void APIENTRY glPolygonOffsetx (GLfixed factor, GLfixed units); +GLAPI void APIENTRY glPopMatrix (void); +GLAPI void APIENTRY glPushMatrix (void); +GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +GLAPI void APIENTRY glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glRotatex (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glSampleCoverage (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSampleCoveragex (GLclampx value, GLboolean invert); +GLAPI void APIENTRY glScalef (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glScalex (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glShadeModel (GLenum mode); +GLAPI void APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilMask (GLuint mask); +GLAPI void APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); +GLAPI void APIENTRY glTexCoordPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glTexEnvf (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTexEnvfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glTexEnvx (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexEnvxv (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTexParameterx (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTranslatef (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTranslatex (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glVertexPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); + +#ifdef __cplusplus +} +#endif + +#endif /* __gl_h_ */ diff --git a/opengl/tests/angeles/license-BSD.txt b/opengl/tests/angeles/license-BSD.txt new file mode 100644 index 000000000..8924e3ca2 --- /dev/null +++ b/opengl/tests/angeles/license-BSD.txt @@ -0,0 +1,34 @@ +This is the BSD-style license for the "San Angeles Observation" +OpenGL ES version example source code +--------------------------------------------------------------- + +San Angeles Observation OpenGL ES version example +Copyright (c) 2004-2005, Jetro Lauha +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of the software product's copyright owner nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/opengl/tests/angeles/license-LGPL.txt b/opengl/tests/angeles/license-LGPL.txt new file mode 100644 index 000000000..b1e3f5a26 --- /dev/null +++ b/opengl/tests/angeles/license-LGPL.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/opengl/tests/angeles/license.txt b/opengl/tests/angeles/license.txt new file mode 100644 index 000000000..620841e82 --- /dev/null +++ b/opengl/tests/angeles/license.txt @@ -0,0 +1,19 @@ +San Angeles Observation OpenGL ES version example +Copyright 2004-2005 Jetro Lauha +All rights reserved. +Web: http://iki.fi/jetro/ + +This source is free software; you can redistribute it and/or +modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this source in the + file LICENSE-LGPL.txt. + (2) The BSD-style license that is included with this source in + the file LICENSE-BSD.txt. + +This source is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files +LICENSE-LGPL.txt and LICENSE-BSD.txt for more details. diff --git a/opengl/tests/angeles/shapes.h b/opengl/tests/angeles/shapes.h new file mode 100644 index 000000000..25ffae8ce --- /dev/null +++ b/opengl/tests/angeles/shapes.h @@ -0,0 +1,59 @@ +/* San Angeles Observation OpenGL ES version example + * Copyright 2004-2005 Jetro Lauha + * All rights reserved. + * Web: http://iki.fi/jetro/ + * + * This source is free software; you can redistribute it and/or + * modify it under the terms of EITHER: + * (1) The GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. The text of the GNU Lesser + * General Public License is included with this source in the + * file LICENSE-LGPL.txt. + * (2) The BSD-style license that is included with this source in + * the file LICENSE-BSD.txt. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details. + * + * $Id: shapes.h,v 1.6 2005/01/31 22:15:30 tonic Exp $ + * $Revision: 1.6 $ + */ + +#ifndef SHAPES_H_INCLUDED +#define SHAPES_H_INCLUDED + + +#define SUPERSHAPE_PARAMS 15 + +static const float sSuperShapeParams[][SUPERSHAPE_PARAMS] = +{ + // m a b n1 n2 n3 m a b n1 n2 n3 res1 res2 scale (org.res1,res2) + { 10, 1, 2, 90, 1, -45, 8, 1, 1, -1, 1, -0.4f, 20, 30, 2 }, // 40, 60 + { 10, 1, 2, 90, 1, -45, 4, 1, 1, 10, 1, -0.4f, 20, 20, 4 }, // 40, 40 + { 10, 1, 2, 60, 1, -10, 4, 1, 1, -1, -2, -0.4f, 41, 41, 1 }, // 82, 82 + { 6, 1, 1, 60, 1, -70, 8, 1, 1, 0.4f, 3, 0.25f, 20, 20, 1 }, // 40, 40 + { 4, 1, 1, 30, 1, 20, 12, 1, 1, 0.4f, 3, 0.25f, 10, 30, 1 }, // 20, 60 + { 8, 1, 1, 30, 1, -4, 8, 2, 1, -1, 5, 0.5f, 25, 26, 1 }, // 60, 60 + { 13, 1, 1, 30, 1, -4, 13, 1, 1, 1, 5, 1, 30, 30, 6 }, // 60, 60 + { 10, 1, 1.1f, -0.5f, 0.1f, 70, 60, 1, 1, -90, 0, -0.25f, 20, 60, 8 }, // 60, 180 + { 7, 1, 1, 20, -0.3f, -3.5f, 6, 1, 1, -1, 4.5f, 0.5f, 10, 20, 4 }, // 60, 80 + { 4, 1, 1, 10, 10, 10, 4, 1, 1, 10, 10, 10, 10, 20, 1 }, // 20, 40 + { 4, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 10, 10, 2 }, // 10, 10 + { 1, 1, 1, 38, -0.25f, 19, 4, 1, 1, 10, 10, 10, 10, 15, 2 }, // 20, 40 + { 2, 1, 1, 0.7f, 0.3f, 0.2f, 3, 1, 1, 100, 100, 100, 10, 25, 2 }, // 20, 50 + { 6, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 30, 30, 2 }, // 60, 60 + { 3, 1, 1, 1, 1, 1, 6, 1, 1, 2, 1, 1, 10, 20, 2 }, // 20, 40 + { 6, 1, 1, 6, 5.5f, 100, 6, 1, 1, 25, 10, 10, 30, 20, 2 }, // 60, 40 + { 3, 1, 1, 0.5f, 1.7f, 1.7f, 2, 1, 1, 10, 10, 10, 20, 20, 2 }, // 40, 40 + { 5, 1, 1, 0.1f, 1.7f, 1.7f, 1, 1, 1, 0.3f, 0.5f, 0.5f, 20, 20, 4 }, // 40, 40 + { 2, 1, 1, 6, 5.5f, 100, 6, 1, 1, 4, 10, 10, 10, 22, 1 }, // 40, 40 + { 6, 1, 1, -1, 70, 0.1f, 9, 1, 0.5f, -98, 0.05f, -45, 20, 30, 4 }, // 60, 91 + { 6, 1, 1, -1, 90, -0.1f, 7, 1, 1, 90, 1.3f, 34, 13, 16, 1 }, // 32, 60 +}; +#define SUPERSHAPE_COUNT (sizeof(sSuperShapeParams) / sizeof(sSuperShapeParams[0])) + + +#endif // !SHAPES_H_INCLUDED diff --git a/opengl/tests/filter/Android.mk b/opengl/tests/filter/Android.mk new file mode 100644 index 000000000..a448f0d46 --- /dev/null +++ b/opengl/tests/filter/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + filter.c + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libEGL \ + libGLESv1_CM \ + libui + +LOCAL_MODULE:= test-opengl-filter + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/opengl/tests/filter/filter.c b/opengl/tests/filter/filter.c new file mode 100644 index 000000000..de9711963 --- /dev/null +++ b/opengl/tests/filter/filter.c @@ -0,0 +1,130 @@ +#include +#include + +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc!=2 && argc!=3) { + printf("usage: %s <0-6> [pbuffer]\n", argv[0]); + return 0; + } + + const int test = atoi(argv[1]); + int usePbuffer = argc==3 && !strcmp(argv[2], "pbuffer"); + EGLint s_configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT|EGL_WINDOW_BIT, + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_NONE + }; + + EGLint numConfigs = -1; + EGLint majorVersion; + EGLint minorVersion; + EGLConfig config; + EGLContext context; + EGLSurface surface; + EGLint w, h; + + EGLDisplay dpy; + + dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(dpy, &majorVersion, &minorVersion); + eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs); + if (!usePbuffer) { + surface = eglCreateWindowSurface(dpy, config, + android_createDisplaySurface(), NULL); + } else { + printf("using pbuffer\n"); + EGLint attribs[] = { EGL_WIDTH, 320, EGL_HEIGHT, 480, EGL_NONE }; + surface = eglCreatePbufferSurface(dpy, config, attribs); + if (surface == EGL_NO_SURFACE) { + printf("eglCreatePbufferSurface error %x\n", eglGetError()); + } + } + context = eglCreateContext(dpy, config, NULL, NULL); + eglMakeCurrent(dpy, surface, surface, context); + eglQuerySurface(dpy, surface, EGL_WIDTH, &w); + eglQuerySurface(dpy, surface, EGL_HEIGHT, &h); + GLint dim = w +#include +#include +#include +#include + +#include +#include +#include + + +long long systemTime() +{ + struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(CLOCK_MONOTONIC, &t); + return (long long)(t.tv_sec)*1000000000LL + t.tv_nsec; +} + +int main(int argc, char** argv) +{ + EGLint s_configAttribs[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_NONE + }; + + EGLint numConfigs = -1; + EGLint majorVersion; + EGLint minorVersion; + EGLConfig config; + EGLContext context; + EGLSurface surface; + EGLint w, h; + + EGLDisplay dpy; + + dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(dpy, &majorVersion, &minorVersion); + eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs); + surface = eglCreateWindowSurface(dpy, config, + android_createDisplaySurface(), NULL); + context = eglCreateContext(dpy, config, NULL, NULL); + eglMakeCurrent(dpy, surface, surface, context); + eglQuerySurface(dpy, surface, EGL_WIDTH, &w); + eglQuerySurface(dpy, surface, EGL_HEIGHT, &h); + GLint dim = w +#include + +#include +#include +#include + +int main(int argc, char** argv) +{ + EGLint s_configAttribs[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_NONE + }; + + EGLint numConfigs = -1; + EGLint majorVersion; + EGLint minorVersion; + EGLConfig config; + EGLContext context; + EGLSurface surface; + EGLint w, h; + + EGLDisplay dpy; + + dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(dpy, &majorVersion, &minorVersion); + eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs); + surface = eglCreateWindowSurface(dpy, config, + android_createDisplaySurface(), NULL); + context = eglCreateContext(dpy, config, NULL, NULL); + eglMakeCurrent(dpy, surface, surface, context); + eglQuerySurface(dpy, surface, EGL_WIDTH, &w); + eglQuerySurface(dpy, surface, EGL_HEIGHT, &h); + GLint dim = w +#include + +#include +#include +#include + +EGLDisplay eglDisplay; +EGLSurface eglSurface; +EGLContext eglContext; +GLuint texture; + +#define FIXED_ONE 0x10000 +#define ITERATIONS 50 + +int init_gl_surface(void); +void free_gl_surface(void); +void init_scene(void); +void render(int quads); +void create_texture(void); +int readTimer(void); + +static void gluLookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, float upX, float upY, + float upZ) +{ + // See the OpenGL GLUT documentation for gluLookAt for a description + // of the algorithm. We implement it in a straightforward way: + + float fx = centerX - eyeX; + float fy = centerY - eyeY; + float fz = centerZ - eyeZ; + + // Normalize f + float rlf = 1.0f / sqrtf(fx*fx + fy*fy + fz*fz); + fx *= rlf; + fy *= rlf; + fz *= rlf; + + // Normalize up + float rlup = 1.0f / sqrtf(upX*upX + upY*upY + upZ*upZ); + upX *= rlup; + upY *= rlup; + upZ *= rlup; + + // compute s = f x up (x means "cross product") + + float sx = fy * upZ - fz * upY; + float sy = fz * upX - fx * upZ; + float sz = fx * upY - fy * upX; + + // compute u = s x f + float ux = sy * fz - sz * fy; + float uy = sz * fx - sx * fz; + float uz = sx * fy - sy * fx; + + float m[16] ; + m[0] = sx; + m[1] = ux; + m[2] = -fx; + m[3] = 0.0f; + + m[4] = sy; + m[5] = uy; + m[6] = -fy; + m[7] = 0.0f; + + m[8] = sz; + m[9] = uz; + m[10] = -fz; + m[11] = 0.0f; + + m[12] = 0.0f; + m[13] = 0.0f; + m[14] = 0.0f; + m[15] = 1.0f; + + glMultMatrixf(m); + glTranslatef(-eyeX, -eyeY, -eyeZ); +} + +int main(int argc, char **argv) +{ + int q; + int start, end; + + printf("Initializing EGL...\n"); + + if(!init_gl_surface()) + { + printf("GL initialisation failed - exiting\n"); + return 0; + } + + init_scene(); + + create_texture(); + + printf("Start test...\n"); + + render(argc==2 ? atoi(argv[1]) : ITERATIONS); + + free_gl_surface(); + + return 0; +} + +int init_gl_surface(void) +{ + EGLint numConfigs = 1; + EGLConfig myConfig = {0}; + EGLint attrib[] = + { + EGL_DEPTH_SIZE, 16, + EGL_NONE + }; + + if ( (eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY ) + { + printf("eglGetDisplay failed\n"); + return 0; + } + + if ( eglInitialize(eglDisplay, NULL, NULL) != EGL_TRUE ) + { + printf("eglInitialize failed\n"); + return 0; + } + + if ( eglChooseConfig(eglDisplay, attrib, &myConfig, 1, &numConfigs) != EGL_TRUE ) + { + printf("eglChooseConfig failed\n"); + return 0; + } + + if ( (eglSurface = eglCreateWindowSurface(eglDisplay, myConfig, + android_createDisplaySurface(), 0)) == EGL_NO_SURFACE ) + { + printf("eglCreateWindowSurface failed\n"); + return 0; + } + + if ( (eglContext = eglCreateContext(eglDisplay, myConfig, 0, 0)) == EGL_NO_CONTEXT ) + { + printf("eglCreateContext failed\n"); + return 0; + } + + if ( eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) != EGL_TRUE ) + { + printf("eglMakeCurrent failed\n"); + return 0; + } + + return 1; +} + +void free_gl_surface(void) +{ + if (eglDisplay != EGL_NO_DISPLAY) + { + eglMakeCurrent( EGL_NO_DISPLAY, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT ); + eglDestroyContext( eglDisplay, eglContext ); + eglDestroySurface( eglDisplay, eglSurface ); + eglTerminate( eglDisplay ); + eglDisplay = EGL_NO_DISPLAY; + } +} + +void init_scene(void) +{ + glDisable(GL_DITHER); + glEnable(GL_CULL_FACE); + + float ratio = 320.0f / 480.0f; + glViewport(0, 0, 320, 480); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustumf(-ratio, ratio, -1, 1, 1, 10); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( + 0, 0, 3, // eye + 0, 0, 0, // center + 0, 1, 0); // up + + glEnable(GL_TEXTURE_2D); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); +} + +void create_texture(void) +{ + const unsigned int on = 0xff0000ff; + const unsigned int off = 0xffffffff; + const unsigned int pixels[] = + { + on, off, on, off, on, off, on, off, + off, on, off, on, off, on, off, on, + on, off, on, off, on, off, on, off, + off, on, off, on, off, on, off, on, + on, off, on, off, on, off, on, off, + off, on, off, on, off, on, off, on, + on, off, on, off, on, off, on, off, + off, on, off, on, off, on, off, on, + }; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +} + +void render(int quads) +{ + int i, j; + + const GLfloat vertices[] = { + -1, -1, 0, + 1, -1, 0, + 1, 1, 0, + -1, 1, 0 + }; + + const GLfixed texCoords[] = { + 0, 0, + FIXED_ONE, 0, + FIXED_ONE, FIXED_ONE, + 0, FIXED_ONE + }; + + const GLushort template[] = { 0, 1, 2, 0, 2, 3 }; + + + GLushort* indices = (GLushort*)malloc(quads*sizeof(template)); + for (i=0 ; i out/android/graphics/Canvas.java +echo "public interface Canvas {}" >> out/android/graphics/Canvas.java + +GLFILE=out/javax/microedition/khronos/opengles/GL.java +cp stubs/GLHeader.java-if $GLFILE + +GLGEN_FILES="CFunc.java CType.java CodeEmitter.java GenerateGL.java JFunc.java JType.java JniCodeEmitter.java ParameterChecker.java" + +pushd src > /dev/null +javac ${GLGEN_FILES} +popd > /dev/null +java -classpath src GenerateGL -c glspec-1.0 glspec-1.0ext glspec-1.1 glspec-1.1ext glspec-1.1extpack glspec-checks + +pushd out > /dev/null +mkdir classes +javac -d classes com/google/android/gles_jni/GLImpl.java javax/microedition/khronos/opengles/GL10.java javax/microedition/khronos/opengles/GL10Ext.java javax/microedition/khronos/opengles/GL11.java javax/microedition/khronos/opengles/GL11Ext.java javax/microedition/khronos/opengles/GL11ExtensionPack.java +popd > /dev/null + +rm -rf generated +mkdir -p generated/C +cp out/com_google_android_gles_jni_GLImpl.cpp generated/C +cp -r out/com generated +cp -r out/javax generated + +rm -rf out + +# com_google_android_gles_jni_GLImpl.cpp +if cmp ../../../frameworks/base/core/jni/com_google_android_gles_jni_GLImpl.cpp generated/C/com_google_android_gles_jni_GLImpl.cpp ; then +echo com_google_android_gles_jni_GLImpl.cpp unchanged +else +echo Please edit ../../../frameworks/base/core/jni/com_google_android_gles_jni_GLImpl.cpp +echo Please cp generated/C/com_google_android_gles_jni_GLImpl.cpp ../../../frameworks/base/core/jni +fi + +# GLImpl.java +if cmp ../../java/com/google/android/gles_jni/GLImpl.java generated/com/google/android/gles_jni/GLImpl.java ; then +echo GLImpl.java unchanged +else +echo Please edit ../../java/com/google/android/gles_jni/GLImpl.java +echo Please cp generated/com/google/android/gles_jni/GLImpl.java ../../java/com/google/android/gles_jni +fi + +# GL.java +if cmp ../../java/javax/microedition/khronos/opengles/GL.java generated/javax/microedition/khronos/opengles/GL.java ; then +echo GL.java unchanged +else +echo Please edit ../../java/javax/microedition/khronos/opengles/GL.java +echo Please cp generated/javax/microedition/khronos/opengles/GL.java ../../java/javax/microedition/khronos/opengles/GL.java +fi + +# GL10.java +if cmp ../../java/javax/microedition/khronos/opengles/GL10.java generated/javax/microedition/khronos/opengles/GL10.java ; then +echo GL10.java unchanged +else +echo Please edit ../../java/javax/microedition/khronos/opengles/GL10.java +echo Please cp generated/javax/microedition/khronos/opengles/GL10.java ../../java/javax/microedition/khronos/opengles/GL10.java +fi + +# GL10Ext.java +if cmp ../../java/javax/microedition/khronos/opengles/GL10Ext.java generated/javax/microedition/khronos/opengles/GL10Ext.java ; then +echo GL10Ext.java unchanged +else +echo Please edit ../../java/javax/microedition/khronos/opengles/GL10Ext.java +echo Please cp generated/javax/microedition/khronos/opengles/GL10Ext.java ../../java/javax/microedition/khronos/opengles/GL10Ext.java +fi + +# GL11.java +if cmp ../../java/javax/microedition/khronos/opengles/GL11.java generated/javax/microedition/khronos/opengles/GL11.java ; then +echo GL11.java unchanged +else +echo Please edit ../../java/javax/microedition/khronos/opengles/GL11.java +echo Please cp generated/javax/microedition/khronos/opengles/GL11.java ../../java/javax/microedition/khronos/opengles/GL11.java +fi + +# GL11Ext.java +if cmp ../../java/javax/microedition/khronos/opengles/GL11Ext.java generated/javax/microedition/khronos/opengles/GL11Ext.java ; then +echo GL11Ext.java unchanged +else +echo Please edit ../../java/javax/microedition/khronos/opengles/GL11Ext.java +echo Please cp generated/javax/microedition/khronos/opengles/GL11Ext.java ../../java/javax/microedition/khronos/opengles/GL11Ext.java +fi + +# GL11ExtensionPack.java +if cmp ../../java/javax/microedition/khronos/opengles/GL11ExtensionPack.java generated/javax/microedition/khronos/opengles/GL11ExtensionPack.java ; then +echo GL11ExtensionPack.java unchanged +else +echo Please edit ../../java/javax/microedition/khronos/opengles/GL11ExtensionPack.java +echo Please cp generated/javax/microedition/khronos/opengles/GL11ExtensionPack.java ../../java/javax/microedition/khronos/opengles/GL11ExtensionPack.java +fi + +rm -rf generated diff --git a/opengl/tools/glgen/glspec-1.0 b/opengl/tools/glgen/glspec-1.0 new file mode 100644 index 000000000..c442320bf --- /dev/null +++ b/opengl/tools/glgen/glspec-1.0 @@ -0,0 +1,106 @@ +void glActiveTexture ( GLenum texture ) +void glAlphaFunc ( GLenum func, GLclampf ref ) +void glAlphaFuncx ( GLenum func, GLclampx ref ) +void glBindTexture ( GLenum target, GLuint texture ) +void glBlendFunc ( GLenum sfactor, GLenum dfactor ) +void glClear ( GLbitfield mask ) +void glClearColor ( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha ) +void glClearColorx ( GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha ) +void glClearDepthf ( GLclampf depth ) +void glClearDepthx ( GLclampx depth ) +void glClearStencil ( GLint s ) +void glClientActiveTexture ( GLenum texture ) +void glColor4f ( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha ) +void glColor4x ( GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha ) +void glColorMask ( GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha ) +void glColorPointer ( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer ) +void glCompressedTexImage2D ( GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data ) +void glCompressedTexSubImage2D ( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data ) +void glCopyTexImage2D ( GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border ) +void glCopyTexSubImage2D ( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height ) +void glCullFace ( GLenum mode ) +void glDeleteTextures ( GLsizei n, const GLuint *textures ) +void glDepthFunc ( GLenum func ) +void glDepthMask ( GLboolean flag ) +void glDepthRangef ( GLclampf zNear, GLclampf zFar ) +void glDepthRangex ( GLclampx zNear, GLclampx zFar ) +void glDisable ( GLenum cap ) +void glDisableClientState ( GLenum array ) +void glDrawArrays ( GLenum mode, GLint first, GLsizei count ) +void glDrawElements ( GLenum mode, GLsizei count, GLenum type, const GLvoid *indices ) +void glEnable ( GLenum cap ) +void glEnableClientState ( GLenum array ) +void glFinish ( void ) +void glFlush ( void ) +void glFogf ( GLenum pname, GLfloat param ) +void glFogfv ( GLenum pname, const GLfloat *params ) +void glFogx ( GLenum pname, GLfixed param ) +void glFogxv ( GLenum pname, const GLfixed *params ) +void glFrontFace ( GLenum mode ) +void glFrustumf ( GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar ) +void glFrustumx ( GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar ) +void glGenTextures ( GLsizei n, GLuint *textures ) +GLenum glGetError ( void ) +void glGetIntegerv ( GLenum pname, GLint *params ) +const GLubyte * glGetString ( GLenum name ) +void glHint ( GLenum target, GLenum mode ) +void glLightModelf ( GLenum pname, GLfloat param ) +void glLightModelfv ( GLenum pname, const GLfloat *params ) +void glLightModelx ( GLenum pname, GLfixed param ) +void glLightModelxv ( GLenum pname, const GLfixed *params ) +void glLightf ( GLenum light, GLenum pname, GLfloat param ) +void glLightfv ( GLenum light, GLenum pname, const GLfloat *params ) +void glLightx ( GLenum light, GLenum pname, GLfixed param ) +void glLightxv ( GLenum light, GLenum pname, const GLfixed *params ) +void glLineWidth ( GLfloat width ) +void glLineWidthx ( GLfixed width ) +void glLoadIdentity ( void ) +void glLoadMatrixf ( const GLfloat *m ) +void glLoadMatrixx ( const GLfixed *m ) +void glLogicOp ( GLenum opcode ) +void glMaterialf ( GLenum face, GLenum pname, GLfloat param ) +void glMaterialfv ( GLenum face, GLenum pname, const GLfloat *params ) +void glMaterialx ( GLenum face, GLenum pname, GLfixed param ) +void glMaterialxv ( GLenum face, GLenum pname, const GLfixed *params ) +void glMatrixMode ( GLenum mode ) +void glMultMatrixf ( const GLfloat *m ) +void glMultMatrixx ( const GLfixed *m ) +void glMultiTexCoord4f ( GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q ) +void glMultiTexCoord4x ( GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q ) +void glNormal3f ( GLfloat nx, GLfloat ny, GLfloat nz ) +void glNormal3x ( GLfixed nx, GLfixed ny, GLfixed nz ) +void glNormalPointer ( GLenum type, GLsizei stride, const GLvoid *pointer ) +void glOrthof ( GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar ) +void glOrthox ( GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar ) +void glPixelStorei ( GLenum pname, GLint param ) +void glPointSize ( GLfloat size ) +void glPointSizex ( GLfixed size ) +void glPolygonOffset ( GLfloat factor, GLfloat units ) +void glPolygonOffsetx ( GLfixed factor, GLfixed units ) +void glPopMatrix ( void ) +void glPushMatrix ( void ) +void glReadPixels ( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels ) +void glRotatef ( GLfloat angle, GLfloat x, GLfloat y, GLfloat z ) +void glRotatex ( GLfixed angle, GLfixed x, GLfixed y, GLfixed z ) +void glSampleCoverage ( GLclampf value, GLboolean invert ) +void glSampleCoveragex ( GLclampx value, GLboolean invert ) +void glScalef ( GLfloat x, GLfloat y, GLfloat z ) +void glScalex ( GLfixed x, GLfixed y, GLfixed z ) +void glScissor ( GLint x, GLint y, GLsizei width, GLsizei height ) +void glShadeModel ( GLenum mode ) +void glStencilFunc ( GLenum func, GLint ref, GLuint mask ) +void glStencilMask ( GLuint mask ) +void glStencilOp ( GLenum fail, GLenum zfail, GLenum zpass ) +void glTexCoordPointer ( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer ) +void glTexEnvf ( GLenum target, GLenum pname, GLfloat param ) +void glTexEnvfv ( GLenum target, GLenum pname, const GLfloat *params ) +void glTexEnvx ( GLenum target, GLenum pname, GLfixed param ) +void glTexEnvxv ( GLenum target, GLenum pname, const GLfixed *params ) +void glTexImage2D ( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels ) +void glTexParameterf ( GLenum target, GLenum pname, GLfloat param ) +void glTexParameterx ( GLenum target, GLenum pname, GLfixed param ) +void glTexSubImage2D ( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels ) +void glTranslatef ( GLfloat x, GLfloat y, GLfloat z ) +void glTranslatex ( GLfixed x, GLfixed y, GLfixed z ) +void glVertexPointer ( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer ) +void glViewport ( GLint x, GLint y, GLsizei width, GLsizei height ) diff --git a/opengl/tools/glgen/glspec-1.0ext b/opengl/tools/glgen/glspec-1.0ext new file mode 100644 index 000000000..7d1975819 --- /dev/null +++ b/opengl/tools/glgen/glspec-1.0ext @@ -0,0 +1 @@ +GLbitfield glQueryMatrixxOES ( GLfixed *mantissa, GLint *exponent ) diff --git a/opengl/tools/glgen/glspec-1.1 b/opengl/tools/glgen/glspec-1.1 new file mode 100644 index 000000000..9149a7f97 --- /dev/null +++ b/opengl/tools/glgen/glspec-1.1 @@ -0,0 +1,42 @@ +void glBindBuffer ( GLenum target, GLuint buffer ) +void glBufferData ( GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage ) +void glBufferSubData ( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data ) +void glClipPlanef ( GLenum plane, const GLfloat *equation ) +void glClipPlanex ( GLenum plane, const GLfixed *equation ) +void glColor4ub ( GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha ) +void glColorPointer ( GLint size, GLenum type, GLsizei stride, GLint offset ) +void glDeleteBuffers ( GLsizei n, const GLuint *buffers ) +void glDrawElements ( GLenum mode, GLsizei count, GLenum type, GLint offset ) +void glGenBuffers ( GLsizei n, GLuint *buffers ) +void glGetBooleanv ( GLenum pname, GLboolean *params ) +void glGetBufferParameteriv ( GLenum target, GLenum pname, GLint *params ) +void glGetClipPlanef ( GLenum pname, GLfloat *eqn ) +void glGetClipPlanex ( GLenum pname, GLfixed *eqn ) +void glGetFixedv ( GLenum pname, GLfixed *params ) +void glGetFloatv ( GLenum pname, GLfloat *params ) +void glGetLightfv ( GLenum light, GLenum pname, GLfloat *params ) +void glGetLightxv ( GLenum light, GLenum pname, GLfixed *params ) +void glGetMaterialfv ( GLenum face, GLenum pname, GLfloat *params ) +void glGetMaterialxv ( GLenum face, GLenum pname, GLfixed *params ) +void glGetTexEnviv ( GLenum env, GLenum pname, GLint *params ) +void glGetTexEnvxv ( GLenum env, GLenum pname, GLfixed *params ) +void glGetTexParameterfv ( GLenum target, GLenum pname, GLfloat *params ) +void glGetTexParameteriv ( GLenum target, GLenum pname, GLint *params ) +void glGetTexParameterxv ( GLenum target, GLenum pname, GLfixed *params ) +GLboolean glIsBuffer ( GLuint buffer ) +GLboolean glIsEnabled ( GLenum cap ) +GLboolean glIsTexture ( GLuint texture ) +void glNormalPointer ( GLenum type, GLsizei stride, GLint offset ) +void glPointParameterf ( GLenum pname, GLfloat param ) +void glPointParameterfv ( GLenum pname, const GLfloat *params ) +void glPointParameterx ( GLenum pname, GLfixed param ) +void glPointParameterxv ( GLenum pname, const GLfixed *params ) +void glPointSizePointerOES ( GLenum type, GLsizei stride, const GLvoid *pointer ) +void glTexCoordPointer ( GLint size, GLenum type, GLsizei stride, GLint offset ) +void glTexEnvi ( GLenum target, GLenum pname, GLint param ) +void glTexEnviv ( GLenum target, GLenum pname, const GLint *params ) +void glTexParameterfv ( GLenum target, GLenum pname, const GLfloat *params ) +void glTexParameteri ( GLenum target, GLenum pname, GLint param ) +void glTexParameteriv ( GLenum target, GLenum pname, const GLint *params ) +void glTexParameterxv ( GLenum target, GLenum pname, const GLfixed *params ) +void glVertexPointer ( GLint size, GLenum type, GLsizei stride, GLint offset ) diff --git a/opengl/tools/glgen/glspec-1.1ext b/opengl/tools/glgen/glspec-1.1ext new file mode 100644 index 000000000..cc08c7352 --- /dev/null +++ b/opengl/tools/glgen/glspec-1.1ext @@ -0,0 +1,16 @@ +void glCurrentPaletteMatrixOES ( GLuint matrixpaletteindex ) +void glDrawTexfOES ( GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height ) +void glDrawTexfvOES ( const GLfloat *coords ) +void glDrawTexiOES ( GLint x, GLint y, GLint z, GLint width, GLint height ) +void glDrawTexivOES ( const GLint *coords ) +void glDrawTexsOES ( GLshort x, GLshort y, GLshort z, GLshort width, GLshort height ) +void glDrawTexsvOES ( const GLshort *coords ) +void glDrawTexxOES ( GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height ) +void glDrawTexxvOES ( const GLfixed *coords ) +void glEnable ( GLenum cap ) +void glEnableClientState ( GLenum array ) +void glLoadPaletteFromModelViewMatrixOES ( void ) +void glMatrixIndexPointerOES ( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer ) +void glMatrixIndexPointerOES ( GLint size, GLenum type, GLsizei stride, GLint offset ) +void glWeightPointerOES ( GLint size, GLenum type, GLsizei stride, const GLvoid *pointer ) +void glWeightPointerOES ( GLint size, GLenum type, GLsizei stride, GLint offset ) diff --git a/opengl/tools/glgen/glspec-1.1extpack b/opengl/tools/glgen/glspec-1.1extpack new file mode 100644 index 000000000..ca9e6d2c5 --- /dev/null +++ b/opengl/tools/glgen/glspec-1.1extpack @@ -0,0 +1,38 @@ +void glBindFramebufferOES ( GLint target, GLint framebuffer ) +void glBindRenderbufferOES ( GLint target, GLint renderbuffer ) +void glBindTexture ( GLint target, GLint texture ) +void glBlendEquation ( GLint mode ) +void glBlendEquationSeparate ( GLint modeRGB, GLint modeAlpha ) +void glBlendFuncSeparate ( GLint srcRGB, GLint dstRGB, GLint srcAlpha, GLint dstAlpha ) +GLint glCheckFramebufferStatusOES ( GLint target ) +void glCompressedTexImage2D ( GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data ) +void glCopyTexImage2D ( GLint target, GLint level, GLint internalformat, GLint x, GLint y, GLint width, GLint height, GLint border ) +void glDeleteFramebuffersOES ( GLint n, GLint *framebuffers ) +void glDeleteRenderbuffersOES ( GLint n, GLint *renderbuffers ) +void glEnable ( GLint cap ) +void glFramebufferRenderbufferOES ( GLint target, GLint attachment, GLint renderbuffertarget, GLint renderbuffer ) +void glFramebufferTexture2DOES ( GLint target, GLint attachment, GLint textarget, GLint texture, GLint level ) +void glGenerateMipmapOES ( GLint target ) +void glGenFramebuffersOES ( GLint n, GLint *framebuffers ) +void glGenRenderbuffersOES ( GLint n, GLint *renderbuffers ) +void glGetFramebufferAttachmentParameterivOES ( GLint target, GLint attachment, GLint pname, GLint *params ) +void glGetIntegerv ( GLint pname, GLint *params ) +void glGetRenderbufferParameterivOES ( GLint target, GLint pname, GLint *params ) +void glGetTexGenfv ( GLint coord, GLint pname, GLfloat *params ) +void glGetTexGeniv ( GLint coord, GLint pname, GLint *params ) +void glGetTexGenxv ( GLint coord, GLint pname, GLint *params ) +GLboolean glIsFramebufferOES ( GLint framebuffer ) +GLboolean glIsRenderbufferOES ( GLint renderbuffer ) +void glRenderbufferStorageOES ( GLint target, GLint internalformat, GLint width, GLint height ) +void glStencilOp ( GLint fail, GLint zfail, GLint zpass ) +void glTexEnvf ( GLint target, GLint pname, GLfloat param ) +void glTexEnvfv ( GLint target, GLint pname, GLfloat *params ) +void glTexEnvx ( GLint target, GLint pname, GLint param ) +void glTexEnvxv ( GLint target, GLint pname, GLint *params ) +void glTexGenf ( GLint coord, GLint pname, GLfloat param ) +void glTexGenfv ( GLint coord, GLint pname, GLfloat *params ) +void glTexGeni ( GLint coord, GLint pname, GLint param ) +void glTexGeniv ( GLint coord, GLint pname, GLint *params ) +void glTexGenx ( GLint coord, GLint pname, GLint param ) +void glTexGenxv ( GLint coord, GLint pname, GLint *params ) +void glTexParameterf ( GLint target, GLint pname, GLfloat param ) diff --git a/opengl/tools/glgen/glspec-checks b/opengl/tools/glgen/glspec-checks new file mode 100644 index 000000000..a84ed6533 --- /dev/null +++ b/opengl/tools/glgen/glspec-checks @@ -0,0 +1,59 @@ +glClipPlanef check equation 4 +glClipPlanex check equation 4 +glDeleteBuffers check buffers n +glDeleteTextures check textures n +glDrawElements check_AIOOBE indices count +glFog ifcheck params 1 pname GL_FOG_MODE,GL_FOG_DENSITY,GL_FOG_START,GL_FOG_END ifcheck params 4 pname GL_FOG_COLOR +glGenBuffers check buffers n +glGenTextures check textures n +glGetClipPlane check eqn 4 +glGetIntegerv ifcheck params 1 pname GL_ALPHA_BITS,GL_ALPHA_TEST_FUNC,GL_ALPHA_TEST_REF,GL_BLEND_DST,GL_BLUE_BITS,GL_COLOR_ARRAY_BUFFER_BINDING,GL_COLOR_ARRAY_SIZE,GL_COLOR_ARRAY_STRIDE,GL_COLOR_ARRAY_TYPE,GL_CULL_FACE,GL_DEPTH_BITS,GL_DEPTH_CLEAR_VALUE,GL_DEPTH_FUNC,GL_DEPTH_WRITEMASK,GL_FOG_DENSITY,GL_FOG_END,GL_FOG_MODE,GL_FOG_START,GL_FRONT_FACE,GL_GREEN_BITS,GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES,GL_IMPLEMENTATION_COLOR_READ_TYPE_OES,GL_LIGHT_MODEL_TWO_SIDE,GL_LINE_SMOOTH_HINT,GL_LINE_WIDTH,GL_LOGIC_OP_MODE,GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES,GL_MATRIX_INDEX_ARRAY_SIZE_OES,GL_MATRIX_INDEX_ARRAY_STRIDE_OES,GL_MATRIX_INDEX_ARRAY_TYPE_OES,GL_MATRIX_MODE,GL_MAX_CLIP_PLANES,GL_MAX_ELEMENTS_INDICES,GL_MAX_ELEMENTS_VERTICES,GL_MAX_LIGHTS,GL_MAX_MODELVIEW_STACK_DEPTH,GL_MAX_PALETTE_MATRICES_OES,GL_MAX_PROJECTION_STACK_DEPTH,GL_MAX_TEXTURE_SIZE,GL_MAX_TEXTURE_STACK_DEPTH,GL_MAX_TEXTURE_UNITS,GL_MAX_VERTEX_UNITS_OES,GL_MODELVIEW_STACK_DEPTH,GL_NORMAL_ARRAY_BUFFER_BINDING,GL_NORMAL_ARRAY_STRIDE,GL_NORMAL_ARRAY_TYPE,GL_NUM_COMPRESSED_TEXTURE_FORMATS,GL_PACK_ALIGNMENT,GL_PERSPECTIVE_CORRECTION_HINT,GL_POINT_SIZE,GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES,GL_POINT_SIZE_ARRAY_STRIDE_OES,GL_POINT_SIZE_ARRAY_TYPE_OES,GL_POINT_SMOOTH_HINT,GL_POLYGON_OFFSET_FACTOR,GL_POLYGON_OFFSET_UNITS,GL_PROJECTION_STACK_DEPTH,GL_RED_BITS,GL_SHADE_MODEL,GL_STENCIL_BITS,GL_STENCIL_CLEAR_VALUE,GL_STENCIL_FAIL,GL_STENCIL_FUNC,GL_STENCIL_PASS_DEPTH_FAIL,GL_STENCIL_PASS_DEPTH_PASS,GL_STENCIL_REF,GL_STENCIL_VALUE_MASK,GL_STENCIL_WRITEMASK,GL_SUBPIXEL_BITS,GL_TEXTURE_BINDING_2D,GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING,GL_TEXTURE_COORD_ARRAY_SIZE,GL_TEXTURE_COORD_ARRAY_STRIDE,GL_TEXTURE_COORD_ARRAY_TYPE,GL_TEXTURE_STACK_DEPTH,GL_UNPACK_ALIGNMENT,GL_VERTEX_ARRAY_BUFFER_BINDING,GL_VERTEX_ARRAY_SIZE,GL_VERTEX_ARRAY_STRIDE,GL_VERTEX_ARRAY_TYPE,GL_WEIGHT_ARRAY_BUFFER_BINDING_OES,GL_WEIGHT_ARRAY_SIZE_OES,GL_WEIGHT_ARRAY_STRIDE_OES,GL_WEIGHT_ARRAY_TYPE_OES ifcheck params 2 pname GL_ALIASED_POINT_SIZE_RANGE,GL_ALIASED_LINE_WIDTH_RANGE,GL_DEPTH_RANGE,GL_MAX_VIEWPORT_DIMS,GL_SMOOTH_LINE_WIDTH_RANGE,GL_SMOOTH_POINT_SIZE_RANGE ifcheck params 4 pname GL_COLOR_CLEAR_VALUE,GL_COLOR_WRITEMASK,GL_SCISSOR_BOX,GL_VIEWPORT ifcheck params 16 pname GL_MODELVIEW_MATRIX,GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES,GL_PROJECTION_MATRIX,GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES,GL_TEXTURE_MATRIX,GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES ifcheck params _NUM_COMPRESSED_TEXTURE_FORMATS pname GL_COMPRESSED_TEXTURE_FORMATS,GL_FOG_COLOR,GL_LIGHT_MODEL_AMBIENT +glGetLight ifcheck params 1 pname GL_SPOT_EXPONENT,GL_SPOT_CUTOFF,GL_CONSTANT_ATTENUATION,GL_LINEAR_ATTENUATION,GL_QUADRATIC_ATTENUATION ifcheck params 3 pname GL_SPOT_DIRECTION ifcheck params 4 pname GL_AMBIENT,GL_DIFFUSE,GL_SPECULAR,GL_EMISSION +glGetMaterial ifcheck params 1 pname GL_SHININESS ifcheck params 4 pname GL_AMBIENT,GL_DIFFUSE,GL_SPECULAR,GL_EMISSION,GL_AMBIENT_AND_DIFFUSE +glGetTexEnv ifcheck params 1 pname GL_TEXTURE_ENV_MODE,GL_COMBINE_RGB,GL_COMBINE_ALPHA ifcheck params 4 pname GL_TEXTURE_ENV_COLOR +glGetTexParameter check params 1 +glLightModel ifcheck params 1 pname GL_LIGHT_MODEL_TWO_SIDE ifcheck params 4 pname GL_LIGHT_MODEL_AMBIENT +glLight ifcheck params 1 pname GL_SPOT_EXPONENT,GL_SPOT_CUTOFF,GL_CONSTANT_ATTENUATION,GL_LINEAR_ATTENUATION,GL_QUADRATIC_ATTENUATION ifcheck params 3 pname GL_SPOT_DIRECTION ifcheck params 4 pname GL_AMBIENT,GL_DIFFUSE,GL_SPECULAR,GL_EMISSION +glLoadMatrix check m 16 +glMaterial ifcheck params 1 pname GL_SHININESS ifcheck params 4 pname GL_AMBIENT,GL_DIFFUSE,GL_SPECULAR,GL_EMISSION,GL_AMBIENT_AND_DIFFUSE +glMultMatrix check m 16 +glPointParameter check params 1 +glTexEnv ifcheck params 1 pname GL_TEXTURE_ENV_MODE,GL_COMBINE_RGB,GL_COMBINE_ALPHA ifcheck params 4 pname GL_TEXTURE_ENV_COLOR +glTexImage2D nullAllowed +glTexSubImage2D nullAllowed +glBufferData nullAllowed +glTexParameter check params 1 +glQueryMatrixxOES check mantissa 16 check exponent 16 return -1 +glDrawTexfvOES check coords 5 +glDrawTexivOES check coords 5 +glDrawTexsvOES check coords 5 +glDrawTexxvOES check coords 5 +glBindFramebufferOES unsupported +glBindRenderbufferOES unsupported +glBlendEquation unsupported +glBlendEquationSeparate unsupported +glBlendFuncSeparate unsupported +glCheckFramebufferStatusOES unsupported return 0 +glCurrentPaletteMatrixOES unsupported +glDeleteFramebuffersOES unsupported +glDeleteRenderbuffersOES unsupported +glFramebufferRenderbufferOES unsupported +glFramebufferStorageOES unsupported +glFramebufferTexture2DOES unsupported +glGenFramebuffersOES unsupported +glGenRenderbuffersOES unsupported +glGenerateMipmapOES unsupported +glGetBufferParameter unsupported +glGetFramebufferAttachmentParameterivOES unsupported +glGetRenderbufferParameterivOES unsupported +glGetTexGen unsupported +glIsFramebufferOES unsupported return JNI_FALSE +glIsRenderbufferOES unsupported return JNI_FALSE +glLoadPaletteFromModelViewMatrixOES unsupported +glMatrixIndexPointerOES unsupported +glRenderbufferStorageOES unsupported return false +glTexGen unsupported +glTexGenf unsupported +glTexGeni unsupported +glTexGenx unsupported +glWeightPointerOES unsupported diff --git a/opengl/tools/glgen/src/CFunc.java b/opengl/tools/glgen/src/CFunc.java new file mode 100644 index 000000000..0794f4174 --- /dev/null +++ b/opengl/tools/glgen/src/CFunc.java @@ -0,0 +1,155 @@ + +import java.util.*; + +public class CFunc { + + String original; + + CType ftype; + String fname; + + List argNames = new ArrayList(); + List argTypes = new ArrayList(); + + boolean hasPointerArg = false; + boolean hasTypedPointerArg = false; + + public CFunc(String original) { + this.original = original; + } + + public String getOriginal() { + return original; + } + + public void setName(String fname) { + this.fname = fname; + } + + public String getName() { + return fname; + } + + public void setType(CType ftype) { + this.ftype = ftype; + } + + public CType getType() { + return ftype; + } + + public void addArgument(String argName, CType argType) { + argNames.add(argName); + argTypes.add(argType); + + if (argType.isPointer()) { + hasPointerArg = true; + } + if (argType.isTypedPointer()) { + hasTypedPointerArg = true; + } + } + + public int getNumArgs() { + return argNames.size(); + } + + public int getArgIndex(String name) { + int len = argNames.size(); + for (int i = 0; i < len; i++) { + if (name.equals(argNames.get(i))) { + return i; + } + } + return -1; + } + + public String getArgName(int index) { + return argNames.get(index); + } + + public CType getArgType(int index) { + return argTypes.get(index); + } + + public boolean hasPointerArg() { + return hasPointerArg; + } + + public boolean hasTypedPointerArg() { + return hasTypedPointerArg; + } + + public String toString() { + String s = "Function " + fname + " returns " + ftype + ": "; + for (int i = 0; i < argNames.size(); i++) { + if (i > 0) { + s += ", "; + } + s += argTypes.get(i) + " " + argNames.get(i); + } + return s; + } + + public static CFunc parseCFunc(String s) { + CFunc cfunc = new CFunc(s); + String[] tokens = s.split("\\s"); + + int i = 0; + CType ftype = new CType(); + String ftypeName = tokens[i++]; + if (ftypeName.equals("const")) { + ftype.setIsConst(true); + ftypeName = tokens[i++]; + } + ftype.setBaseType(ftypeName); + + String fname = tokens[i++]; + if (fname.equals("*")) { + ftype.setIsPointer(true); + fname = tokens[i++]; + } + + cfunc.setName(fname); + cfunc.setType(ftype); + + while (i < tokens.length) { + String tok = tokens[i++]; + + if (tok.equals("(")) { + continue; + } + if (tok.equals(")")) { + break; + } + + CType argType = new CType(); + + String argTypeName = tok; + String argName = ""; + + if (argTypeName.equals("const")) { + argType.setIsConst(true); + argTypeName = tokens[i++]; + } + argType.setBaseType(argTypeName); + + if (argTypeName.equals("void")) { + break; + } + + argName = tokens[i++]; + if (argName.startsWith("*")) { + argType.setIsPointer(true); + argName = argName.substring(1, argName.length()); + } + if (argName.endsWith(",")) { + argName = argName.substring(0, argName.length() - 1); + } + + cfunc.addArgument(argName, argType); + } + + return cfunc; + } +} diff --git a/opengl/tools/glgen/src/CType.java b/opengl/tools/glgen/src/CType.java new file mode 100644 index 000000000..331ec625a --- /dev/null +++ b/opengl/tools/glgen/src/CType.java @@ -0,0 +1,85 @@ + +public class CType { + + String baseType; + boolean isConst; + boolean isPointer; + + public CType() { + } + + public CType(String baseType) { + setBaseType(baseType); + } + + public CType(String baseType, boolean isConst, boolean isPointer) { + setBaseType(baseType); + setIsConst(isConst); + setIsPointer(isPointer); + } + + public String getDeclaration() { + return baseType + (isPointer ? " *" : ""); + } + + public void setIsConst(boolean isConst) { + this.isConst = isConst; + } + + public boolean isConst() { + return isConst; + } + + public void setIsPointer(boolean isPointer) { + this.isPointer = isPointer; + } + + public boolean isPointer() { + return isPointer; + } + + boolean isVoid() { + String baseType = getBaseType(); + return baseType.equals("GLvoid") || + baseType.equals("void"); + } + + public boolean isTypedPointer() { + return isPointer() && !isVoid(); + } + + public void setBaseType(String baseType) { + this.baseType = baseType; + } + + public String getBaseType() { + return baseType; + } + + public String toString() { + String s = ""; + if (isConst()) { + s += "const "; + } + s += baseType; + if (isPointer()) { + s += "*"; + } + + return s; + } + + public int hashCode() { + return baseType.hashCode() ^ (isPointer ? 2 : 0) ^ (isConst ? 1 : 0); + } + + public boolean equals(Object o) { + if (o != null && o instanceof CType) { + CType c = (CType)o; + return baseType.equals(c.baseType) && + isPointer() == c.isPointer() && + isConst() == c.isConst(); + } + return false; + } +} diff --git a/opengl/tools/glgen/src/CodeEmitter.java b/opengl/tools/glgen/src/CodeEmitter.java new file mode 100644 index 000000000..3e9b90aab --- /dev/null +++ b/opengl/tools/glgen/src/CodeEmitter.java @@ -0,0 +1,8 @@ + +public interface CodeEmitter { + + void setVersion(int version, boolean ext, boolean pack); + void emitCode(CFunc cfunc, String original); + void addNativeRegistration(String fname); + void emitNativeRegistration(); +} diff --git a/opengl/tools/glgen/src/GenerateGL.java b/opengl/tools/glgen/src/GenerateGL.java new file mode 100644 index 000000000..657ee6e7c --- /dev/null +++ b/opengl/tools/glgen/src/GenerateGL.java @@ -0,0 +1,164 @@ + +import java.io.*; +import java.util.*; + +public class GenerateGL { + + static void copy(String filename, PrintStream out) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(filename)); + String s; + while ((s = br.readLine()) != null) { + out.println(s); + } + } + + private static void emit(int version, boolean ext, boolean pack, + CodeEmitter emitter, + BufferedReader specReader, + PrintStream glStream, + PrintStream glImplStream, + PrintStream cStream) throws Exception { + String s = null; + int counter = 0; + while ((s = specReader.readLine()) != null) { + if (s.trim().startsWith("//")) { + continue; + } + + CFunc cfunc = CFunc.parseCFunc(s); + + String fname = cfunc.getName(); + File f = new File("stubs/" + fname + + ".java-1" + version + "-if"); + if (f.exists()) { + System.out.println("Special-casing function " + fname); + copy("stubs/" + fname + + ".java-1" + version + "-if", glStream); + copy("stubs/" + fname + ".java-impl", glImplStream); + copy("stubs/" + fname + ".cpp", cStream); + + // Register native function names + // This should be improved to require fewer discrete files + String filename = "stubs/" + fname + ".nativeReg"; + BufferedReader br = + new BufferedReader(new FileReader(filename)); + String nfunc; + while ((nfunc = br.readLine()) != null) { + emitter.addNativeRegistration(nfunc); + } + } else { + emitter.setVersion(version, ext, pack); + emitter.emitCode(cfunc, s); + } + } + } + + public static void main(String[] args) throws Exception { + String classPathName = "com/google/android/gles_jni/GLImpl"; + boolean useContextPointer = true; + + int aidx = 0; + while (args[aidx].charAt(0) == '-') { + switch (args[aidx].charAt(1)) { + case 'c': + useContextPointer = false; + break; + + default: + System.err.println("Unknown flag: " + args[aidx]); + System.exit(1); + } + + aidx++; + } + + System.out.println("useContextPointer = " + useContextPointer); + + BufferedReader spec10Reader = + new BufferedReader(new FileReader(args[aidx++])); + BufferedReader spec10ExtReader = + new BufferedReader(new FileReader(args[aidx++])); + BufferedReader spec11Reader = + new BufferedReader(new FileReader(args[aidx++])); + BufferedReader spec11ExtReader = + new BufferedReader(new FileReader(args[aidx++])); + BufferedReader spec11ExtPackReader = + new BufferedReader(new FileReader(args[aidx++])); + BufferedReader checksReader = + new BufferedReader(new FileReader(args[aidx++])); + + String gl10Filename = "javax/microedition/khronos/opengles/GL10.java"; + String gl10ExtFilename = + "javax/microedition/khronos/opengles/GL10Ext.java"; + String gl11Filename = "javax/microedition/khronos/opengles/GL11.java"; + String gl11ExtFilename = + "javax/microedition/khronos/opengles/GL11Ext.java"; + String gl11ExtPackFilename = + "javax/microedition/khronos/opengles/GL11ExtensionPack.java"; + String glImplFilename = "com/google/android/gles_jni/GLImpl.java"; + String cFilename = "com_google_android_gles_jni_GLImpl.cpp"; + + PrintStream gl10Stream = + new PrintStream(new FileOutputStream("out/" + gl10Filename)); + PrintStream gl10ExtStream = + new PrintStream(new FileOutputStream("out/" + gl10ExtFilename)); + PrintStream gl11Stream = + new PrintStream(new FileOutputStream("out/" + gl11Filename)); + PrintStream gl11ExtStream = + new PrintStream(new FileOutputStream("out/" + gl11ExtFilename)); + PrintStream gl11ExtPackStream = + new PrintStream(new FileOutputStream("out/" + gl11ExtPackFilename)); + PrintStream glImplStream = + new PrintStream(new FileOutputStream("out/" + glImplFilename)); + PrintStream cStream = + new PrintStream(new FileOutputStream("out/" + cFilename)); + + ParameterChecker checker = new ParameterChecker(checksReader); + + CodeEmitter emitter = + new JniCodeEmitter(classPathName, + checker, + gl10Stream, gl10ExtStream, + gl11Stream, gl11ExtStream, gl11ExtPackStream, + glImplStream, cStream, + useContextPointer); + + gl10Stream.println("/* //device/java/android/" + gl10Filename); + gl10ExtStream.println("/* //device/java/android/" + gl10ExtFilename); + gl11Stream.println("/* //device/java/android/" + gl11Filename); + gl11ExtStream.println("/* //device/java/android/" + gl11ExtFilename); + gl11ExtPackStream.println("/* //device/java/android/" + + gl11ExtPackFilename); + glImplStream.println("/* //device/java/android/" + glImplFilename); + cStream.println("/* //device/libs/android_runtime/" + cFilename); + + copy("stubs/GL10Header.java-if", gl10Stream); + copy("stubs/GL10ExtHeader.java-if", gl10ExtStream); + copy("stubs/GL11Header.java-if", gl11Stream); + copy("stubs/GL11ExtHeader.java-if", gl11ExtStream); + copy("stubs/GL11ExtensionPackHeader.java-if", gl11ExtPackStream); + copy("stubs/GLImplHeader.java-impl", glImplStream); + copy("stubs/GLCHeader.cpp", cStream); + + emit(0, false, false, + emitter, spec10Reader, gl10Stream, glImplStream, cStream); + emit(0, true, false, + emitter, spec10ExtReader, gl10ExtStream, glImplStream, cStream); + emit(1, false, false, + emitter, spec11Reader, gl11Stream, glImplStream, cStream); + emit(1, true, false, + emitter, spec11ExtReader, gl11ExtStream, glImplStream, cStream); + emit(1, true, true, + emitter, spec11ExtPackReader, gl11ExtPackStream, glImplStream, + cStream); + + emitter.emitNativeRegistration(); + + gl10Stream.println("}"); + gl10ExtStream.println("}"); + gl11Stream.println("}"); + gl11ExtStream.println("}"); + gl11ExtPackStream.println("}"); + glImplStream.println("}"); + } +} diff --git a/opengl/tools/glgen/src/JFunc.java b/opengl/tools/glgen/src/JFunc.java new file mode 100644 index 000000000..42d466c33 --- /dev/null +++ b/opengl/tools/glgen/src/JFunc.java @@ -0,0 +1,148 @@ + +import java.util.ArrayList; +import java.util.List; + +public class JFunc { + + String className = "com.google.android.gles_jni.GL11Impl"; + + CFunc cfunc; + JType ftype; + String fname; + + List argNames = new ArrayList(); + List argTypes = new ArrayList(); + List argCIndices = new ArrayList(); + + boolean hasBufferArg = false; + boolean hasTypedBufferArg = false; + ArrayList bufferArgNames = new ArrayList(); + + public JFunc(CFunc cfunc) { + this.cfunc = cfunc; + } + + public CFunc getCFunc() { + return cfunc; + } + + public void setName(String fname) { + this.fname = fname; + } + + public String getName() { + return fname; + } + + public void setType(JType ftype) { + this.ftype = ftype; + } + + public JType getType() { + return ftype; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getClassName() { + return className; + } + + public boolean hasBufferArg() { + return hasBufferArg; + } + + public boolean hasTypedBufferArg() { + return hasTypedBufferArg; + } + + public String getBufferArgName(int index) { + return bufferArgNames.get(index); + } + + public void addArgument(String argName, JType argType, int cindex) { + argNames.add(argName); + argTypes.add(argType); + argCIndices.add(new Integer(cindex)); + + if (argType.isBuffer()) { + hasBufferArg = true; + bufferArgNames.add(argName); + } + if (argType.isTypedBuffer()) { + hasTypedBufferArg = true; + bufferArgNames.add(argName); + } + } + + public int getNumArgs() { + return argNames.size(); + } + + public int getArgIndex(String name) { + int len = argNames.size(); + for (int i = 0; i < len; i++) { + if (name.equals(argNames.get(i))) { + return i; + } + } + return -1; + } + + public String getArgName(int index) { + return argNames.get(index); + } + + public JType getArgType(int index) { + return argTypes.get(index); + } + + public int getArgCIndex(int index) { + return argCIndices.get(index).intValue(); + } + + public static JFunc convert(CFunc cfunc, boolean useArray) { + JFunc jfunc = new JFunc(cfunc); + jfunc.setName(cfunc.getName()); + jfunc.setType(JType.convert(cfunc.getType(), false)); + + int numArgs = cfunc.getNumArgs(); + int numOffsets = 0; + for (int i = 0; i < numArgs; i++) { + CType cArgType = cfunc.getArgType(i); + if (cArgType.isTypedPointer() && useArray) { + ++numOffsets; + } + } + + for (int i = 0; i < numArgs; i++) { + String cArgName = cfunc.getArgName(i); + CType cArgType = cfunc.getArgType(i); + + jfunc.addArgument(cArgName, JType.convert(cArgType, useArray), i); + if (cArgType.isTypedPointer() && useArray) { + if (numOffsets > 1) { + jfunc.addArgument(cArgName + "Offset", new JType("int"), i); + } else { + jfunc.addArgument("offset", new JType("int"), i); + } + } + } + + return jfunc; + } + + public String toString() { + String s = "Function " + fname + " returns " + ftype + ": "; + for (int i = 0; i < argNames.size(); i++) { + if (i > 0) { + s += ", "; + } + s += argTypes.get(i) + " " + argNames.get(i); + } + return s; + } + +} diff --git a/opengl/tools/glgen/src/JType.java b/opengl/tools/glgen/src/JType.java new file mode 100644 index 000000000..a16d4400d --- /dev/null +++ b/opengl/tools/glgen/src/JType.java @@ -0,0 +1,139 @@ + +import java.util.HashMap; + +public class JType { + + String baseType; + boolean isArray; + boolean isClass; + + static HashMap typeMapping = new HashMap(); + static HashMap arrayTypeMapping = new HashMap(); + + static { + // Primitive types + typeMapping.put(new CType("GLbitfield"), new JType("int")); + typeMapping.put(new CType("GLboolean"), new JType("boolean")); + typeMapping.put(new CType("GLclampf"), new JType("float")); + typeMapping.put(new CType("GLclampx"), new JType("int")); + typeMapping.put(new CType("GLenum"), new JType("int")); + typeMapping.put(new CType("GLfloat"), new JType("float")); + typeMapping.put(new CType("GLfixed"), new JType("int")); + typeMapping.put(new CType("GLint"), new JType("int")); + typeMapping.put(new CType("GLintptr"), new JType("int")); + typeMapping.put(new CType("GLshort"), new JType("short")); + typeMapping.put(new CType("GLsizei"), new JType("int")); + typeMapping.put(new CType("GLsizeiptr"), new JType("int")); + typeMapping.put(new CType("GLubyte"), new JType("byte")); + typeMapping.put(new CType("GLuint"), new JType("int")); + typeMapping.put(new CType("void"), new JType("void")); + typeMapping.put(new CType("GLubyte", true, true), new JType("String")); + + // Untyped pointers map to untyped Buffers + typeMapping.put(new CType("GLvoid", true, true), + new JType("java.nio.Buffer", true, false)); + typeMapping.put(new CType("GLvoid", false, true), + new JType("java.nio.Buffer", true, false)); + typeMapping.put(new CType("void", false, true), + new JType("java.nio.Buffer", true, false)); + + // Typed pointers map to typed Buffers + typeMapping.put(new CType("GLboolean", false, true), + new JType("java.nio.IntBuffer", true, false)); + typeMapping.put(new CType("GLfixed", false, true), + new JType("java.nio.IntBuffer", true, false)); + typeMapping.put(new CType("GLfixed", true, true), + new JType("java.nio.IntBuffer", true, false)); + typeMapping.put(new CType("GLfloat", false, true), + new JType("java.nio.FloatBuffer", true, false)); + typeMapping.put(new CType("GLfloat", true, true), + new JType("java.nio.FloatBuffer", true, false)); + typeMapping.put(new CType("GLint", false, true), + new JType("java.nio.IntBuffer", true, false)); + typeMapping.put(new CType("GLint", true, true), + new JType("java.nio.IntBuffer", true, false)); + typeMapping.put(new CType("GLuint", false, true), + new JType("java.nio.IntBuffer", true, false)); + typeMapping.put(new CType("GLuint", true, true), + new JType("java.nio.IntBuffer", true, false)); + typeMapping.put(new CType("GLshort", true, true), + new JType("java.nio.ShortBuffer", true, false)); + + // Typed pointers map to arrays + offsets + arrayTypeMapping.put(new CType("GLboolean", false, true), + new JType("boolean", false, true)); + arrayTypeMapping.put(new CType("GLfixed", true, true), new JType("int", false, true)); + arrayTypeMapping.put(new CType("GLfixed", false, true), new JType("int", false, true)); + arrayTypeMapping.put(new CType("GLfloat", false, true), new JType("float", false, true)); + arrayTypeMapping.put(new CType("GLfloat", true, true), new JType("float", false, true)); + arrayTypeMapping.put(new CType("GLint", false, true), new JType("int", false, true)); + arrayTypeMapping.put(new CType("GLint", true, true), new JType("int", false, true)); + arrayTypeMapping.put(new CType("GLshort", true, true), new JType("short", false, true)); + arrayTypeMapping.put(new CType("GLuint", false, true), new JType("int", false, true)); + arrayTypeMapping.put(new CType("GLuint", true, true), new JType("int", false, true)); + arrayTypeMapping.put(new CType("GLintptr"), new JType("int", false, true)); + arrayTypeMapping.put(new CType("GLsizeiptr"), new JType("int", false, true)); + } + + public JType() { + } + + public JType(String primitiveTypeName) { + this.baseType = primitiveTypeName; + this.isClass = false; + this.isArray = false; + } + + public JType(String primitiveTypeName, boolean isClass, boolean isArray) { + this.baseType = primitiveTypeName; + this.isClass = isClass; + this.isArray = isArray; + } + + public String getBaseType() { + return baseType; + } + + public String toString() { + return baseType + (isArray ? "[]" : ""); + } + + public boolean isArray() { + return isArray; + } + + public boolean isClass() { + return isClass; + } + + public boolean isPrimitive() { + return !isClass() && !isArray(); + } + + public boolean isVoid() { + return baseType.equals("void"); + } + + public boolean isBuffer() { + return baseType.indexOf("Buffer") != -1; + } + + public boolean isTypedBuffer() { + return !baseType.equals("java.nio.Buffer") && + (baseType.indexOf("Buffer") != -1); + } + + public static JType convert(CType ctype, boolean useArray) { + JType javaType = null; + if (useArray) { + javaType = arrayTypeMapping.get(ctype); + } + if (javaType == null) { + javaType = typeMapping.get(ctype); + } + if (javaType == null) { + throw new RuntimeException("Unsupported C type: " + ctype); + } + return javaType; + } +} diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java new file mode 100644 index 000000000..33b9a3e53 --- /dev/null +++ b/opengl/tools/glgen/src/JniCodeEmitter.java @@ -0,0 +1,1086 @@ +import java.io.PrintStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +/** + * Emits a Java interface and Java & C implementation for a C function. + * + *

    The Java interface will have Buffer and array variants for functions that + * have a typed pointer argument. The array variant will convert a single " *data" + * argument to a pair of arguments "[] data, int offset". + */ +public class JniCodeEmitter implements CodeEmitter { + + // If true, use C++ style for calling through a JNIEnv *: + // env->Func(...) + // If false, use C style: + // (*env)->Func(env, ...) + static final boolean mUseCPlusPlus = true; + + boolean mUseContextPointer = true; + + String mClassPathName; + + ParameterChecker mChecker; + PrintStream mJava10InterfaceStream; + PrintStream mJava10ExtInterfaceStream; + PrintStream mJava11InterfaceStream; + PrintStream mJava11ExtInterfaceStream; + PrintStream mJava11ExtPackInterfaceStream; + PrintStream mJavaImplStream; + PrintStream mCStream; + + PrintStream mJavaInterfaceStream; + + List nativeRegistrations = new ArrayList(); + + boolean needsExit; + + static String indent = " "; + + HashSet mFunctionsEmitted = new HashSet(); + + /** + * @param java10InterfaceStream the PrintStream to which to emit the Java interface for GL 1.0 functions + * @param java10ExtInterfaceStream the PrintStream to which to emit the Java interface for GL 1.0 extension functions + * @param java11InterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 functions + * @param java11ExtInterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 Extension functions + * @param java11ExtPackInterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 Extension Pack functions + * @param javaImplStream the PrintStream to which to emit the Java implementation + * @param cStream the PrintStream to which to emit the C implementation + */ + public JniCodeEmitter(String classPathName, + ParameterChecker checker, + PrintStream java10InterfaceStream, + PrintStream java10ExtInterfaceStream, + PrintStream java11InterfaceStream, + PrintStream java11ExtInterfaceStream, + PrintStream java11ExtPackInterfaceStream, + PrintStream javaImplStream, + PrintStream cStream, + boolean useContextPointer) { + mClassPathName = classPathName; + mChecker = checker; + mJava10InterfaceStream = java10InterfaceStream; + mJava10ExtInterfaceStream = java10ExtInterfaceStream; + mJava11InterfaceStream = java11InterfaceStream; + mJava11ExtInterfaceStream = java11ExtInterfaceStream; + mJava11ExtPackInterfaceStream = java11ExtPackInterfaceStream; + mJavaImplStream = javaImplStream; + mCStream = cStream; + mUseContextPointer = useContextPointer; + } + + public void setVersion(int version, boolean ext, boolean pack) { + if (version == 0) { + mJavaInterfaceStream = ext ? mJava10ExtInterfaceStream : + mJava10InterfaceStream; + } else if (version == 1) { + mJavaInterfaceStream = ext ? + (pack ? mJava11ExtPackInterfaceStream : + mJava11ExtInterfaceStream) : + mJava11InterfaceStream; + } else { + throw new RuntimeException("Bad version: " + version); + } + } + + public void emitCode(CFunc cfunc, String original) { + JFunc jfunc; + String signature; + boolean duplicate; + + if (cfunc.hasTypedPointerArg()) { + jfunc = JFunc.convert(cfunc, true); + + // Don't emit duplicate functions + // These may appear because they are defined in multiple + // Java interfaces (e.g., GL11/GL11ExtensionPack) + signature = jfunc.toString(); + duplicate = false; + if (mFunctionsEmitted.contains(signature)) { + duplicate = true; + } else { + mFunctionsEmitted.add(signature); + } + + if (!duplicate) { + emitNativeDeclaration(jfunc, mJavaImplStream); + emitJavaCode(jfunc, mJavaImplStream); + } + emitJavaInterfaceCode(jfunc, mJavaInterfaceStream); + if (!duplicate) { + emitJniCode(jfunc, mCStream); + } + } + + jfunc = JFunc.convert(cfunc, false); + + signature = jfunc.toString(); + duplicate = false; + if (mFunctionsEmitted.contains(signature)) { + duplicate = true; + } else { + mFunctionsEmitted.add(signature); + } + + if (!duplicate) { + emitNativeDeclaration(jfunc, mJavaImplStream); + } + emitJavaInterfaceCode(jfunc, mJavaInterfaceStream); + if (!duplicate) { + emitJavaCode(jfunc, mJavaImplStream); + emitJniCode(jfunc, mCStream); + } + } + + public void emitNativeDeclaration(JFunc jfunc, PrintStream out) { + out.println(" // C function " + jfunc.getCFunc().getOriginal()); + out.println(); + + emitFunction(jfunc, out, true, false); + } + + public void emitJavaInterfaceCode(JFunc jfunc, PrintStream out) { + emitFunction(jfunc, out, false, true); + } + + public void emitJavaCode(JFunc jfunc, PrintStream out) { + emitFunction(jfunc, out, false, false); + } + + void emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray ) { + boolean isVoid = jfunc.getType().isVoid(); + boolean isPointerFunc = jfunc.getName().endsWith("Pointer") && + jfunc.getCFunc().hasPointerArg(); + + if (!isVoid) { + out.println(iii + + jfunc.getType() + " _returnValue;"); + } + out.println(iii + + (isVoid ? "" : "_returnValue = ") + + jfunc.getName() + + (isPointerFunc ? "Bounds" : "" ) + + "("); + + int numArgs = jfunc.getNumArgs(); + for (int i = 0; i < numArgs; i++) { + String argName = jfunc.getArgName(i); + JType argType = jfunc.getArgType(i); + + if (grabArray && argType.isTypedBuffer()) { + String typeName = argType.getBaseType(); + typeName = typeName.substring(9, typeName.length() - 6); + out.println(iii + indent + "get" + typeName + "Array(" + argName + "),"); + out.print(iii + indent + "getOffset(" + argName + ")"); + } else { + out.print(iii + indent + argName); + } + if (i == numArgs - 1) { + if (isPointerFunc) { + out.println(","); + out.println(iii + indent + argName + ".remaining()"); + } else { + out.println(); + } + } else { + out.println(","); + } + } + + out.println(iii + ");"); + } + + void printIfcheckPostamble(PrintStream out, boolean isBuffer, + boolean emitExceptionCheck, String iii) { + printIfcheckPostamble(out, isBuffer, emitExceptionCheck, + "offset", "_remaining", iii); + } + + void printIfcheckPostamble(PrintStream out, boolean isBuffer, + boolean emitExceptionCheck, + String offset, String remaining, String iii) { + out.println(iii + " default:"); + out.println(iii + " _needed = 0;"); + out.println(iii + " break;"); + out.println(iii + "}"); + + out.println(iii + "if (" + remaining + " < _needed) {"); + if (emitExceptionCheck) { + out.println(iii + indent + "_exception = 1;"); + } + out.println(iii + indent + + (mUseCPlusPlus ? "_env" : "(*_env)") + + "->ThrowNew(" + + (mUseCPlusPlus ? "" : "_env, ") + + "IAEClass, " + + "\"" + + (isBuffer ? + "remaining()" : "length - " + offset) + + " < needed\");"); + out.println(iii + indent + "goto exit;"); + needsExit = true; + out.println(iii + "}"); + } + + boolean isNullAllowed(CFunc cfunc) { + String[] checks = mChecker.getChecks(cfunc.getName()); + int index = 1; + if (checks != null) { + while (index < checks.length) { + if (checks[index].equals("return")) { + index += 2; + } else if (checks[index].startsWith("check")) { + index += 3; + } else if (checks[index].equals("ifcheck")) { + index += 5; + } else if (checks[index].equals("unsupported")) { + index += 1; + } else if (checks[index].equals("nullAllowed")) { + return true; + } else { + System.out.println("Error: unknown keyword \"" + + checks[index] + "\""); + System.exit(0); + } + } + } + return false; + } + + String getErrorReturnValue(CFunc cfunc) { + CType returnType = cfunc.getType(); + boolean isVoid = returnType.isVoid(); + if (isVoid) { + return null; + } + + String[] checks = mChecker.getChecks(cfunc.getName()); + + int index = 1; + if (checks != null) { + while (index < checks.length) { + if (checks[index].equals("return")) { + return checks[index + 1]; + } else if (checks[index].startsWith("check")) { + index += 3; + } else if (checks[index].equals("ifcheck")) { + index += 5; + } else if (checks[index].equals("unsupported")) { + index += 1; + } else if (checks[index].equals("nullAllowed")) { + index += 1; + } else { + System.out.println("Error: unknown keyword \"" + + checks[index] + "\""); + System.exit(0); + } + } + } + + return null; + } + + boolean isUnsupportedFunc(CFunc cfunc) { + String[] checks = mChecker.getChecks(cfunc.getName()); + int index = 1; + if (checks != null) { + while (index < checks.length) { + if (checks[index].equals("unsupported")) { + return true; + } else if (checks[index].equals("return")) { + index += 2; + } else if (checks[index].startsWith("check")) { + index += 3; + } else if (checks[index].equals("ifcheck")) { + index += 5; + } else if (checks[index].equals("nullAllowed")) { + index += 1; + } else { + System.out.println("Error: unknown keyword \"" + + checks[index] + "\""); + System.exit(0); + } + } + } + return false; + } + + void emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out, + boolean isBuffer, boolean emitExceptionCheck, + String offset, String remaining, String iii) { + CType returnType = cfunc.getType(); + boolean isVoid = returnType.isVoid(); + + String[] checks = mChecker.getChecks(cfunc.getName()); + String checkVar; + String retval = getErrorReturnValue(cfunc); + + boolean lastWasIfcheck = false; + + int index = 1; + if (checks != null) { + boolean remainingDeclared = false; + boolean nullCheckDeclared = false; + boolean offsetChecked = false; + while (index < checks.length) { + if (checks[index].startsWith("check")) { + if (lastWasIfcheck) { + printIfcheckPostamble(out, isBuffer, emitExceptionCheck, + offset, remaining, iii); + } + lastWasIfcheck = false; + if (cname != null && !cname.equals(checks[index + 1])) { + index += 3; + continue; + } + out.println(iii + "if (" + remaining + " < " + + checks[index + 2] + + ") {"); + if (emitExceptionCheck) { + out.println(iii + indent + "_exception = 1;"); + } + String exceptionClassName = "IAEClass"; + // If the "check" keyword was of the form + // "check_", use the class name in the + // exception to be thrown + int underscore = checks[index].indexOf('_'); + if (underscore >= 0) { + exceptionClassName = checks[index].substring(underscore + 1) + "Class"; + } + out.println(iii + indent + + (mUseCPlusPlus ? "_env" : "(*_env)") + + "->ThrowNew(" + + (mUseCPlusPlus ? "" : "_env, ") + + exceptionClassName + ", " + + "\"" + + (isBuffer ? + "remaining()" : "length - " + offset) + + " < " + checks[index + 2] + + "\");"); + + out.println(iii + indent + "goto exit;"); + needsExit = true; + out.println(iii + "}"); + + index += 3; + } else if (checks[index].equals("ifcheck")) { + String[] matches = checks[index + 4].split(","); + + if (!lastWasIfcheck) { + out.println(iii + "int _needed;"); + out.println(iii + + "switch (" + + checks[index + 3] + + ") {"); + } + + for (int i = 0; i < matches.length; i++) { + out.println("#if defined(" + matches[i] + ")"); + out.println(iii + + " case " + + matches[i] + + ":"); + out.println("#endif // defined(" + matches[i] + ")"); + } + out.println(iii + + " _needed = " + + checks[index + 2] + + ";"); + out.println(iii + + " break;"); + + lastWasIfcheck = true; + index += 5; + } else if (checks[index].equals("return")) { + // ignore + index += 2; + } else if (checks[index].equals("unsupported")) { + // ignore + index += 1; + } else if (checks[index].equals("nullAllowed")) { + // ignore + index += 1; + } else { + System.out.println("Error: unknown keyword \"" + + checks[index] + "\""); + System.exit(0); + } + } + } + + if (lastWasIfcheck) { + printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii); + } + } + + boolean hasNonConstArg(JFunc jfunc, CFunc cfunc, + List nonPrimitiveArgs) { + if (nonPrimitiveArgs.size() > 0) { + for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) { + int idx = nonPrimitiveArgs.get(i).intValue(); + int cIndex = jfunc.getArgCIndex(idx); + if (jfunc.getArgType(idx).isArray()) { + if (!cfunc.getArgType(cIndex).isConst()) { + return true; + } + } else if (jfunc.getArgType(idx).isBuffer()) { + if (!cfunc.getArgType(cIndex).isConst()) { + return true; + } + } + } + } + + return false; + } + + /** + * Emit a function in several variants: + * + * if nativeDecl: public native func(args); + * + * if !nativeDecl: + * if interfaceDecl: public func(args); + * if !interfaceDecl: public func(args) { body } + */ + void emitFunction(JFunc jfunc, + PrintStream out, + boolean nativeDecl, boolean interfaceDecl) { + boolean isPointerFunc = + jfunc.getName().endsWith("Pointer") && + jfunc.getCFunc().hasPointerArg(); + + if (!nativeDecl && !interfaceDecl && !isPointerFunc) { + // If it's not a pointer function, we've already emitted it + // with nativeDecl == true + return; + } + + if (isPointerFunc) { + out.println(indent + + (nativeDecl ? "private native " : + (interfaceDecl ? "" : "public ")) + + jfunc.getType() + " " + + jfunc.getName() + + (nativeDecl ? "Bounds" : "") + + "("); + } else { + out.println(indent + + (nativeDecl ? "public native " : + (interfaceDecl ? "" : "public ")) + + jfunc.getType() + " " + + jfunc.getName() + + "("); + } + + int numArgs = jfunc.getNumArgs(); + for (int i = 0; i < numArgs; i++) { + String argName = jfunc.getArgName(i); + JType argType = jfunc.getArgType(i); + + out.print(indent + indent + argType + " " + argName); + if (i == numArgs - 1) { + if (isPointerFunc && nativeDecl) { + out.println(","); + out.println(indent + indent + "int remaining"); + } else { + out.println(); + } + } else { + out.println(","); + } + } + + if (nativeDecl || interfaceDecl) { + out.println(indent + ");"); + } else { + out.println(indent + ") {"); + + String iii = indent + indent; + + String fname = jfunc.getName(); + if (isPointerFunc) { + // TODO - deal with VBO variants + if (fname.equals("glColorPointer")) { + out.println(iii + "if ((size == 4) &&"); + out.println(iii + " ((type == GL_FLOAT) ||"); + out.println(iii + " (type == GL_UNSIGNED_BYTE) ||"); + out.println(iii + " (type == GL_FIXED)) &&"); + out.println(iii + " (stride >= 0)) {"); + out.println(iii + indent + "_colorPointer = pointer;"); + out.println(iii + "}"); + } else if (fname.equals("glNormalPointer")) { + out.println(iii + "if (((type == GL_FLOAT) ||"); + out.println(iii + " (type == GL_BYTE) ||"); + out.println(iii + " (type == GL_SHORT) ||"); + out.println(iii + " (type == GL_FIXED)) &&"); + out.println(iii + " (stride >= 0)) {"); + out.println(iii + indent + "_normalPointer = pointer;"); + out.println(iii + "}"); + } else if (fname.equals("glTexCoordPointer")) { + out.println(iii + "if (((size == 2) ||"); + out.println(iii + " (size == 3) ||"); + out.println(iii + " (size == 4)) &&"); + out.println(iii + " ((type == GL_FLOAT) ||"); + out.println(iii + " (type == GL_BYTE) ||"); + out.println(iii + " (type == GL_SHORT) ||"); + out.println(iii + " (type == GL_FIXED)) &&"); + out.println(iii + " (stride >= 0)) {"); + out.println(iii + indent + "_texCoordPointer = pointer;"); + out.println(iii + "}"); + } else if (fname.equals("glVertexPointer")) { + out.println(iii + "if (((size == 2) ||"); + out.println(iii + " (size == 3) ||"); + out.println(iii + " (size == 4)) &&"); + out.println(iii + " ((type == GL_FLOAT) ||"); + out.println(iii + " (type == GL_BYTE) ||"); + out.println(iii + " (type == GL_SHORT) ||"); + out.println(iii + " (type == GL_FIXED)) &&"); + out.println(iii + " (stride >= 0)) {"); + out.println(iii + indent + "_vertexPointer = pointer;"); + out.println(iii + "}"); + } + } + + // emitBoundsChecks(jfunc, out, iii); + emitFunctionCall(jfunc, out, iii, false); + + boolean isVoid = jfunc.getType().isVoid(); + + if (!isVoid) { + out.println(indent + indent + "return _returnValue;"); + } + out.println(indent + "}"); + } + out.println(); + } + + public static String getJniName(JType jType) { + String jniName = ""; + if (jType.isClass()) { + return "L" + jType.getBaseType() + ";"; + } else if (jType.isArray()) { + jniName = "["; + } + + String baseType = jType.getBaseType(); + if (baseType.equals("int")) { + jniName += "I"; + } else if (baseType.equals("float")) { + jniName += "F"; + } else if (baseType.equals("boolean")) { + jniName += "Z"; + } else if (baseType.equals("short")) { + jniName += "S"; + } else if (baseType.equals("long")) { + jniName += "L"; + } else if (baseType.equals("byte")) { + jniName += "B"; + } + return jniName; + } + + String getJniType(JType jType) { + if (jType.isVoid()) { + return "void"; + } + + String baseType = jType.getBaseType(); + if (jType.isPrimitive()) { + if (baseType.equals("String")) { + return "jstring"; + } else { + return "j" + baseType; + } + } else if (jType.isArray()) { + return "j" + baseType + "Array"; + } else { + return "jobject"; + } + } + + String getJniMangledName(String name) { + name = name.replaceAll("_", "_1"); + name = name.replaceAll(";", "_2"); + name = name.replaceAll("\\[", "_3"); + return name; + } + + public void emitJniCode(JFunc jfunc, PrintStream out) { + CFunc cfunc = jfunc.getCFunc(); + + // Emit comment identifying original C function + // + // Example: + // + // /* void glClipPlanef ( GLenum plane, const GLfloat *equation ) */ + // + out.println("/* " + cfunc.getOriginal() + " */"); + + // Emit JNI signature (name) + // + // Example: + // + // void + // android_glClipPlanef__I_3FI + // + + String outName = "android_" + jfunc.getName(); + boolean isPointerFunc = outName.endsWith("Pointer") && + jfunc.getCFunc().hasPointerArg(); + boolean isVBOPointerFunc = (outName.endsWith("Pointer") || + outName.endsWith("DrawElements")) && + !jfunc.getCFunc().hasPointerArg(); + if (isPointerFunc) { + outName += "Bounds"; + } + + out.print("static "); + out.println(getJniType(jfunc.getType())); + out.print(outName); + + String rsignature = getJniName(jfunc.getType()); + + String signature = ""; + int numArgs = jfunc.getNumArgs(); + for (int i = 0; i < numArgs; i++) { + JType argType = jfunc.getArgType(i); + signature += getJniName(argType); + } + if (isPointerFunc) { + signature += "I"; + } + + // Append signature to function name + String sig = getJniMangledName(signature).replace('.', '_'); + out.print("__" + sig); + outName += "__" + sig; + + signature = signature.replace('.', '/'); + rsignature = rsignature.replace('.', '/'); + + out.println(); + if (rsignature.length() == 0) { + rsignature = "V"; + } + + String s = "{\"" + + jfunc.getName() + + (isPointerFunc ? "Bounds" : "") + + "\", \"(" + signature +")" + + rsignature + + "\", (void *) " + + outName + + " },"; + nativeRegistrations.add(s); + + List nonPrimitiveArgs = new ArrayList(); + int numBufferArgs = 0; + List bufferArgNames = new ArrayList(); + + // Emit JNI signature (arguments) + // + // Example: + // + // (JNIEnv *_env, jobject this, jint plane, jfloatArray equation_ref, jint offset) { + // + out.print(" (JNIEnv *_env, jobject _this"); + for (int i = 0; i < numArgs; i++) { + out.print(", "); + JType argType = jfunc.getArgType(i); + String suffix; + if (!argType.isPrimitive()) { + if (argType.isArray()) { + suffix = "_ref"; + } else { + suffix = "_buf"; + } + nonPrimitiveArgs.add(new Integer(i)); + if (jfunc.getArgType(i).isBuffer()) { + int cIndex = jfunc.getArgCIndex(i); + String cname = cfunc.getArgName(cIndex); + bufferArgNames.add(cname); + numBufferArgs++; + } + } else { + suffix = ""; + } + + out.print(getJniType(argType) + " " + jfunc.getArgName(i) + suffix); + } + if (isPointerFunc) { + out.print(", jint remaining"); + } + out.println(") {"); + + int numArrays = 0; + int numBuffers = 0; + for (int i = 0; i < nonPrimitiveArgs.size(); i++) { + int idx = nonPrimitiveArgs.get(i).intValue(); + int cIndex = jfunc.getArgCIndex(idx); + String cname = cfunc.getArgName(cIndex); + if (jfunc.getArgType(idx).isArray()) { + ++numArrays; + } + if (jfunc.getArgType(idx).isBuffer()) { + ++numBuffers; + } + } + + // Emit method body + + // Emit local variable declarations for _exception and _returnValue + // + // Example: + // + // android::gl::ogles_context_t *ctx; + // + // jint _exception; + // GLenum _returnValue; + // + CType returnType = cfunc.getType(); + boolean isVoid = returnType.isVoid(); + + boolean isUnsupported = isUnsupportedFunc(cfunc); + if (isUnsupported) { + out.println(indent + + "_env->ThrowNew(UOEClass,"); + out.println(indent + + " \"" + cfunc.getName() + "\");"); + if (!isVoid) { + String retval = getErrorReturnValue(cfunc); + out.println(indent + "return " + retval + ";"); + } + out.println("}"); + out.println(); + return; + } + + if (mUseContextPointer) { + out.println(indent + + "android::gl::ogles_context_t *ctx = getContext(_env, _this);"); + } + + boolean emitExceptionCheck = (numArrays > 0 || numBuffers > 0) && + hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs); + // mChecker.getChecks(cfunc.getName()) != null + + // Emit an _exeption variable if there will be error checks + if (emitExceptionCheck) { + out.println(indent + "jint _exception = 0;"); + } + + // Emit a single _array or multiple _XXXArray variables + if (numBufferArgs == 1) { + out.println(indent + "jarray _array = (jarray) 0;"); + } else { + for (int i = 0; i < numBufferArgs; i++) { + out.println(indent + "jarray _" + bufferArgNames.get(i) + + "Array = (jarray) 0;"); + } + } + if (!isVoid) { + String retval = getErrorReturnValue(cfunc); + if (retval != null) { + out.println(indent + returnType.getDeclaration() + + " _returnValue = " + retval + ";"); + } else { + out.println(indent + returnType.getDeclaration() + + " _returnValue;"); + } + } + + // Emit local variable declarations for pointer arguments + // + // Example: + // + // GLfixed *eqn_base; + // GLfixed *eqn; + // + String offset = "offset"; + String remaining = "_remaining"; + if (nonPrimitiveArgs.size() > 0) { + for (int i = 0; i < nonPrimitiveArgs.size(); i++) { + int idx = nonPrimitiveArgs.get(i).intValue(); + int cIndex = jfunc.getArgCIndex(idx); + String cname = cfunc.getArgName(cIndex); + + CType type = cfunc.getArgType(jfunc.getArgCIndex(idx)); + String decl = type.getDeclaration(); + if (jfunc.getArgType(idx).isArray()) { + out.println(indent + + decl + + (decl.endsWith("*") ? "" : " ") + + jfunc.getArgName(idx) + + "_base = (" + decl + ") 0;"); + } + remaining = (numArrays <= 1 && numBuffers <= 1) ? "_remaining" : + "_" + cname + "Remaining"; + out.println(indent + + "jint " + remaining + ";"); + out.println(indent + + decl + + (decl.endsWith("*") ? "" : " ") + + jfunc.getArgName(idx) + + " = (" + decl + ") 0;"); + } + + out.println(); + } + + String retval = isVoid ? "" : " _returnValue"; + + // Emit 'GetPrimitiveArrayCritical' for arrays + // Emit 'GetPointer' calls for Buffer pointers + int bufArgIdx = 0; + if (nonPrimitiveArgs.size() > 0) { + for (int i = 0; i < nonPrimitiveArgs.size(); i++) { + int idx = nonPrimitiveArgs.get(i).intValue(); + int cIndex = jfunc.getArgCIndex(idx); + + String cname = cfunc.getArgName(cIndex); + offset = numArrays <= 1 ? "offset" : + cname + "Offset"; + remaining = (numArrays <= 1 && numBuffers <= 1) ? "_remaining" : + "_" + cname + "Remaining"; + + if (jfunc.getArgType(idx).isArray()) { + out.println(indent + + "if (!" + + cname + + "_ref) {"); + if (emitExceptionCheck) { + out.println(indent + indent + "_exception = 1;"); + } + out.println(indent + " " + + (mUseCPlusPlus ? "_env" : "(*_env)") + + "->ThrowNew(" + + (mUseCPlusPlus ? "" : "_env, ") + + "IAEClass, " + + "\"" + cname + + " == null\");"); + out.println(indent + " goto exit;"); + needsExit = true; + out.println(indent + "}"); + + out.println(indent + "if (" + offset + " < 0) {"); + if (emitExceptionCheck) { + out.println(indent + indent + "_exception = 1;"); + } + out.println(indent + " " + + (mUseCPlusPlus ? "_env" : "(*_env)") + + "->ThrowNew(" + + (mUseCPlusPlus ? "" : "_env, ") + + "IAEClass, " + + "\"" + offset + " < 0\");"); + out.println(indent + " goto exit;"); + needsExit = true; + out.println(indent + "}"); + + out.println(indent + remaining + " = " + + (mUseCPlusPlus ? "_env" : "(*_env)") + + "->GetArrayLength(" + + (mUseCPlusPlus ? "" : "_env, ") + + cname + "_ref) - " + offset + ";"); + + emitNativeBoundsChecks(cfunc, cname, out, false, + emitExceptionCheck, + offset, remaining, " "); + + out.println(indent + + cname + + "_base = (" + + cfunc.getArgType(cIndex).getDeclaration() + + ")"); + out.println(indent + " " + + (mUseCPlusPlus ? "_env" : "(*_env)") + + "->GetPrimitiveArrayCritical(" + + (mUseCPlusPlus ? "" : "_env, ") + + jfunc.getArgName(idx) + + "_ref, (jboolean *)0);"); + out.println(indent + + cname + " = " + cname + "_base + " + offset + + ";"); + out.println(); + } else { + String array = numBufferArgs <= 1 ? "_array" : + "_" + bufferArgNames.get(bufArgIdx++) + "Array"; + + boolean nullAllowed = isNullAllowed(cfunc); + if (nullAllowed) { + out.println(indent + "if (" + cname + "_buf) {"); + out.print(indent); + } + + out.println(indent + + cname + + " = (" + + cfunc.getArgType(cIndex).getDeclaration() + + ")getPointer(_env, " + + cname + + "_buf, &" + array + ", &" + remaining + ");"); + + if (nullAllowed) { + out.println(indent + "}"); + } + + emitNativeBoundsChecks(cfunc, cname, out, true, + emitExceptionCheck, + offset, remaining, " "); + } + } + } + + if (!isVoid) { + out.print(indent + "_returnValue = "); + } else { + out.print(indent); + } + String name = cfunc.getName(); + + if (mUseContextPointer) { + name = name.substring(2, name.length()); // Strip off 'gl' prefix + name = name.substring(0, 1).toLowerCase() + + name.substring(1, name.length()); + out.print("ctx->procs."); + } + + out.print(name + (isPointerFunc ? "Bounds" : "") + "("); + + numArgs = cfunc.getNumArgs(); + if (numArgs == 0) { + if (mUseContextPointer) { + out.println("ctx);"); + } else { + out.println(");"); + } + } else { + if (mUseContextPointer) { + out.println("ctx,"); + } else { + out.println(); + } + for (int i = 0; i < numArgs; i++) { + String typecast; + if (i == numArgs - 1 && isVBOPointerFunc) { + typecast = "const GLvoid *"; + } else { + typecast = cfunc.getArgType(i).getDeclaration(); + } + out.print(indent + indent + + "(" + + typecast + + ")" + + cfunc.getArgName(i)); + + if (i == numArgs - 1) { + if (isPointerFunc) { + out.println(","); + out.println(indent + indent + "(GLsizei)remaining"); + } else { + out.println(); + } + } else { + out.println(","); + } + } + out.println(indent + ");"); + } + + if (needsExit) { + out.println(); + out.println("exit:"); + needsExit = false; + } + + bufArgIdx = 0; + if (nonPrimitiveArgs.size() > 0) { + for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) { + int idx = nonPrimitiveArgs.get(i).intValue(); + + int cIndex = jfunc.getArgCIndex(idx); + if (jfunc.getArgType(idx).isArray()) { + + // If the argument is 'const', GL will not write to it. + // In this case, we can use the 'JNI_ABORT' flag to avoid + // the need to write back to the Java array + out.println(indent + + "if (" + jfunc.getArgName(idx) + "_base) {"); + out.println(indent + indent + + (mUseCPlusPlus ? "_env" : "(*_env)") + + "->ReleasePrimitiveArrayCritical(" + + (mUseCPlusPlus ? "" : "_env, ") + + jfunc.getArgName(idx) + "_ref, " + + cfunc.getArgName(cIndex) + + "_base,"); + out.println(indent + indent + indent + + (cfunc.getArgType(cIndex).isConst() ? + "JNI_ABORT" : + "_exception ? JNI_ABORT: 0") + + ");"); + out.println(indent + "}"); + } else if (jfunc.getArgType(idx).isBuffer()) { + String array = numBufferArgs <= 1 ? "_array" : + "_" + bufferArgNames.get(bufArgIdx++) + "Array"; + out.println(indent + "if (" + array + ") {"); + out.println(indent + indent + + "releasePointer(_env, " + array + ", " + + cfunc.getArgName(cIndex) + + ", " + + (cfunc.getArgType(cIndex).isConst() ? + "JNI_FALSE" : "_exception ? JNI_FALSE : JNI_TRUE") + + ");"); + out.println(indent + "}"); + } + } + } + + if (!isVoid) { + out.println(indent + "return _returnValue;"); + } + + out.println("}"); + out.println(); + } + + public void addNativeRegistration(String s) { + nativeRegistrations.add(s); + } + + public void emitNativeRegistration() { + mCStream.println("static const char *classPathName = \"" + + mClassPathName + + "\";"); + mCStream.println(); + + mCStream.println("static JNINativeMethod methods[] = {"); + + mCStream.println("{\"_nativeClassInit\", \"()V\", (void*)nativeClassInit },"); + + Iterator i = nativeRegistrations.iterator(); + while (i.hasNext()) { + mCStream.println(i.next()); + } + + mCStream.println("};"); + mCStream.println(); + + + mCStream.println("int register_com_google_android_gles_jni_GLImpl(JNIEnv *_env)"); + mCStream.println("{"); + mCStream.println(indent + + "int err;"); + + mCStream.println(indent + + "err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));"); + + mCStream.println(indent + "return err;"); + mCStream.println("}"); + } +} diff --git a/opengl/tools/glgen/src/ParameterChecker.java b/opengl/tools/glgen/src/ParameterChecker.java new file mode 100644 index 000000000..df26acdf4 --- /dev/null +++ b/opengl/tools/glgen/src/ParameterChecker.java @@ -0,0 +1,28 @@ + +import java.io.BufferedReader; +import java.util.HashMap; + +public class ParameterChecker { + + HashMap map = new HashMap(); + + public ParameterChecker(BufferedReader reader) throws Exception { + String s; + while ((s = reader.readLine()) != null) { + String[] tokens = s.split("\\s"); + map.put(tokens[0], tokens); + } + } + + public String[] getChecks(String functionName) { + String[] checks = map.get(functionName); + if (checks == null && + (functionName.endsWith("fv") || + functionName.endsWith("xv") || + functionName.endsWith("iv"))) { + functionName = functionName.substring(0, functionName.length() - 2); + checks = map.get(functionName); + } + return checks; + } +} diff --git a/opengl/tools/glgen/stubs/GL10ExtHeader.java-if b/opengl/tools/glgen/stubs/GL10ExtHeader.java-if new file mode 100644 index 000000000..b0999c25f --- /dev/null +++ b/opengl/tools/glgen/stubs/GL10ExtHeader.java-if @@ -0,0 +1,22 @@ +** +** Copyright 2007, 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. +*/ + +// This source file is automatically generated + +package javax.microedition.khronos.opengles; + +public interface GL10Ext extends GL { + diff --git a/opengl/tools/glgen/stubs/GL10Header.java-if b/opengl/tools/glgen/stubs/GL10Header.java-if new file mode 100644 index 000000000..8392821e7 --- /dev/null +++ b/opengl/tools/glgen/stubs/GL10Header.java-if @@ -0,0 +1,259 @@ +** +** Copyright 2006, 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. +*/ + +// This source file is automatically generated + +package javax.microedition.khronos.opengles; + +public interface GL10 extends GL { + int GL_ADD = 0x0104; + int GL_ALIASED_LINE_WIDTH_RANGE = 0x846E; + int GL_ALIASED_POINT_SIZE_RANGE = 0x846D; + int GL_ALPHA = 0x1906; + int GL_ALPHA_BITS = 0x0D55; + int GL_ALPHA_TEST = 0x0BC0; + int GL_ALWAYS = 0x0207; + int GL_AMBIENT = 0x1200; + int GL_AMBIENT_AND_DIFFUSE = 0x1602; + int GL_AND = 0x1501; + int GL_AND_INVERTED = 0x1504; + int GL_AND_REVERSE = 0x1502; + int GL_BACK = 0x0405; + int GL_BLEND = 0x0BE2; + int GL_BLUE_BITS = 0x0D54; + int GL_BYTE = 0x1400; + int GL_CCW = 0x0901; + int GL_CLAMP_TO_EDGE = 0x812F; + int GL_CLEAR = 0x1500; + int GL_COLOR_ARRAY = 0x8076; + int GL_COLOR_BUFFER_BIT = 0x4000; + int GL_COLOR_LOGIC_OP = 0x0BF2; + int GL_COLOR_MATERIAL = 0x0B57; + int GL_COMPRESSED_TEXTURE_FORMATS = 0x86A3; + int GL_CONSTANT_ATTENUATION = 0x1207; + int GL_COPY = 0x1503; + int GL_COPY_INVERTED = 0x150C; + int GL_CULL_FACE = 0x0B44; + int GL_CW = 0x0900; + int GL_DECAL = 0x2101; + int GL_DECR = 0x1E03; + int GL_DEPTH_BITS = 0x0D56; + int GL_DEPTH_BUFFER_BIT = 0x0100; + int GL_DEPTH_TEST = 0x0B71; + int GL_DIFFUSE = 0x1201; + int GL_DITHER = 0x0BD0; + int GL_DONT_CARE = 0x1100; + int GL_DST_ALPHA = 0x0304; + int GL_DST_COLOR = 0x0306; + int GL_EMISSION = 0x1600; + int GL_EQUAL = 0x0202; + int GL_EQUIV = 0x1509; + int GL_EXP = 0x0800; + int GL_EXP2 = 0x0801; + int GL_EXTENSIONS = 0x1F03; + int GL_FALSE = 0; + int GL_FASTEST = 0x1101; + int GL_FIXED = 0x140C; + int GL_FLAT = 0x1D00; + int GL_FLOAT = 0x1406; + int GL_FOG = 0x0B60; + int GL_FOG_COLOR = 0x0B66; + int GL_FOG_DENSITY = 0x0B62; + int GL_FOG_END = 0x0B64; + int GL_FOG_HINT = 0x0C54; + int GL_FOG_MODE = 0x0B65; + int GL_FOG_START = 0x0B63; + int GL_FRONT = 0x0404; + int GL_FRONT_AND_BACK = 0x0408; + int GL_GEQUAL = 0x0206; + int GL_GREATER = 0x0204; + int GL_GREEN_BITS = 0x0D53; + int GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES = 0x8B9B; + int GL_IMPLEMENTATION_COLOR_READ_TYPE_OES = 0x8B9A; + int GL_INCR = 0x1E02; + int GL_INVALID_ENUM = 0x0500; + int GL_INVALID_OPERATION = 0x0502; + int GL_INVALID_VALUE = 0x0501; + int GL_INVERT = 0x150A; + int GL_KEEP = 0x1E00; + int GL_LEQUAL = 0x0203; + int GL_LESS = 0x0201; + int GL_LIGHT_MODEL_AMBIENT = 0x0B53; + int GL_LIGHT_MODEL_TWO_SIDE = 0x0B52; + int GL_LIGHT0 = 0x4000; + int GL_LIGHT1 = 0x4001; + int GL_LIGHT2 = 0x4002; + int GL_LIGHT3 = 0x4003; + int GL_LIGHT4 = 0x4004; + int GL_LIGHT5 = 0x4005; + int GL_LIGHT6 = 0x4006; + int GL_LIGHT7 = 0x4007; + int GL_LIGHTING = 0x0B50; + int GL_LINE_LOOP = 0x0002; + int GL_LINE_SMOOTH = 0x0B20; + int GL_LINE_SMOOTH_HINT = 0x0C52; + int GL_LINE_STRIP = 0x0003; + int GL_LINEAR = 0x2601; + int GL_LINEAR_ATTENUATION = 0x1208; + int GL_LINEAR_MIPMAP_LINEAR = 0x2703; + int GL_LINEAR_MIPMAP_NEAREST = 0x2701; + int GL_LINES = 0x0001; + int GL_LUMINANCE = 0x1909; + int GL_LUMINANCE_ALPHA = 0x190A; + int GL_MAX_ELEMENTS_INDICES = 0x80E9; + int GL_MAX_ELEMENTS_VERTICES = 0x80E8; + int GL_MAX_LIGHTS = 0x0D31; + int GL_MAX_MODELVIEW_STACK_DEPTH = 0x0D36; + int GL_MAX_PROJECTION_STACK_DEPTH = 0x0D38; + int GL_MAX_TEXTURE_SIZE = 0x0D33; + int GL_MAX_TEXTURE_STACK_DEPTH = 0x0D39; + int GL_MAX_TEXTURE_UNITS = 0x84E2; + int GL_MAX_VIEWPORT_DIMS = 0x0D3A; + int GL_MODELVIEW = 0x1700; + int GL_MODULATE = 0x2100; + int GL_MULTISAMPLE = 0x809D; + int GL_NAND = 0x150E; + int GL_NEAREST = 0x2600; + int GL_NEAREST_MIPMAP_LINEAR = 0x2702; + int GL_NEAREST_MIPMAP_NEAREST = 0x2700; + int GL_NEVER = 0x0200; + int GL_NICEST = 0x1102; + int GL_NO_ERROR = 0; + int GL_NOOP = 0x1505; + int GL_NOR = 0x1508; + int GL_NORMAL_ARRAY = 0x8075; + int GL_NORMALIZE = 0x0BA1; + int GL_NOTEQUAL = 0x0205; + int GL_NUM_COMPRESSED_TEXTURE_FORMATS = 0x86A2; + int GL_ONE = 1; + int GL_ONE_MINUS_DST_ALPHA = 0x0305; + int GL_ONE_MINUS_DST_COLOR = 0x0307; + int GL_ONE_MINUS_SRC_ALPHA = 0x0303; + int GL_ONE_MINUS_SRC_COLOR = 0x0301; + int GL_OR = 0x1507; + int GL_OR_INVERTED = 0x150D; + int GL_OR_REVERSE = 0x150B; + int GL_OUT_OF_MEMORY = 0x0505; + int GL_PACK_ALIGNMENT = 0x0D05; + int GL_PALETTE4_R5_G6_B5_OES = 0x8B92; + int GL_PALETTE4_RGB5_A1_OES = 0x8B94; + int GL_PALETTE4_RGB8_OES = 0x8B90; + int GL_PALETTE4_RGBA4_OES = 0x8B93; + int GL_PALETTE4_RGBA8_OES = 0x8B91; + int GL_PALETTE8_R5_G6_B5_OES = 0x8B97; + int GL_PALETTE8_RGB5_A1_OES = 0x8B99; + int GL_PALETTE8_RGB8_OES = 0x8B95; + int GL_PALETTE8_RGBA4_OES = 0x8B98; + int GL_PALETTE8_RGBA8_OES = 0x8B96; + int GL_PERSPECTIVE_CORRECTION_HINT = 0x0C50; + int GL_POINT_SMOOTH = 0x0B10; + int GL_POINT_SMOOTH_HINT = 0x0C51; + int GL_POINTS = 0x0000; + int GL_POINT_FADE_THRESHOLD_SIZE = 0x8128; + int GL_POINT_SIZE = 0x0B11; + int GL_POLYGON_OFFSET_FILL = 0x8037; + int GL_POLYGON_SMOOTH_HINT = 0x0C53; + int GL_POSITION = 0x1203; + int GL_PROJECTION = 0x1701; + int GL_QUADRATIC_ATTENUATION = 0x1209; + int GL_RED_BITS = 0x0D52; + int GL_RENDERER = 0x1F01; + int GL_REPEAT = 0x2901; + int GL_REPLACE = 0x1E01; + int GL_RESCALE_NORMAL = 0x803A; + int GL_RGB = 0x1907; + int GL_RGBA = 0x1908; + int GL_SAMPLE_ALPHA_TO_COVERAGE = 0x809E; + int GL_SAMPLE_ALPHA_TO_ONE = 0x809F; + int GL_SAMPLE_COVERAGE = 0x80A0; + int GL_SCISSOR_TEST = 0x0C11; + int GL_SET = 0x150F; + int GL_SHININESS = 0x1601; + int GL_SHORT = 0x1402; + int GL_SMOOTH = 0x1D01; + int GL_SMOOTH_LINE_WIDTH_RANGE = 0x0B22; + int GL_SMOOTH_POINT_SIZE_RANGE = 0x0B12; + int GL_SPECULAR = 0x1202; + int GL_SPOT_CUTOFF = 0x1206; + int GL_SPOT_DIRECTION = 0x1204; + int GL_SPOT_EXPONENT = 0x1205; + int GL_SRC_ALPHA = 0x0302; + int GL_SRC_ALPHA_SATURATE = 0x0308; + int GL_SRC_COLOR = 0x0300; + int GL_STACK_OVERFLOW = 0x0503; + int GL_STACK_UNDERFLOW = 0x0504; + int GL_STENCIL_BITS = 0x0D57; + int GL_STENCIL_BUFFER_BIT = 0x0400; + int GL_STENCIL_TEST = 0x0B90; + int GL_SUBPIXEL_BITS = 0x0D50; + int GL_TEXTURE = 0x1702; + int GL_TEXTURE_2D = 0x0DE1; + int GL_TEXTURE_COORD_ARRAY = 0x8078; + int GL_TEXTURE_ENV = 0x2300; + int GL_TEXTURE_ENV_COLOR = 0x2201; + int GL_TEXTURE_ENV_MODE = 0x2200; + int GL_TEXTURE_MAG_FILTER = 0x2800; + int GL_TEXTURE_MIN_FILTER = 0x2801; + int GL_TEXTURE_WRAP_S = 0x2802; + int GL_TEXTURE_WRAP_T = 0x2803; + int GL_TEXTURE0 = 0x84C0; + int GL_TEXTURE1 = 0x84C1; + int GL_TEXTURE2 = 0x84C2; + int GL_TEXTURE3 = 0x84C3; + int GL_TEXTURE4 = 0x84C4; + int GL_TEXTURE5 = 0x84C5; + int GL_TEXTURE6 = 0x84C6; + int GL_TEXTURE7 = 0x84C7; + int GL_TEXTURE8 = 0x84C8; + int GL_TEXTURE9 = 0x84C9; + int GL_TEXTURE10 = 0x84CA; + int GL_TEXTURE11 = 0x84CB; + int GL_TEXTURE12 = 0x84CC; + int GL_TEXTURE13 = 0x84CD; + int GL_TEXTURE14 = 0x84CE; + int GL_TEXTURE15 = 0x84CF; + int GL_TEXTURE16 = 0x84D0; + int GL_TEXTURE17 = 0x84D1; + int GL_TEXTURE18 = 0x84D2; + int GL_TEXTURE19 = 0x84D3; + int GL_TEXTURE20 = 0x84D4; + int GL_TEXTURE21 = 0x84D5; + int GL_TEXTURE22 = 0x84D6; + int GL_TEXTURE23 = 0x84D7; + int GL_TEXTURE24 = 0x84D8; + int GL_TEXTURE25 = 0x84D9; + int GL_TEXTURE26 = 0x84DA; + int GL_TEXTURE27 = 0x84DB; + int GL_TEXTURE28 = 0x84DC; + int GL_TEXTURE29 = 0x84DD; + int GL_TEXTURE30 = 0x84DE; + int GL_TEXTURE31 = 0x84DF; + int GL_TRIANGLE_FAN = 0x0006; + int GL_TRIANGLE_STRIP = 0x0005; + int GL_TRIANGLES = 0x0004; + int GL_TRUE = 1; + int GL_UNPACK_ALIGNMENT = 0x0CF5; + int GL_UNSIGNED_BYTE = 0x1401; + int GL_UNSIGNED_SHORT = 0x1403; + int GL_UNSIGNED_SHORT_4_4_4_4 = 0x8033; + int GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034; + int GL_UNSIGNED_SHORT_5_6_5 = 0x8363; + int GL_VENDOR = 0x1F00; + int GL_VERSION = 0x1F02; + int GL_VERTEX_ARRAY = 0x8074; + int GL_XOR = 0x1506; + int GL_ZERO = 0; + diff --git a/opengl/tools/glgen/stubs/GL11ExtHeader.java-if b/opengl/tools/glgen/stubs/GL11ExtHeader.java-if new file mode 100644 index 000000000..7be2164f6 --- /dev/null +++ b/opengl/tools/glgen/stubs/GL11ExtHeader.java-if @@ -0,0 +1,40 @@ +** +** Copyright 2007, 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. +*/ + +// This source file is automatically generated + +package javax.microedition.khronos.opengles; + +public interface GL11Ext extends GL { + int GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES = 0x8B9E; + int GL_MATRIX_INDEX_ARRAY_OES = 0x8844; + int GL_MATRIX_INDEX_ARRAY_POINTER_OES = 0x8849; + int GL_MATRIX_INDEX_ARRAY_SIZE_OES = 0x8846; + int GL_MATRIX_INDEX_ARRAY_STRIDE_OES = 0x8848; + int GL_MATRIX_INDEX_ARRAY_TYPE_OES = 0x8847; + int GL_MATRIX_PALETTE_OES = 0x8840; + int GL_MAX_PALETTE_MATRICES_OES = 0x8842; + int GL_MAX_VERTEX_UNITS_OES = 0x86A4; + int GL_TEXTURE_CROP_RECT_OES = 0x8B9D; + int GL_WEIGHT_ARRAY_BUFFER_BINDING_OES = 0x889E; + int GL_WEIGHT_ARRAY_OES = 0x86AD; + int GL_WEIGHT_ARRAY_POINTER_OES = 0x86AC; + int GL_WEIGHT_ARRAY_SIZE_OES = 0x86AB; + int GL_WEIGHT_ARRAY_STRIDE_OES = 0x86AA; + int GL_WEIGHT_ARRAY_TYPE_OES = 0x86A9; + + void glTexParameterfv(int target, int pname, float[] param, int offset); + diff --git a/opengl/tools/glgen/stubs/GL11ExtensionPackHeader.java-if b/opengl/tools/glgen/stubs/GL11ExtensionPackHeader.java-if new file mode 100644 index 000000000..a8001919c --- /dev/null +++ b/opengl/tools/glgen/stubs/GL11ExtensionPackHeader.java-if @@ -0,0 +1,108 @@ +** +** Copyright 2007, 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. +*/ + +// This source file is automatically generated + +package javax.microedition.khronos.opengles; + +public interface GL11ExtensionPack extends GL { + int GL_BLEND_DST_ALPHA = 0x80CA; + int GL_BLEND_DST_RGB = 0x80C8; + int GL_BLEND_EQUATION = 0x8009; + int GL_BLEND_EQUATION_ALPHA = 0x883D; + int GL_BLEND_EQUATION_RGB = 0x8009; + int GL_BLEND_SRC_ALPHA = 0x80CB; + int GL_BLEND_SRC_RGB = 0x80C9; + int GL_COLOR_ATTACHMENT0_OES = 0x8CE0; + int GL_COLOR_ATTACHMENT1_OES = 0x8CE1; + int GL_COLOR_ATTACHMENT2_OES = 0x8CE2; + int GL_COLOR_ATTACHMENT3_OES = 0x8CE3; + int GL_COLOR_ATTACHMENT4_OES = 0x8CE4; + int GL_COLOR_ATTACHMENT5_OES = 0x8CE5; + int GL_COLOR_ATTACHMENT6_OES = 0x8CE6; + int GL_COLOR_ATTACHMENT7_OES = 0x8CE7; + int GL_COLOR_ATTACHMENT8_OES = 0x8CE8; + int GL_COLOR_ATTACHMENT9_OES = 0x8CE9; + int GL_COLOR_ATTACHMENT10_OES = 0x8CEA; + int GL_COLOR_ATTACHMENT11_OES = 0x8CEB; + int GL_COLOR_ATTACHMENT12_OES = 0x8CEC; + int GL_COLOR_ATTACHMENT13_OES = 0x8CED; + int GL_COLOR_ATTACHMENT14_OES = 0x8CEE; + int GL_COLOR_ATTACHMENT15_OES = 0x8CEF; + int GL_DECR_WRAP = 0x8508; + int GL_DEPTH_ATTACHMENT_OES = 0x8D00; + int GL_DEPTH_COMPONENT = 0x1902; + int GL_DEPTH_COMPONENT16 = 0x81A5; + int GL_DEPTH_COMPONENT24 = 0x81A6; + int GL_DEPTH_COMPONENT32 = 0x81A7; + int GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_OES = 0x8CD1; + int GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_OES = 0x8CD0; + int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_OES = 0x8CD3; + int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES = 0x8CD2; + int GL_FRAMEBUFFER_BINDING_OES = 0x8CA6; + int GL_FRAMEBUFFER_COMPLETE_OES = 0x8CD5; + int GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES = 0x8CD6; + int GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES = 0x8CD9; + int GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES = 0x8CDB; + int GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES = 0x8CDA; + int GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES = 0x8CD7; + int GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES = 0x8CDC; + int GL_FRAMEBUFFER_OES = 0x8D40; + int GL_FRAMEBUFFER_UNSUPPORTED_OES = 0x8CDD; + int GL_FUNC_ADD = 0x8006; + int GL_FUNC_REVERSE_SUBTRACT = 0x800B; + int GL_FUNC_SUBTRACT = 0x800A; + int GL_INCR_WRAP = 0x8507; + int GL_INVALID_FRAMEBUFFER_OPERATION_OES = 0x0506; + int GL_MAX_COLOR_ATTACHMENTS_OES = 0x8CDF; + int GL_MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C; + int GL_MAX_RENDERBUFFER_SIZE_OES = 0x84E8; + int GL_MIRRORED_REPEAT = 0x8370; + int GL_NORMAL_MAP = 0x8511; + int GL_REFLECTION_MAP = 0x8512; + int GL_RENDERBUFFER_ALPHA_SIZE_OES = 0x8D53; + int GL_RENDERBUFFER_BINDING_OES = 0x8CA7; + int GL_RENDERBUFFER_BLUE_SIZE_OES = 0x8D52; + int GL_RENDERBUFFER_DEPTH_SIZE_OES = 0x8D54; + int GL_RENDERBUFFER_GREEN_SIZE_OES = 0x8D51; + int GL_RENDERBUFFER_HEIGHT_OES = 0x8D43; + int GL_RENDERBUFFER_INTERNAL_FORMAT_OES = 0x8D44; + int GL_RENDERBUFFER_OES = 0x8D41; + int GL_RENDERBUFFER_RED_SIZE_OES = 0x8D50; + int GL_RENDERBUFFER_STENCIL_SIZE_OES = 0x8D55; + int GL_RENDERBUFFER_WIDTH_OES = 0x8D42; + int GL_RGB5_A1 = 0x8057; + int GL_RGB565_OES = 0x8D62; + int GL_RGB8 = 0x8051; + int GL_RGBA4 = 0x8056; + int GL_RGBA8 = 0x8058; + int GL_STENCIL_ATTACHMENT_OES = 0x8D20; + int GL_STENCIL_INDEX = 0x1901; + int GL_STENCIL_INDEX1_OES = 0x8D46; + int GL_STENCIL_INDEX4_OES = 0x8D47; + int GL_STENCIL_INDEX8_OES = 0x8D48; + int GL_STR = -1; + int GL_TEXTURE_BINDING_CUBE_MAP = 0x8514; + int GL_TEXTURE_CUBE_MAP = 0x8513; + int GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516; + int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518; + int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A; + int GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515; + int GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517; + int GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519; + int GL_TEXTURE_GEN_MODE = 0x2500; + int GL_TEXTURE_GEN_STR = 0x8D60; + diff --git a/opengl/tools/glgen/stubs/GL11Header.java-if b/opengl/tools/glgen/stubs/GL11Header.java-if new file mode 100644 index 000000000..b0e5a6bea --- /dev/null +++ b/opengl/tools/glgen/stubs/GL11Header.java-if @@ -0,0 +1,145 @@ +** +** Copyright 2006, 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. +*/ + +// This source file is automatically generated + +package javax.microedition.khronos.opengles; + +public interface GL11 extends GL10 { + int GL_ACTIVE_TEXTURE = 0x84E0; + int GL_ADD_SIGNED = 0x8574; + int GL_ALPHA_SCALE = 0x0D1C; + int GL_ALPHA_TEST_FUNC = 0x0BC1; + int GL_ALPHA_TEST_REF = 0x0BC2; + int GL_ARRAY_BUFFER = 0x8892; + int GL_ARRAY_BUFFER_BINDING = 0x8894; + int GL_BLEND_DST = 0x0BE0; + int GL_BLEND_SRC = 0x0BE1; + int GL_BUFFER_ACCESS = 0x88BB; + int GL_BUFFER_SIZE = 0x8764; + int GL_BUFFER_USAGE = 0x8765; + int GL_CLIENT_ACTIVE_TEXTURE = 0x84E1; + int GL_CLIP_PLANE0 = 0x3000; + int GL_CLIP_PLANE1 = 0x3001; + int GL_CLIP_PLANE2 = 0x3002; + int GL_CLIP_PLANE3 = 0x3003; + int GL_CLIP_PLANE4 = 0x3004; + int GL_CLIP_PLANE5 = 0x3005; + int GL_COLOR_ARRAY_BUFFER_BINDING = 0x8898; + int GL_COLOR_ARRAY_POINTER = 0x8090; + int GL_COLOR_ARRAY_SIZE = 0x8081; + int GL_COLOR_ARRAY_STRIDE = 0x8083; + int GL_COLOR_ARRAY_TYPE = 0x8082; + int GL_COLOR_CLEAR_VALUE = 0x0C22; + int GL_COLOR_WRITEMASK = 0x0C23; + int GL_COMBINE = 0x8570; + int GL_COMBINE_ALPHA = 0x8572; + int GL_COMBINE_RGB = 0x8571; + int GL_CONSTANT = 0x8576; + int GL_COORD_REPLACE_OES = 0x8862; + int GL_CULL_FACE_MODE = 0x0B45; + int GL_CURRENT_COLOR = 0x0B00; + int GL_CURRENT_NORMAL = 0x0B02; + int GL_CURRENT_TEXTURE_COORDS = 0x0B03; + int GL_DEPTH_CLEAR_VALUE = 0x0B73; + int GL_DEPTH_FUNC = 0x0B74; + int GL_DEPTH_RANGE = 0x0B70; + int GL_DEPTH_WRITEMASK = 0x0B72; + int GL_DOT3_RGB = 0x86AE; + int GL_DOT3_RGBA = 0x86AF; + int GL_DYNAMIC_DRAW = 0x88E8; + int GL_ELEMENT_ARRAY_BUFFER = 0x8893; + int GL_ELEMENT_ARRAY_BUFFER_BINDING = 0x8895; + int GL_FRONT_FACE = 0x0B46; + int GL_GENERATE_MIPMAP = 0x8191; + int GL_GENERATE_MIPMAP_HINT = 0x8192; + int GL_INTERPOLATE = 0x8575; + int GL_LINE_WIDTH = 0x0B21; + int GL_LOGIC_OP_MODE = 0x0BF0; + int GL_MATRIX_MODE = 0x0BA0; + int GL_MAX_CLIP_PLANES = 0x0D32; + int GL_MODELVIEW_MATRIX = 0x0BA6; + int GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES = 0x898D; + int GL_MODELVIEW_STACK_DEPTH = 0x0BA3; + int GL_NORMAL_ARRAY_BUFFER_BINDING = 0x8897; + int GL_NORMAL_ARRAY_POINTER = 0x808F; + int GL_NORMAL_ARRAY_STRIDE = 0x807F; + int GL_NORMAL_ARRAY_TYPE = 0x807E; + int GL_OPERAND0_ALPHA = 0x8598; + int GL_OPERAND0_RGB = 0x8590; + int GL_OPERAND1_ALPHA = 0x8599; + int GL_OPERAND1_RGB = 0x8591; + int GL_OPERAND2_ALPHA = 0x859A; + int GL_OPERAND2_RGB = 0x8592; + int GL_POINT_DISTANCE_ATTENUATION = 0x8129; + int GL_POINT_FADE_THRESHOLD_SIZE = 0x8128; + int GL_POINT_SIZE = 0x0B11; + int GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES = 0x8B9F; + int GL_POINT_SIZE_ARRAY_OES = 0x8B9C; + int GL_POINT_SIZE_ARRAY_POINTER_OES = 0x898C; + int GL_POINT_SIZE_ARRAY_STRIDE_OES = 0x898B; + int GL_POINT_SIZE_ARRAY_TYPE_OES = 0x898A; + int GL_POINT_SIZE_MAX = 0x8127; + int GL_POINT_SIZE_MIN = 0x8126; + int GL_POINT_SPRITE_OES = 0x8861; + int GL_POLYGON_OFFSET_FACTOR = 0x8038; + int GL_POLYGON_OFFSET_UNITS = 0x2A00; + int GL_PREVIOUS = 0x8578; + int GL_PRIMARY_COLOR = 0x8577; + int GL_PROJECTION_MATRIX = 0x0BA7; + int GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES = 0x898E; + int GL_PROJECTION_STACK_DEPTH = 0x0BA4; + int GL_RGB_SCALE = 0x8573; + int GL_SAMPLE_BUFFERS = 0x80A8; + int GL_SAMPLE_COVERAGE_INVERT = 0x80AB; + int GL_SAMPLE_COVERAGE_VALUE = 0x80AA; + int GL_SAMPLES = 0x80A9; + int GL_SCISSOR_BOX = 0x0C10; + int GL_SHADE_MODEL = 0x0B54; + int GL_SRC0_ALPHA = 0x8588; + int GL_SRC0_RGB = 0x8580; + int GL_SRC1_ALPHA = 0x8589; + int GL_SRC1_RGB = 0x8581; + int GL_SRC2_ALPHA = 0x858A; + int GL_SRC2_RGB = 0x8582; + int GL_STATIC_DRAW = 0x88E4; + int GL_STENCIL_CLEAR_VALUE = 0x0B91; + int GL_STENCIL_FAIL = 0x0B94; + int GL_STENCIL_FUNC = 0x0B92; + int GL_STENCIL_PASS_DEPTH_FAIL = 0x0B95; + int GL_STENCIL_PASS_DEPTH_PASS = 0x0B96; + int GL_STENCIL_REF = 0x0B97; + int GL_STENCIL_VALUE_MASK = 0x0B93; + int GL_STENCIL_WRITEMASK = 0x0B98; + int GL_SUBTRACT = 0x84E7; + int GL_TEXTURE_BINDING_2D = 0x8069; + int GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING = 0x889A; + int GL_TEXTURE_COORD_ARRAY_POINTER = 0x8092; + int GL_TEXTURE_COORD_ARRAY_SIZE = 0x8088; + int GL_TEXTURE_COORD_ARRAY_STRIDE = 0x808A; + int GL_TEXTURE_COORD_ARRAY_TYPE = 0x8089; + int GL_TEXTURE_MATRIX = 0x0BA8; + int GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES = 0x898F; + int GL_TEXTURE_STACK_DEPTH = 0x0BA5; + int GL_VERTEX_ARRAY_BUFFER_BINDING = 0x8896; + int GL_VERTEX_ARRAY_POINTER = 0x808E; + int GL_VERTEX_ARRAY_SIZE = 0x807A; + int GL_VERTEX_ARRAY_STRIDE = 0x807C; + int GL_VERTEX_ARRAY_TYPE = 0x807B; + int GL_VIEWPORT = 0x0BA2; + int GL_WRITE_ONLY = 0x88B9; + + void glGetPointerv(int pname, java.nio.Buffer[] params); diff --git a/opengl/tools/glgen/stubs/GL11ImplHeader.java-impl b/opengl/tools/glgen/stubs/GL11ImplHeader.java-impl new file mode 100644 index 000000000..501be659c --- /dev/null +++ b/opengl/tools/glgen/stubs/GL11ImplHeader.java-impl @@ -0,0 +1,30 @@ +// Copyright 2006 The Android Open Source Project + +// All Rights Reserved. + +// This source file is automatically generated + +package com.google.android.gles_jni; + +import java.nio.Buffer; +import javax.microedition.khronos.opengles.GL11; +import android.graphics.Canvas; + +public class GL11Impl implements GL11 { + + // Private accessors for native code + + native private static void _nativeClassInit(); + static { + _nativeClassInit(); + } + + Buffer _colorPointer = null; + Buffer _normalPointer = null; + Buffer _texCoordPointer = null; + Buffer _vertexPointer = null; + + public GL11Impl() { + } + + diff --git a/opengl/tools/glgen/stubs/GLCHeader.cpp b/opengl/tools/glgen/stubs/GLCHeader.cpp new file mode 100644 index 000000000..6495686b2 --- /dev/null +++ b/opengl/tools/glgen/stubs/GLCHeader.cpp @@ -0,0 +1,129 @@ +** +** Copyright 2006, 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. +*/ + +// This source file is automatically generated + +#include +#include + +#include +#include + +#include + +#define _NUM_COMPRESSED_TEXTURE_FORMATS \ + (::android::OGLES_NUM_COMPRESSED_TEXTURE_FORMATS) + +static int initialized = 0; + +static jclass nioAccessClass; +static jclass bufferClass; +static jclass OOMEClass; +static jclass UOEClass; +static jclass IAEClass; +static jclass AIOOBEClass; +static jmethodID getBasePointerID; +static jmethodID getBaseArrayID; +static jmethodID getBaseArrayOffsetID; +static jfieldID positionID; +static jfieldID limitID; +static jfieldID elementSizeShiftID; + +/* Cache method IDs each time the class is loaded. */ + +void +nativeClassInitBuffer(JNIEnv *_env) +{ + jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess"); + nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal); + + jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); + bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); + + getBasePointerID = _env->GetStaticMethodID(nioAccessClass, + "getBasePointer", "(Ljava/nio/Buffer;)J"); + getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, + "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); + getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, + "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); + + positionID = _env->GetFieldID(bufferClass, "position", "I"); + limitID = _env->GetFieldID(bufferClass, "limit", "I"); + elementSizeShiftID = + _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); +} + + +static void +nativeClassInit(JNIEnv *_env, jclass glImplClass) +{ + nativeClassInitBuffer(_env); + + jclass IAEClassLocal = + _env->FindClass("java/lang/IllegalArgumentException"); + jclass OOMEClassLocal = + _env->FindClass("java/lang/OutOfMemoryError"); + jclass UOEClassLocal = + _env->FindClass("java/lang/UnsupportedOperationException"); + jclass AIOOBEClassLocal = + _env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); + + IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal); + OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal); + UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal); + AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal); +} + +static void * +getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining) +{ + jint position; + jint limit; + jint elementSizeShift; + jlong pointer; + jint offset; + void *data; + + position = _env->GetIntField(buffer, positionID); + limit = _env->GetIntField(buffer, limitID); + elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + *remaining = (limit - position) << elementSizeShift; + pointer = _env->CallStaticLongMethod(nioAccessClass, + getBasePointerID, buffer); + if (pointer != 0L) { + *array = NULL; + return (void *) (jint) pointer; + } + + *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, + getBaseArrayID, buffer); + offset = _env->CallStaticIntMethod(nioAccessClass, + getBaseArrayOffsetID, buffer); + data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0); + + return (void *) ((char *) data + offset); +} + + +static void +releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) +{ + _env->ReleasePrimitiveArrayCritical(array, data, + commit ? 0 : JNI_ABORT); +} + +// -------------------------------------------------------------------------- + diff --git a/opengl/tools/glgen/stubs/GLHeader.java-if b/opengl/tools/glgen/stubs/GLHeader.java-if new file mode 100644 index 000000000..3b78f3d2c --- /dev/null +++ b/opengl/tools/glgen/stubs/GLHeader.java-if @@ -0,0 +1,22 @@ +/* //device/java/android/javax/microedition/khronos/opengles/GL.java +** +** Copyright 2006, 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 javax.microedition.khronos.opengles; + +public interface GL { +} + diff --git a/opengl/tools/glgen/stubs/GLImplHeader.java-impl b/opengl/tools/glgen/stubs/GLImplHeader.java-impl new file mode 100644 index 000000000..db3a41c01 --- /dev/null +++ b/opengl/tools/glgen/stubs/GLImplHeader.java-impl @@ -0,0 +1,48 @@ +** +** Copyright 2006, 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. +*/ + +// This source file is automatically generated + +package com.google.android.gles_jni; + +import java.nio.Buffer; +import javax.microedition.khronos.opengles.GL10; +import javax.microedition.khronos.opengles.GL10Ext; +import javax.microedition.khronos.opengles.GL11; +import javax.microedition.khronos.opengles.GL11Ext; +import javax.microedition.khronos.opengles.GL11ExtensionPack; + +public class GLImpl implements GL10, GL10Ext, GL11, GL11Ext, GL11ExtensionPack { + + // Private accessors for native code + + native private static void _nativeClassInit(); + static { + _nativeClassInit(); + } + + Buffer _colorPointer = null; + Buffer _normalPointer = null; + Buffer _texCoordPointer = null; + Buffer _vertexPointer = null; + + public GLImpl() { + } + + public void glGetPointerv(int pname, java.nio.Buffer[] params) { + throw new UnsupportedOperationException("glGetPointerv"); + } + diff --git a/opengl/tools/glgen/stubs/glGetString.cpp b/opengl/tools/glgen/stubs/glGetString.cpp new file mode 100644 index 000000000..22e129717 --- /dev/null +++ b/opengl/tools/glgen/stubs/glGetString.cpp @@ -0,0 +1,10 @@ +#include + +/* const GLubyte * glGetString ( GLenum name ) */ +jstring +android_glGetString + (JNIEnv *_env, jobject _this, jint name) { + const char * chars = (const char *)glGetString((GLenum)name); + jstring output = _env->NewStringUTF(chars); + return output; +} diff --git a/opengl/tools/glgen/stubs/glGetString.java-10-if b/opengl/tools/glgen/stubs/glGetString.java-10-if new file mode 100644 index 000000000..898fabcf2 --- /dev/null +++ b/opengl/tools/glgen/stubs/glGetString.java-10-if @@ -0,0 +1,4 @@ + public String glGetString( + int name + ); + diff --git a/opengl/tools/glgen/stubs/glGetString.java-if b/opengl/tools/glgen/stubs/glGetString.java-if new file mode 100644 index 000000000..898fabcf2 --- /dev/null +++ b/opengl/tools/glgen/stubs/glGetString.java-if @@ -0,0 +1,4 @@ + public String glGetString( + int name + ); + diff --git a/opengl/tools/glgen/stubs/glGetString.java-impl b/opengl/tools/glgen/stubs/glGetString.java-impl new file mode 100644 index 000000000..8c7881cfd --- /dev/null +++ b/opengl/tools/glgen/stubs/glGetString.java-impl @@ -0,0 +1,16 @@ + // C function const GLubyte * glGetString ( GLenum name ) + + public native String _glGetString( + int name + ); + + public String glGetString( + int name + ) { + String returnValue; + returnValue = _glGetString( + name + ); + return returnValue; + } + diff --git a/opengl/tools/glgen/stubs/glGetString.nativeReg b/opengl/tools/glgen/stubs/glGetString.nativeReg new file mode 100644 index 000000000..e64187c8a --- /dev/null +++ b/opengl/tools/glgen/stubs/glGetString.nativeReg @@ -0,0 +1 @@ +{"_glGetString", "(I)Ljava/lang/String;", (void *) android_glGetString }, diff --git a/services/Android.mk b/services/Android.mk new file mode 100644 index 000000000..5e912d6f3 --- /dev/null +++ b/services/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) + +# the library +# ============================================================ +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(call all-subdir-java-files) + +LOCAL_MODULE:= services + +LOCAL_JAVA_LIBRARIES := android.policy + +include $(BUILD_JAVA_LIBRARY) + +include $(BUILD_DROIDDOC) +