How to prevent image from rotating 90CW?

Just starting out? Need help? Post your questions and find answers here.
akee
Enthusiast
Enthusiast
Posts: 475
Joined: Wed Aug 18, 2004 9:52 am
Location: Penang, Malaysia

How to prevent image from rotating 90CW?

Post by akee »

How do you prevent a portrait image from rotating 90CW? I need to preserve the orientation.

Code: Select all

UseJPEGImageDecoder()

; use a portrait oriented image.
filename$ = "C:\Users\akee\Desktop\IMG_0703.JPG"

; image is rotated 90 degrees CW.
image = LoadImage(#PB_Any, filename$)

; the height is now the width.
Debug ImageWidth(image)

; the width is now the height.
Debug ImageHeight(image)
Mesa
Enthusiast
Enthusiast
Posts: 345
Joined: Fri Feb 24, 2012 10:19 am

Re: How to prevent image from rotating 90CW?

Post by Mesa »

Windows 10 straightens images without the user's knowledge. Your image is already rotated 90 degrees but Windows displays it straight.

M.
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: How to prevent image from rotating 90CW?

Post by ChrisR »

You can possibly change it in the registry

Code: Select all

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AutoRotation]
"Enable"=dword:00000000
Restart to apply the settings

; Enable 0=Off, 1=On (Default 1)
; LastOrientation: 0=0°, 1=90°, 2=180°, 3=270° (Default 0)
; Slate Enable 0=Off, 1=On (Default 1)
User avatar
Caronte3D
Addict
Addict
Posts: 1027
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: How to prevent image from rotating 90CW?

Post by Caronte3D »

Take a look at image info in the Exif, possibly the rotation should be indicated somewhere, so you can read it an rotate the image with your code.
User avatar
Dreamland Fantasy
Enthusiast
Enthusiast
Posts: 334
Joined: Fri Jun 11, 2004 9:35 pm
Location: Glasgow, UK
Contact:

Re: How to prevent image from rotating 90CW?

Post by Dreamland Fantasy »

A few years ago I wrote some code to deal with EXIF information in images, but never finished it. I've just added some code to include the orientation data.

Below is an example of getting the orientation data of an image with EXIF information:

Code: Select all

; Exif library for PureBasic
; Programming by Francis G. Loch
;
; Based on information from http://www.media.mit.edu/pia/Research/deepview/exif.html
; and https://sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html

#Exif_Header                      = $45786966
#Tiff_MagicWord                   = $002A
#Marker_StartOfImage              = $FFD8
#Marker_EndOfImage                = $FFD9
#Marker_StartOfStream             = $FFDA
#Marker_Application_APP0          = $FFE0
#Marker_Application_APP1          = $FFE1
#ByteAlign_Intel                  = $4949
#ByteAlign_Motorola               = $4D4D
#ExifIFD_BodySerialNumber         = $A431
#IFD0_ImageDescription            = $010E
#IFD0_Make                        = $010F
#IFD0_Model                       = $0110
#IFD0_Orientation                 = $0112
#IFD0_XResolution                 = $011A
#IFD0_YResolution                 = $011B
#IFD0_ResolutionUnit              = $0128
#IFD0_Software                    = $0131
#IFD0_DateTime                    = $0132
#IFD0_WhitePoint                  = $013E
#IFD0_PrimaryChromaticities       = $013F
#IFD0_YCbCrCoefficients           = $0211
#IFD0_YCbCrPositioning            = $0213
#IFD0_ReferenceBlackWhite         = $0214
#IFD0_Copyright                   = $8298
#IFD0_ExifOffset                  = $8769
#IFD0_CameraSerialNumber          = $C62F
#SubIFD_DateTimeOriginal          = $9003
#SubIFD_SerialNumber              = $FDE9

Enumeration 1
  #Exif_DataFormat_UnsignedByte
  #Exif_DataFormat_AsciiStrings
  #Exif_DataFormat_UnsignedShort
  #Exif_DataFormat_UnsignedLong
  #Exif_DataFormat_UnsignedRational
  #Exif_DataFormat_SignedByte
  #Exif_DataFormat_Undefined
  #Exif_DataFormat_SignedShort
  #Exif_DataFormat_SignedLong
  #Exif_DataFormat_SignedRational
  #Exif_DataFormat_SingleFloat
  #Exif_DataFormat_DoubleFloat
EndEnumeration

Structure IFD0
  Make.s
  Model.s
  Orientation.l
  ExifOffset.q
  CameraSerialNumber.s
EndStructure

Structure SubIFD
  DateTimeOriginal.s
  SerialNumber.s
EndStructure

Structure ExifTags
  Offset.l
  BodySerialNumber.s
  IFD0.IFD0
  SubIFD.SubIFD
EndStructure

Macro ConvertEndianWord(n)
  (((n & $FF) << 8) | ((n >> 8) & $FF))
EndMacro
 
Macro ConvertEndianLong(n)
  (((n & $FF) << 24) | ((n & $FF00) << 8) | ((n >> 8) & $FF00) | ((n >> 24) & $FF))
EndMacro

Procedure.w FixByteOrderWord(Value.w, ByteOrder)
   Select ByteOrder
    Case #ByteAlign_Intel
      ProcedureReturn Value
    Case #ByteAlign_Motorola
      ProcedureReturn ConvertEndianWord(Value)
  EndSelect
EndProcedure

Procedure.l FixByteOrderLong(Value.l, ByteOrder)
  Select ByteOrder
    Case #ByteAlign_Intel
      ProcedureReturn Value
    Case #ByteAlign_Motorola
      ProcedureReturn ConvertEndianLong(Value)
  EndSelect
EndProcedure

Procedure GetExifData(hndFile, *ExifTags.ExifTags, Offset = 0)
  
  Protected i, TagID, TagCount, DataFormat, DataLength, DataOffset, DataValue.q, DataString$, ByteOrder, MagicWord, CurrentOffset, FileOffset
  
  If IsFile(hndFile) = 0
    ProcedureReturn 0
  EndIf
  
  FileSeek(hndFile, *ExifTags\Offset)
  
  ByteOrder = ReadWord(hndFile)
  If ByteOrder <> #ByteAlign_Intel And ByteOrder <> #ByteAlign_Motorola
    ProcedureReturn 0
  EndIf
  
  MagicWord = ReadWord(hndFile)
  If MagicWord <> #Tiff_MagicWord And MagicWord <> ConvertEndianWord(#Tiff_MagicWord)
    ProcedureReturn 0
  EndIf
  
  If Offset
    FileSeek(hndFile, *ExifTags\Offset + Offset)
  Else
    FileSeek(hndFile, *ExifTags\Offset + FixByteOrderLong(ReadLong(hndFile), ByteOrder))
  EndIf

  TagCount = FixByteOrderWord(ReadWord(hndFile), ByteOrder)
  
  FileOffset = Loc(hndFile)
  For i = 1 To TagCount
    FileSeek(hndFile, FileOffset)
    TagID       = FixByteOrderWord(ReadWord(hndFile), ByteOrder) & $FFFF
    DataFormat  = FixByteOrderWord(ReadWord(hndFile), ByteOrder) & $FFFF
    DataLength  = FixByteOrderLong(ReadLong(hndFile), ByteOrder) & $FFFFFFFF
    Select DataFormat
      Case #Exif_DataFormat_AsciiStrings
        If DataLength > 4
          DataOffset = FixByteOrderLong(ReadLong(hndFile), ByteOrder)
          FileSeek(hndFile, *ExifTags\Offset + DataOffset)
          DataString$ = ReadString(hndFile, #PB_Ascii, DataLength)
          FileSeek(hndFile, CurrentOffset)
        Else
          DataString$ = ReadString(hndFile, #PB_Ascii, DataLength)
        EndIf
        Select TagID
          Case #IFD0_Make
            *ExifTags\IFD0\Make = DataString$
          Case #IFD0_Model
            *ExifTags\IFD0\Model = DataString$
          Case #ExifIFD_BodySerialNumber
            *ExifTags\BodySerialNumber = DataString$
            ; Debug "ExifIFD Body Serial Number = " + DataString$ + " (Make: " + *ExifTags\IFD0\Make + ", Model: " + *ExifTags\IFD0\Model + ")"
          Case #IFD0_CameraSerialNumber
            *ExifTags\IFD0\CameraSerialNumber = DataString$
            ; Debug "IFD0 Camera Serial Number = " + DataString$ + " (Make: " + *ExifTags\IFD0\Make + ", Model: " + *ExifTags\IFD0\Model + ")"
          Case #SubIFD_SerialNumber
            *ExifTags\SubIFD\SerialNumber = DataString$
            ; Debug "SubIFD Serial Number = " + DataString$ + " (Make: " + *ExifTags\IFD0\Make + ", Model: " + *ExifTags\IFD0\Model + ")"
          Case #SubIFD_DateTimeOriginal
            *ExifTags\SubIFD\DateTimeOriginal = DataString$
        EndSelect
      Case #Exif_DataFormat_UnsignedShort
        DataValue = FixByteOrderWord(ReadLong(hndFile), ByteOrder) & $FFFFFFFF
        Select TagID
          Case #IFD0_Orientation
            *ExifTags\IFD0\Orientation = DataValue
        EndSelect
      Case #Exif_DataFormat_UnsignedLong
        DataValue = FixByteOrderLong(ReadLong(hndFile), ByteOrder) & $FFFFFFFF
        Select TagID
          Case #IFD0_ExifOffset
            *ExifTags\IFD0\ExifOffset = DataValue
        EndSelect
    EndSelect
    FileOffset + 12
  Next
  
EndProcedure

Procedure ReadExifFile(*ExifTags.ExifTags, FileName$)
  
  Protected hndFile, AppMarker, Value, Offset, *mem, i
  
  hndFile = ReadFile(#PB_Any, FileName$, #PB_File_SharedRead)
  If hndFile = 0
    Debug "Unable to open file! (" + FileName$ + ")"
    ProcedureReturn 0
  EndIf
  
  Value = ReadWord(hndFile)
  
  Select Value
    Case ConvertEndianWord(#Marker_StartOfImage)
      AppMarker = ConvertEndianWord(ReadWord(hndFile)) & $FFFF
  
      If AppMarker = #Marker_Application_APP0
        FileSeek(hndFile, ConvertEndianWord(ReadWord(hndFile)) & $FFFF + 4) ; Jump to end of APP0 section
        AppMarker = ConvertEndianWord(ReadWord(hndFile)) & $FFFF
      EndIf
      
      If AppMarker = #Marker_Application_APP1
        FileSeek(hndFile, 2, #PB_Relative)
        If ReadLong(hndFile) = ConvertEndianLong(#Exif_Header) And ReadWord(hndFile) = 0
          *ExifTags\Offset = Loc(hndFile)
          GetExifData(hndFile, *ExifTags)
          If *ExifTags\IFD0\ExifOffset
            GetExifData(hndFile, *ExifTags, *ExifTags\IFD0\ExifOffset)
          EndIf
        EndIf
      EndIf
        
    Case #ByteAlign_Intel, #ByteAlign_Motorola
      If FixByteOrderWord(ReadWord(hndFile), Value) = #Tiff_MagicWord
        *ExifTags\Offset = 0
        GetExifData(hndFile, *ExifTags)
        If *ExifTags\IFD0\ExifOffset
          GetExifData(hndFile, *ExifTags, *ExifTags\IFD0\ExifOffset)
        EndIf
      EndIf
      
    Default
      
      ; Brute force method of finding the data. Not ideal!
      
      If FileSize(FileName$) > 4096
        FileSeek(hndFile, 0)
        *mem = AllocateMemory(4096)
        ReadData(hndFile, *mem, MemorySize(*mem))
        For i = 0 To MemorySize(*mem) - 1 - 128 ; Reduced by 128 so we don't go outside the allocated memory
          Value = PeekW(*mem + i)
          If Value = #ByteAlign_Intel Or Value = #ByteAlign_Motorola
            If FixByteOrderWord(PeekW(*mem + i + 2), Value) = #Tiff_MagicWord
              *ExifTags\Offset = i
              GetExifData(hndFile, *ExifTags, 0)
              If *ExifTags\IFD0\ExifOffset
                GetExifData(hndFile, *ExifTags, *ExifTags\IFD0\ExifOffset)
              EndIf
              Break
            EndIf
          EndIf
        Next
        FreeMemory(*mem)
      EndIf
      
  EndSelect
  
  CloseFile(hndFile)
  
EndProcedure

;- Sample code

Define ExifTags.ExifTags

FileName$ = OpenFileRequester("Open Image", "", "", 0)

ReadExifFile(@ExifTags, FileName$)
GetExifData(0, @ExifTags)

Debug "Camera: " + ExifTags\IFD0\Make + " " + ExifTags\IFD0\Model

Select ExifTags\IFD0\Orientation
  Case 1
    Debug "Orientation: Do nothing"
  Case 2
    Debug "Orientation: Flip horizontally"
  Case 3
    Debug "Orientation: Rotate 180 degrees"
  Case 4
    Debug "Orientation: Flip vertically"
  Case 5
    Debug "Orientation: Rotate 90 degrees clockwise and flip horizontally"
  Case 6
    Debug "Orientation: Rotate 90 degrees clockwise"
  Case 7
    Debug "Orientation: Rotate 90 degrees clockwise and flip vertically"
  Case 8
    Debug "Orientation: Rotate 270 degrees clockwise"
  Default
    Debug "No orientation data"
EndSelect
It's not pretty, but hopefully it will be of use.

Kind regards,

Francis
akee
Enthusiast
Enthusiast
Posts: 475
Joined: Wed Aug 18, 2004 9:52 am
Location: Penang, Malaysia

Re: How to prevent image from rotating 90CW?

Post by akee »

Thanks guys. Especially Dreamland. I'll take a look at the code.
Post Reply